結(jié)合Kruise Rollouts實(shí)現(xiàn)基于MSE的全鏈路灰度
作為Kubernetes應(yīng)用部署工具,Kruise Rollouts提供金絲雀發(fā)布、藍(lán)綠發(fā)布等多種灰度發(fā)布策略,并通過與MSE微服務(wù)治理的結(jié)合,實(shí)現(xiàn)對(duì)新版本應(yīng)用在服務(wù)調(diào)用鏈路上的平滑灰度升級(jí),確保新版本的穩(wěn)定性。
全鏈路灰度介紹
在微服務(wù)架構(gòu)場景下,傳統(tǒng)的灰度發(fā)布模式往往不能滿足交付的復(fù)雜需求,全鏈路灰度發(fā)布的場景也就應(yīng)運(yùn)而生,此時(shí)每個(gè)微服務(wù)都會(huì)有灰度環(huán)境或分組來接受灰度流量。開發(fā)者希望進(jìn)入上游灰度環(huán)境的流量也能進(jìn)入下游灰度環(huán)境中,確保一個(gè)請(qǐng)求始終在灰度環(huán)境中傳遞,從而形成流量泳道。在泳道內(nèi)的流量鏈路中,即使這個(gè)調(diào)用鏈路上有一些微服務(wù)應(yīng)用不存在灰度環(huán)境,那么這些微服務(wù)應(yīng)用在請(qǐng)求下游的時(shí)候依然能夠重新回到灰度環(huán)境中。此時(shí)可以根據(jù)服務(wù)的實(shí)際情況,控制多個(gè)服務(wù)同時(shí)進(jìn)行發(fā)布變更,從而保證整個(gè)系統(tǒng)的穩(wěn)定性,效果如下圖所示:
Kruise Rollouts介紹
Kruise Rollouts是OpenKruise社區(qū)開源的漸進(jìn)式交付框架。Kruise Rollouts支持配合流量和實(shí)例灰度的灰度發(fā)布、藍(lán)綠發(fā)布和A/B Testing發(fā)布。基于Prometheus Metrics指標(biāo),Kruise Rollouts還可以實(shí)現(xiàn)發(fā)布過程的自動(dòng)化分批與暫停,并提供旁路的無感對(duì)接,兼容已有的多種工作負(fù)載(Deployment、CloneSet、StatefulSet),更多信息,請(qǐng)參見Kruise Rollouts。
Kruise Rollouts是一種旁路式的工作機(jī)制。您只需配置一份Rollouts資源并將其下發(fā)到K8s集群中,后續(xù)的業(yè)務(wù)發(fā)布和升級(jí)均無需額外操作,并且可以與Helm和PaaS平臺(tái)低成本無縫對(duì)接。使用Kruise Rollouts實(shí)現(xiàn)灰度發(fā)布架構(gòu)圖如下:
前提條件
創(chuàng)建Kubernetes托管版集群且集群版本為1.19及以上。
步驟一:準(zhǔn)備工作
安裝Kruise Rollouts組件。
登錄容器服務(wù)管理控制臺(tái),在左側(cè)導(dǎo)航欄選擇集群。
在集群列表頁面,單擊目標(biāo)集群名稱,然后在左側(cè)導(dǎo)航欄,選擇 。
在組件管理頁面,單擊應(yīng)用管理頁簽。
在ack-kruise卡片右下方,單擊安裝。
在彈出的對(duì)話框中,單擊確定。
安裝MSE Ingress組件。創(chuàng)建MseIngressConfig和IngressClass,具體操作,請(qǐng)參見通過MSE Ingress訪問容器服務(wù)。
為應(yīng)用開啟微服務(wù)治理。具體操作,請(qǐng)參見ACK微服務(wù)應(yīng)用接入MSE治理中心。
步驟二:部署Demo應(yīng)用
部署業(yè)務(wù)應(yīng)用(Deployment、Service和Ingress)。
使用如下內(nèi)容,創(chuàng)建mse-demo.yaml文件。
# a service apiVersion: v1 kind: Service metadata: name: spring-cloud-a spec: ports: - name: http port: 20001 protocol: TCP targetPort: 20001 selector: app: spring-cloud-a # a application --- apiVersion: apps/v1 kind: Deployment metadata: name: spring-cloud-a spec: replicas: 2 selector: matchLabels: app: spring-cloud-a template: metadata: annotations: msePilotCreateAppName: spring-cloud-a labels: app: spring-cloud-a spec: containers: - env: - name: JAVA_HOME value: /usr/lib/jvm/java-1.8-openjdk/jre image: registry.cn-hangzhou.aliyuncs.com/mse-demo-hz/spring-cloud-a:mse-2.0.0 imagePullPolicy: Always name: spring-cloud-a ports: - containerPort: 20001 # b application --- apiVersion: apps/v1 kind: Deployment metadata: name: spring-cloud-b spec: replicas: 2 selector: matchLabels: app: spring-cloud-b template: metadata: annotations: msePilotCreateAppName: spring-cloud-b labels: app: spring-cloud-b spec: containers: - env: - name: JAVA_HOME value: /usr/lib/jvm/java-1.8-openjdk/jre image: registry.cn-hangzhou.aliyuncs.com/mse-demo-hz/spring-cloud-b:mse-2.0.0 imagePullPolicy: Always name: spring-cloud-b # c application --- apiVersion: apps/v1 kind: Deployment metadata: name: spring-cloud-c spec: replicas: 2 selector: matchLabels: app: spring-cloud-c template: metadata: annotations: msePilotCreateAppName: spring-cloud-c labels: app: spring-cloud-c spec: containers: - env: - name: JAVA_HOME value: /usr/lib/jvm/java-1.8-openjdk/jre image: registry.cn-hangzhou.aliyuncs.com/mse-demo-hz/spring-cloud-c:mse-2.0.0 imagePullPolicy: Always name: spring-cloud-c --- 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: nacos/nacos-server:v2.2.0 imagePullPolicy: Always name: nacos-server dnsPolicy: ClusterFirst restartPolicy: Always --- apiVersion: v1 kind: Service metadata: name: nacos-server spec: type: ClusterIP ports: - name: nacos-server-8848-8848 port: 8848 protocol: TCP targetPort: 8848 - name: nacos-server-9848-9848 port: 9848 protocol: TCP targetPort: 9848 selector: app: nacos-server --- apiVersion: v1 kind: Service metadata: labels: app: demo-mysql name: demo-mysql spec: ports: - port: 3306 targetPort: 3306 selector: app: demo-mysql --- apiVersion: apps/v1 kind: Deployment metadata: name: demo-mysql spec: selector: matchLabels: app: demo-mysql replicas: 1 strategy: type: Recreate template: metadata: labels: app: demo-mysql spec: containers: - args: - --character-set-server=utf8mb4 - --collation-server=utf8mb4_unicode_ci env: - name: MYSQL_ROOT_PASSWORD value: root image: registry.cn-hangzhou.aliyuncs.com/mse-demo-hz/demo-mysql:3.0.1 name: demo-mysql ports: - containerPort: 3306
執(zhí)行如下命令,部署業(yè)務(wù)應(yīng)用。
kubectl apply -f mse-demo.yaml
使用如下內(nèi)容,創(chuàng)建mse-Ingress.yaml文件。
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: mse.ingress.kubernetes.io/service-subset: "" name: spring-cloud-a spec: ingressClassName: mse rules: - http: paths: - backend: service: name: spring-cloud-a port: number: 20001 path: / pathType: Prefix
執(zhí)行如下命令,創(chuàng)建Ingress規(guī)則。
kubectl apply -f mse-ingress.yaml
執(zhí)行如下命令,獲取外部IP。
kubectl get ingress
預(yù)期輸出如下所示:
NAME CLASS HOSTS ADDRESS PORTS AGE spring-cloud-a <none> * EXTERNAL_IP 80 12m
執(zhí)行如下命令,查看路由訪問情況。替換以下<EXTERNAL_IP>為您上一步獲取的外部IP。
curl http://<EXTERNAL_IP>/A/a
預(yù)期輸出如下所示:
A[192.168.42.115][config=base] -> B[192.168.42.118] -> C[192.168.42.101]%
步驟三:通過Kruise Rollouts實(shí)現(xiàn)自動(dòng)化的全鏈路灰度發(fā)布
定義Kruise Rollouts的灰度發(fā)布規(guī)則。
說明以下Rollout資源將定義灰度發(fā)布規(guī)則,發(fā)布分為三批:
A/B Testing發(fā)布,具有header[User-Agent]=xiaoming的流量將導(dǎo)入到新版本,其它則為老版本。
按照流量比例進(jìn)行灰度,此批次將灰度50%的實(shí)例及流量。
將灰度完成所有的實(shí)例。
使用如下內(nèi)容,創(chuàng)建rollout.yaml文件。
# 用于圈定全鏈路的應(yīng)用 # a rollout configuration apiVersion: rollouts.kruise.io/v1alpha1 kind: Rollout metadata: # 其它微服務(wù)應(yīng)用類似配置,例如:rollout-b, rollout-c name: rollout-a annotations: rollouts.kruise.io/trafficrouting: mse-traffic spec: objectRef: workloadRef: apiVersion: apps/v1 kind: Deployment name: spring-cloud-a strategy: canary: steps: - pause: {} replicas: 1 patchPodTemplateMetadata: labels: alicloud.service.tag: gray opensergo.io/canary-gray: gray --- # b rollout configuration apiVersion: rollouts.kruise.io/v1alpha1 kind: Rollout metadata: name: rollout-b annotations: rollouts.kruise.io/trafficrouting: mse-traffic spec: objectRef: workloadRef: apiVersion: apps/v1 kind: Deployment name: spring-cloud-b strategy: canary: steps: - pause: {} replicas: 1 patchPodTemplateMetadata: labels: alicloud.service.tag: gray opensergo.io/canary-gray: gray --- # c rollout configuration apiVersion: rollouts.kruise.io/v1alpha1 kind: Rollout metadata: name: rollout-c annotations: rollouts.kruise.io/trafficrouting: mse-traffic spec: objectRef: workloadRef: apiVersion: apps/v1 kind: Deployment name: spring-cloud-c strategy: canary: steps: - pause: {} replicas: 1 patchPodTemplateMetadata: labels: alicloud.service.tag: gray opensergo.io/canary-gray: gray --- # 全鏈路泳道配置 apiVersion: rollouts.kruise.io/v1alpha1 kind: TrafficRouting metadata: name: mse-traffic spec: objectRef: - service: spring-cloud-a ingress: classType: mse name: spring-cloud-a strategy: matches: # A/B Testing發(fā)布,基于header的流量灰度方式。 - headers: - type: Exact name: User-Agent value: xiaoming # 此批次灰度的流量比例50%。 #weight: 30 , 灰度30%流量 requestHeaderModifier: set: - name: x-mse-tag value: gray
執(zhí)行如下命令,將該Rollout資源下發(fā)到K8s集群。
kubectl apply -f rollout.yaml
執(zhí)行如下命令,查看Rollout資源的狀態(tài)。
kubectl get rollout
預(yù)期輸出STATUS=Healthy,表明Rollout資源正常工作。
升級(jí)應(yīng)用版本。
Kruise Rollouts是一個(gè)常態(tài)化的配置,將其下發(fā)到集群后,后續(xù)業(yè)務(wù)版本發(fā)布只需調(diào)整Deployment配置,無需再對(duì)Kruise Rollouts進(jìn)行額外操作。例如,業(yè)務(wù)將spring-cloud-a服務(wù)跟spring-cloud-c鏡像版本升級(jí)到mse-2.0.1,然后通過執(zhí)行
kubectl apply -f mse-demo.yaml
命令將Deployment部署到集群。將Deployment配置下發(fā)到K8s集群時(shí),除kubectl方式外,也可以使用Helm或Vela等方式。修改mse-demo.yaml文件,將spring-cloud-a服務(wù)跟spring-cloud-c鏡像版本升級(jí)到mse-2.0.1。
# a application --- apiVersion: apps/v1 kind: Deployment metadata: name: spring-cloud-a ... containers: - env: - name: JAVA_HOME value: /usr/lib/jvm/java-1.8-openjdk/jre image: registry.cn-hangzhou.aliyuncs.com/mse-demo-hz/spring-cloud-a:mse-2.0.0 imagePullPolicy: Always name: spring-cloud-a ports: - containerPort: 20001 ... # c application --- apiVersion: apps/v1 kind: Deployment metadata: name: spring-cloud-c spec: ... image: registry.cn-hangzhou.aliyuncs.com/mse-demo-hz/spring-cloud-c:mse-2.0.1
執(zhí)行如下命令,查看Rollout資源的狀態(tài)。
kubectl get rollouts rollouts-a -n default kubectl get rollouts rollouts-c -n default
預(yù)期輸出:
NAME STATUS CANARY_STEP CANARY_STATE MESSAGE AGE rollouts-a Progressing 1 StepPaused Rollout is in step(1/1), and you need manually confirm to enter the next step 41m rollouts-c Progressing 1 StepPaused Rollout is in step(1/1), and you need manually confirm to enter the next step 41m
通過預(yù)期輸出的STATUS和CANARY,可以觀察Rollout的過程以及步驟:
若預(yù)期輸出STATUS=Progressing:表明已經(jīng)在灰度發(fā)布過程中。
若預(yù)期輸出CANARY_STATE=StepPaused:表明當(dāng)前批次已經(jīng)完成,是否需要繼續(xù),可通過人工確認(rèn)。
確認(rèn)灰度發(fā)布的新版本發(fā)布正常后,繼續(xù)后續(xù)發(fā)布。
前面的步驟僅發(fā)布部分實(shí)例以及部分的金絲雀流量灰度,通過一些業(yè)務(wù)日志或監(jiān)控確認(rèn)新版本發(fā)布正常后,可通過
rollout.rollouts.kruise.io/<rollouts-demo> approved
命令繼續(xù)后續(xù)發(fā)布,其中<rollouts-demo>表示Rollout資源的名稱。(可選)若新版本服務(wù)異常,可進(jìn)行業(yè)務(wù)回滾。
如果在Rollout過程中,發(fā)現(xiàn)新版本服務(wù)異常,可以通過Deployment配置恢復(fù)到之前版本。然后通過
kubectl apply -f mse-demo.yaml
命令進(jìn)行部署,無需對(duì)Rollout資源做任何改動(dòng)。
在微服務(wù)治理架構(gòu)中,全鏈路灰度功能提供流量分流,極大地方便了測(cè)試和發(fā)布時(shí)的快速驗(yàn)證,結(jié)合Kruise Rollouts能夠大幅度幫助DevOps提升線上穩(wěn)定性。