通過Jenkins構(gòu)建CI/CD實(shí)現(xiàn)微服務(wù)全鏈路灰度
使用Jenkins構(gòu)建流水線,可以實(shí)現(xiàn)全鏈路灰度功能。通過Pipeline腳本,將構(gòu)建、部署和測(cè)試等環(huán)節(jié)串聯(lián)起來,根據(jù)灰度驗(yàn)證的結(jié)果,決策后續(xù)步驟。如果新版本穩(wěn)定,則逐步調(diào)整路由規(guī)則,增大灰度流量,直至全量上線。如果發(fā)現(xiàn)問題,則立即回滾流量至舊版本,并進(jìn)行問題排查。整個(gè)過程實(shí)現(xiàn)了從構(gòu)建到灰度發(fā)布的全鏈路自動(dòng)化管理,確保了服務(wù)更新的安全性和穩(wěn)定性。本文介紹如何通過Jenkins構(gòu)建流水線的方式實(shí)現(xiàn)全鏈路灰度功能。
整體架構(gòu)
灰度驗(yàn)證通常采用以下策略:
直接使用線上小部分流量來測(cè)試(按照百分比放量)。
從線上按照特定規(guī)則選擇流量(比如特定的Header、特定的Cookie等)。
在客戶端或?yàn)g覽器上標(biāo)識(shí)出流量是否灰度(比如通過Header傳遞)。
以如下Demo為例:
準(zhǔn)備工作
將ACK微服務(wù)應(yīng)用接入MSE治理中心
將ACK微服務(wù)應(yīng)用接入MSE治理中心。具體操作,請(qǐng)參見ACK微服務(wù)應(yīng)用接入MSE治理中心。
部署Demo應(yīng)用程序
登錄容器服務(wù)管理控制臺(tái),在左側(cè)導(dǎo)航欄選擇集群。
在集群列表頁面,單擊目標(biāo)集群名稱或者目標(biāo)集群右側(cè)操作列下的詳情。
在集群管理頁左側(cè)導(dǎo)航欄,選擇 。
在無狀態(tài)頁面單擊使用YAML創(chuàng)建資源。
對(duì)模板進(jìn)行相關(guān)配置,完成配置后單擊創(chuàng)建。
本文示例部署A、B、C三個(gè)應(yīng)用,以及注冊(cè)中心Nacos Server,流量入口應(yīng)用Spring-Cloud-Zuul,流量調(diào)用鏈路為:Spring-Cloud-Zuul->A->B->C。
Spring-Cloud-Zuul 應(yīng)用默認(rèn)有100 QPS正常流量,而另外有10 QPS帶有
x-mse-tag: gray
特殊Header,這個(gè)Header是MSE內(nèi)置的灰度Header,只要帶上x-mse-tag: gray
這個(gè)Header,在開啟MSE微服務(wù)治理并且安裝探針之后會(huì)自動(dòng)路由到下游帶有g(shù)ray標(biāo)簽的節(jié)點(diǎn)上。根據(jù)需要,這個(gè)gray的值也可以替換成其他節(jié)點(diǎn)上打的標(biāo)簽。入口Spring-Cloud-Zuul 應(yīng)用YAML。
apiVersion: apps/v1 kind: Deployment metadata: name: spring-cloud-zuul spec: replicas: 1 selector: matchLabels: app: spring-cloud-zuul template: metadata: labels: app: spring-cloud-zuul msePilotCreateAppName: spring-cloud-zuul spec: containers: - name: spring-cloud-zuul image: registry.cn-hangzhou.aliyuncs.com/mse-governance-demo/spring-cloud-zuul:3.0.1 imagePullPolicy: Always ports: - containerPort: 20000
A應(yīng)用基線(base)版本YAML。
apiVersion: apps/v1 kind: Deployment metadata: name: spring-cloud-a spec: replicas: 2 selector: matchLabels: app: spring-cloud-a template: metadata: labels: app: spring-cloud-a msePilotCreateAppName: spring-cloud-a spec: containers: - name: spring-cloud-a image: registry.cn-hangzhou.aliyuncs.com/mse-governance-demo/spring-cloud-a:3.0.1 imagePullPolicy: Always ports: - containerPort: 20001 livenessProbe: tcpSocket: port: 20001 initialDelaySeconds: 10 periodSeconds: 30
B應(yīng)用基線(base)版本YAML:
apiVersion: apps/v1 kind: Deployment metadata: name: spring-cloud-b spec: replicas: 2 selector: matchLabels: app: spring-cloud-b strategy: template: metadata: labels: app: spring-cloud-b msePilotCreateAppName: spring-cloud-b spec: containers: - name: spring-cloud-b image: registry.cn-hangzhou.aliyuncs.com/mse-governance-demo/spring-cloud-b:3.0.1 imagePullPolicy: Always ports: - containerPort: 8080 livenessProbe: tcpSocket: port: 20002 initialDelaySeconds: 10 periodSeconds: 30
C應(yīng)用基線(base)版本YAML。
apiVersion: apps/v1 kind: Deployment metadata: name: spring-cloud-c spec: replicas: 1 selector: matchLabels: app: spring-cloud-c template: metadata: labels: app: spring-cloud-c msePilotCreateAppName: spring-cloud-c spec: containers: - name: spring-cloud-c image: registry.cn-hangzhou.aliyuncs.com/mse-governance-demo/spring-cloud-c:3.0.1 imagePullPolicy: Always ports: - containerPort: 20003 livenessProbe: tcpSocket: port: 20003 initialDelaySeconds: 10 periodSeconds: 30
Nacos Server應(yīng)用YAML。
apiVersion: apps/v1 kind: Deployment metadata: name: nacos-server spec: replicas: 1 selector: matchLabels: app: nacos-server template: metadata: labels: app: nacos-server spec: containers: - env: - name: MODE value: standalone image: registry.cn-hangzhou.aliyuncs.com/mse-governance-demo/nacos-server:v2.1.2 imagePullPolicy: Always name: nacos-server dnsPolicy: ClusterFirst restartPolicy: Always # zuul網(wǎng)關(guān)開啟SLB暴露展示頁面 --- apiVersion: v1 kind: Service metadata: annotations: service.beta.kubernetes.io/alibaba-cloud-loadbalancer-spec: slb.s1.small name: zuul-slb spec: ports: - port: 80 protocol: TCP targetPort: 20000 selector: app: spring-cloud-zuul type: LoadBalancer
應(yīng)用部署成功后,在MSE治理中心控制臺(tái)觀察A應(yīng)用的流量,確認(rèn)流量都打到未打標(biāo)的節(jié)點(diǎn),并沒有灰度節(jié)點(diǎn)的流量。
配置鏡像倉庫的推送權(quán)限
本文實(shí)踐需要將源碼打包后執(zhí)行鏡像推送,請(qǐng)確保Jenkins有權(quán)限推送到鏡像倉庫。具體操作,請(qǐng)參見在ACK集群中部署Jenkins并完成應(yīng)用構(gòu)建和部署。
創(chuàng)建泳道
登錄MSE治理中心控制臺(tái),并在頂部菜單欄選擇地域。
在左側(cè)導(dǎo)航欄,選擇治理中心 > 全鏈路灰度。
在全鏈路灰度頁面,單擊創(chuàng)建泳道組及泳道。如果您選擇的微服務(wù)空間內(nèi)已經(jīng)創(chuàng)建過泳道組,則單擊+創(chuàng)建泳道組。
在創(chuàng)建泳道組面板,選擇入口應(yīng)用以及整個(gè)鏈路涉及到的應(yīng)用,然后單擊確定。
配置項(xiàng)
描述
泳道組名稱
自定義設(shè)置泳道組的名稱。
入口類型
選擇Java服務(wù)網(wǎng)關(guān)。
入口應(yīng)用
根據(jù)實(shí)際情況選擇。
泳道組涉及應(yīng)用
選擇您的入口應(yīng)用或入口網(wǎng)關(guān)所涉及的所有相關(guān)服務(wù)。
泳道組創(chuàng)建完成后,在全鏈路灰度頁面的泳道組涉及應(yīng)用區(qū)域出現(xiàn)您所創(chuàng)建的泳道組。請(qǐng)檢查入口應(yīng)用和所涉及的應(yīng)用是否正確,如需變更泳道組信息,請(qǐng)單擊右側(cè)的圖標(biāo)并修改相關(guān)信息。
在全鏈路灰度頁面上方選擇創(chuàng)建和泳道組時(shí)相同的微服務(wù)空間,然后底部單擊點(diǎn)擊創(chuàng)建第一個(gè)分流泳道。
如果您選擇的微服務(wù)空間內(nèi)已經(jīng)創(chuàng)建過泳道,則單擊創(chuàng)建泳道。
重要加入全鏈路流量控制的應(yīng)用,將不再支持金絲雀發(fā)布、標(biāo)簽路由等功能。
在創(chuàng)建泳道面板設(shè)置流控泳道相關(guān)參數(shù),將符合規(guī)則的應(yīng)用劃入gray泳道。然后單擊確定。
配置項(xiàng)
描述
配置節(jié)點(diǎn)標(biāo)簽
需要您手工給您的灰度應(yīng)用節(jié)點(diǎn)打上標(biāo)簽,用來和正常節(jié)點(diǎn)做區(qū)分。
填寫泳道信息
泳道標(biāo)簽:該泳道內(nèi)的匹配的流量去往的目標(biāo)標(biāo)簽。
確認(rèn)匹配關(guān)系:檢查您配置了該標(biāo)簽的應(yīng)用節(jié)點(diǎn)數(shù)是否符合預(yù)期。
路由和灰度規(guī)則
Path:要匹配的路徑,可以多選。如果不填寫,將匹配任意路徑。
條件模式:路由條件之間的關(guān)系。
條件列表:
參數(shù)類型:表示參數(shù)的來源,可選值包括:Parameter(請(qǐng)求參數(shù))、Header(請(qǐng)求頭部)、Cookie、Body Content(JSON格式的請(qǐng)求body)。
參數(shù):參數(shù)名稱。
條件:匹配規(guī)則。
值:表示要匹配的參數(shù)值。
沒有匹配的流量會(huì)分配到基線環(huán)境,也就是沒有打標(biāo)的應(yīng)用節(jié)點(diǎn)上。
配置完成后,訪問網(wǎng)關(guān)。
如果不符合灰度規(guī)則,走基線環(huán)境。
如果符合灰度規(guī)則,走灰度環(huán)境。
配置Jenkins流水線
本文實(shí)踐需要將源碼打包后執(zhí)行鏡像推送,請(qǐng)確保Jenkins有權(quán)限推送到鏡像倉庫。具體操作,請(qǐng)參見在ACK集群中部署Jenkins并完成應(yīng)用構(gòu)建和部署。
在Jenkins命名空間使用生成的config.json
文件創(chuàng)建名為jenkins-docker-cfg的Secret。
kubectl create secret generic jenkins-docker-cfg -n jenkins --from-file=/root/.docker/config.json
在Jenkins中創(chuàng)建全鏈路灰度發(fā)布流水線
基于Jenkins實(shí)現(xiàn)自動(dòng)化發(fā)布的流水線,通過該流水線可以使應(yīng)用發(fā)布具備可灰度、可觀測(cè)、可回滾的安全生產(chǎn)三種能力。
在Jenkins控制臺(tái)左側(cè)導(dǎo)航欄,單擊新建任務(wù)。
輸入任務(wù)名稱,選擇流水線,然后單擊確定。
在頂部菜單欄,單擊流水線頁簽,在流水線區(qū)域配置相關(guān)參數(shù),輸入腳本路徑,然后單擊保存。
定義:Pipeline script from SCM。
SCM:Git。
Repository URL:輸入Git倉庫的URL。本文示例代碼倉庫的地址為https://gitee.com/ralf0131/mse-demo。
說明阿里云機(jī)器無法拉取Github的源碼,暫時(shí)使用Gitee替代。
腳本路徑:輸入Jenkinsfile。
您可以參考以下文件填寫指定參數(shù),也可以根據(jù)需求編寫Jenkinsfile,并上傳至Git的指定路徑(流水線中指定的腳本路徑)。
#!groovy pipeline { // 定義本次構(gòu)建使用哪個(gè)標(biāo)簽的構(gòu)建環(huán)境,本示例為 “slave-pipeline” agent{ node{ label 'slave-pipeline' } } //常量參數(shù),初始確定后一般不需更改 environment{ IMAGE = sh(returnStdout: true,script: 'echo registry.$image_region.aliyuncs.com/$image_namespace/$image_reponame:$image_tag').trim() BRANCH = sh(returnStdout: true,script: 'echo $branch').trim() } options { //保持構(gòu)建的最大個(gè)數(shù) buildDiscarder(logRotator(numToKeepStr: '10')) } parameters { string(name: 'image_region', defaultValue: 'cn-shanghai') string(name: 'image_namespace', defaultValue: 'yizhan') string(name: 'image_reponame', defaultValue: 'spring-cloud-a') string(name: 'image_tag', defaultValue: 'gray') string(name: 'branch', defaultValue: 'master') string(name: 'number_of_pods', defaultValue: '2') } //pipeline的各個(gè)階段場(chǎng)景 stages { stage('代碼打包') { steps{ container("maven") { echo "鏡像構(gòu)建......" sh "cd A && mvn clean package" } } } stage('鏡像構(gòu)建及發(fā)布'){ steps{ container("kaniko") { sh "kaniko -f `pwd`/A/Dockerfile -c `pwd`/A --destination=${IMAGE} --skip-tls-verify" } } } stage('灰度部署') { steps{ container('kubectl') { echo "灰度部署......" sh "cd A && sed -i -E \"s/${env.image_reponame}:.+/${env.image_reponame}:${env.image_tag}/\" A-gray-deployment.yaml" sh "cd A && sed -i -E \"s/replicas:.+/replicas: ${env.number_of_pods}/\" A-gray-deployment.yaml" sh "kubectl apply -f A/A-gray-deployment.yaml -n default" } } } stage('結(jié)束灰度') { input { message "請(qǐng)確認(rèn)是否全量發(fā)布" ok "確認(rèn)" parameters { string(name: 'continue', defaultValue: 'true', description: 'true為全量發(fā)布,其他為回滾') } } steps{ script { env.continue = sh (script: 'echo ${continue}', returnStdout: true).trim() if (env.continue.equals('true')) { container('kubectl') { echo "全量發(fā)布......" sh "cd A && sed -i -E \"s/${env.image_reponame}:.+/${env.image_reponame}:${env.image_tag}/\" A-deployment.yaml" sh "cd A && sed -i -E \"s/replicas:.+/replicas: ${env.number_of_pods}/\" A-deployment.yaml" sh "kubectl apply -f A/A-deployment.yaml -n default" } } else { echo '回滾' } container('kubectl') { sh "kubectl delete -f A/A-gray-deployment.yaml -n default" } } } } } }
構(gòu)建Jenkins流水線
在Jenkins控制臺(tái),單擊流水線右側(cè)的圖標(biāo)。
單擊流水線的開始構(gòu)建。
說明第一次構(gòu)建時(shí),需要從Git倉庫拉取配置并初始化流水線,所以可能會(huì)報(bào)錯(cuò)。您可以再次執(zhí)行Build with Parameters,生成相關(guān)的參數(shù),填寫相關(guān)的參數(shù),再次執(zhí)行構(gòu)建。
查看部署狀態(tài),代碼打包,鏡像構(gòu)建及發(fā)布,灰度部署階段都已經(jīng)完成,結(jié)束灰度階段等待確認(rèn)。
如果驗(yàn)證結(jié)果符合預(yù)期,執(zhí)行全量發(fā)布。更多信息,請(qǐng)參見全量發(fā)布應(yīng)用。
如果驗(yàn)證結(jié)果不符合預(yù)期,執(zhí)行回滾。更多信息,請(qǐng)參見回滾應(yīng)用。
結(jié)果驗(yàn)證
登錄容器服務(wù)管理控制臺(tái),在左側(cè)導(dǎo)航欄選擇集群。
在集群列表頁面,單擊目標(biāo)集群名稱,然后在左側(cè)導(dǎo)航欄,選擇 。
在無狀態(tài)應(yīng)用列表頁面,spring-cloud-a-gray應(yīng)用已經(jīng)自動(dòng)創(chuàng)建,并且它的鏡像已經(jīng)替換為
spring-cloud-a:gray
版本。在集群管理頁面左側(cè)導(dǎo)航欄選擇
,選擇設(shè)置的命名空間,單擊zuul-slb服務(wù)的外部端點(diǎn),查看真實(shí)的調(diào)用情況。不帶灰度Header進(jìn)行調(diào)用,發(fā)現(xiàn)路由到A的正常節(jié)點(diǎn)。
curl命令:
curl http://182.92.XX.XX/A/a
執(zhí)行結(jié)果:
A[10.4.XX.XX] -> B[10.4.XX.XX] -> C[10.4.XX.XX]%
帶上符合條件的參數(shù)進(jìn)行訪問,路由到A的灰度節(jié)點(diǎn)中。
curl命令:
curl http://182.92.XX.XX/A/a?name=xiaoming
執(zhí)行結(jié)果:
Agray[10.4.XX.XX] -> B[10.4.XX.XX] -> C[10.4.XX.XX]%
登錄MSE治理中心控制臺(tái),在應(yīng)用詳情頁面,可以看到灰度流量已經(jīng)進(jìn)入到灰度的節(jié)點(diǎn)。
全量發(fā)布應(yīng)用
結(jié)果驗(yàn)證通過之后,確認(rèn)全量發(fā)布。
在Jenkins控制臺(tái),單擊目標(biāo)流水線名稱。
單擊需要全量發(fā)布的階段,在請(qǐng)確認(rèn)是否全量發(fā)布對(duì)話框輸入true,然后單擊確認(rèn)。
在容器服務(wù)控制臺(tái),發(fā)現(xiàn)spring-cloud-a-gray應(yīng)用已經(jīng)被刪除,并且spring-cloud-a應(yīng)用的鏡像已經(jīng)替換為
spring-cloud-a:gray
版本。在MSE治理中心控制臺(tái),發(fā)現(xiàn)灰度流量已經(jīng)消失。
回滾應(yīng)用
在驗(yàn)證結(jié)果不符合預(yù)期時(shí),回滾應(yīng)用。
在Jenkins控制臺(tái),單擊目標(biāo)流水線名稱。
單擊需要全量發(fā)布的階段,在請(qǐng)確認(rèn)是否全量發(fā)布對(duì)話框輸入false,然后單擊確認(rèn)。
在容器服務(wù)控制臺(tái),發(fā)現(xiàn)spring-cloud-a-gray應(yīng)用已經(jīng)被刪除,并且spring-cloud-a應(yīng)用的鏡像仍然是老版本。
在MSE治理中心控制臺(tái),發(fā)現(xiàn)灰度流量已經(jīng)消失。