如何用20分钟就能获得同款企业级全链路灰度能力?

网友投稿 265 2022-10-09

如何用20分钟就能获得同款企业级全链路灰度能力?

作者:十眠审核&校对:望陶、亦盏编辑&排版:雯燕 今年双 11,云原生中间件完成了开源、自研、商业化三位一体,全面升级到中间件云产品。MSE 微服务治理通过 Dubbo3.0 支撑了阿里集团核心业务双 11 的流量洪峰,截止目前集团内 50% 的用户已经习惯使用 MSE 微服务治理 HSF 和 Dubbo3.0 应用,今天我们来细聊一下 MSE 服务治理专业版中的全链路灰度能力,以及它在生产大规模实践的一些场景。

背景

微服务架构下,有一些需求开发,涉及到微服务调用链路上的多个微服务同时发生了改动,需要通过灰度发布方式来更好地控制新版本服务上线的风险和爆炸半径。通常每个微服务都会有灰度环境或分组来接受灰度流量,我们希望通过进入上游灰度环境的流量,也能进入下游灰度的环境中,确保 1 个请求始终在灰度环境中传递,即使这个调用链路上有一些微服务没有灰度环境,这些应用请求下游的时候依然能够回到灰度环境中。通过 MSE 提供的全链路灰度能力,可以在不需要修改任何您的业务代码的情况下,能够轻松实现上述能力。

MSE 微服务治理全链路灰度特点

全链路灰度作为 MSE 服务治理专业版中的拳头功能,具备以下六大特点

可通过定制规则引入精细化流量

全链路隔离流量泳道

1) 通过设置流量规则对所需流量进行“染色”,“染色”流量会路由到灰度机器。 2) 灰度流量携带灰度标往下游传递,形成灰度专属环境流量泳道,无灰度环境应用会默认选择未打标的基线环境。

端到端的稳定基线环境

未打标的应用属于基线稳定版本的应用,即稳定的线上环境。当我们将发布对应的灰度版本代码,然后可以配置规则定向引入特定的线上流量,控制灰度代码的风险。

流量一键动态切流

流量规则定制后,可根据需求进行一键停启,增删改查,实时生效。灰度引流更便捷。

低成本接入,基于 Java Agent 技术实现无需修改一行业务代码

MSE 微服务治理能力基于 Java Agent 字节码增强的技术实现,无缝支持市面上近 5 年的所有 Spring Cloud 和 Dubbo 的版本,用户不用改一行代码就可以使用,不需要改变业务的现有架构,随时可上可下,没有绑定。只需开启 MSE 微服务治理专业版,在线配置,实时生效。

具备无损上下线能力,使得发布更加丝滑

应用开启 MSE 微服务治理后就具备无损上下线能力,大流量下的发布、回滚、扩容、缩容等场景,均能保证流量无损。

大规模生产实践的场景

本文主要介绍 MSE 微服务治理在支持大客户过程中总结抽象出来的常用的几个全链路灰度方案生产落地实践的场景。

场景一:对经过机器的流量进行自动染色,实现全链路灰度

进入带 tag 的节点后续调用优先选择带有相同 tag 的节点,即对经过 tag 节点的流量进行“染色”。  有 tag 的调用链路上找不到相同 tag 的节点,则 fallback 到无 tag 的节点。  有 tag 的调用链路经过无 tag 的节点,如果链路后续调用有 tag 的节点,则恢复 tag 调用的模式。

场景二:通过给流量带上特定的 header 实现全链路灰度

场景三:通过自定义路由规则来进行全链路灰度

全链路灰度的实践

我们如何快速获得上述同款全链路灰度的能力呢?下面我会带大家从 0 到 1 快速搭建我们的全链路灰度能力。 我们假设应用的架构由 Ingress-nginx 以及后端的微服务架构(Spring Cloud)来组成,后端调用链路有 3 跳,购物车(a),交易中心(b),库存中心(c),他们通过 Nacos 注册中心做服务发现,客户端通过客户端或者是 H5 页面来访问后端服务。

前提条件

安装 Ingress-nginx 组件

$ kubectl get deployment -n kube-system NAME READY UP-TO-DATE AVAILABLE AGE ack-ingress-nginx-default-controller 2/2 2 2 18h

开启 MSE 微服务治理专业版

部署 Demo 应用程序

将下面的文件保存到 ingress-gray.yaml 中,并执行 kubectl apply -f ingress-gray.yaml 以部署应用,这里我们将要部署 A, B, C 三个应用,每个应用分别部署一个基线版本和一个灰度版本。

# A 应用 base 版本 --- apiVersion: apps/v1 kind: Deployment metadata: labels: app: spring-cloud-a 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: LANG value: C.UTF-8 - name: JAVA_HOME value: /usr/lib/jvm/java-1.8-openjdk/jre image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-a:0.1-SNAPSHOT imagePullPolicy: Always name: spring-cloud-a ports: - containerPort: 20001 protocol: TCP resources: requests: cpu: 250m memory: 512Mi livenessProbe: tcpSocket: port: 20001 initialDelaySeconds: 10 periodSeconds: 30 # A 应用 gray 版本 --- apiVersion: apps/v1 kind: Deployment metadata: labels: app: spring-cloud-a-new name: spring-cloud-a-new spec: replicas: 2 selector: matchLabels: app: spring-cloud-a-new strategy: template: metadata: annotations: alicloud.service.tag: gray msePilotCreateAppName: spring-cloud-a labels: app: spring-cloud-a-new spec: containers: - env: - name: LANG value: C.UTF-8 - name: JAVA_HOME value: /usr/lib/jvm/java-1.8-openjdk/jre - name: profiler.micro.service.tag.trace.enable value: "true" image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-a:0.1-SNAPSHOT imagePullPolicy: Always name: spring-cloud-a-new ports: - containerPort: 20001 protocol: TCP resources: requests: cpu: 250m memory: 512Mi livenessProbe: tcpSocket: port: 20001 initialDelaySeconds: 10 periodSeconds: 30 # B 应用 base 版本 --- apiVersion: apps/v1 kind: Deployment metadata: labels: app: spring-cloud-b name: spring-cloud-b spec: replicas: 2 selector: matchLabels: app: spring-cloud-b strategy: template: metadata: annotations: msePilotCreateAppName: spring-cloud-b labels: app: spring-cloud-b spec: containers: - env: - name: LANG value: C.UTF-8 - name: JAVA_HOME value: /usr/lib/jvm/java-1.8-openjdk/jre image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-b:0.1-SNAPSHOT imagePullPolicy: Always name: spring-cloud-b ports: - containerPort: 8080 protocol: TCP resources: requests: cpu: 250m memory: 512Mi livenessProbe: tcpSocket: port: 20002 initialDelaySeconds: 10 periodSeconds: 30 # B 应用 gray 版本 --- apiVersion: apps/v1 kind: Deployment metadata: labels: app: spring-cloud-b-new name: spring-cloud-b-new spec: replicas: 2 selector: matchLabels: app: spring-cloud-b-new template: metadata: annotations: alicloud.service.tag: gray msePilotCreateAppName: spring-cloud-b labels: app: spring-cloud-b-new spec: containers: - env: - name: LANG value: C.UTF-8 - name: JAVA_HOME value: /usr/lib/jvm/java-1.8-openjdk/jre image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-b:0.1-SNAPSHOT imagePullPolicy: Always name: spring-cloud-b-new ports: - containerPort: 8080 protocol: TCP resources: requests: cpu: 250m memory: 512Mi livenessProbe: tcpSocket: port: 20002 initialDelaySeconds: 10 periodSeconds: 30 # C 应用 base 版本 --- apiVersion: apps/v1 kind: Deployment metadata: labels: app: spring-cloud-c 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: LANG value: C.UTF-8 - name: JAVA_HOME value: /usr/lib/jvm/java-1.8-openjdk/jre image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-c:0.1-SNAPSHOT imagePullPolicy: Always name: spring-cloud-c ports: - containerPort: 8080 protocol: TCP resources: requests: cpu: 250m memory: 512Mi livenessProbe: tcpSocket: port: 20003 initialDelaySeconds: 10 periodSeconds: 30 # C 应用 gray 版本 --- apiVersion: apps/v1 kind: Deployment metadata: labels: app: spring-cloud-c-new name: spring-cloud-c-new spec: replicas: 2 selector: matchLabels: app: spring-cloud-c-new template: metadata: annotations: alicloud.service.tag: gray msePilotCreateAppName: spring-cloud-c labels: app: spring-cloud-c-new spec: containers: - env: - name: LANG value: C.UTF-8 - name: JAVA_HOME value: /usr/lib/jvm/java-1.8-openjdk/jre image: registry.cn-shanghai.aliyuncs.com/yizhan/spring-cloud-c:0.1-SNAPSHOT imagePullPolicy: IfNotPresent name: spring-cloud-c-new ports: - containerPort: 8080 protocol: TCP resources: requests: cpu: 250m memory: 512Mi livenessProbe: tcpSocket: port: 20003 initialDelaySeconds: 10 periodSeconds: 30 # Nacos Server --- apiVersion: apps/v1 kind: Deployment metadata: labels: app: nacos-server 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:latest imagePullPolicy: Always name: nacos-server resources: requests: cpu: 250m memory: 512Mi dnsPolicy: ClusterFirst restartPolicy: Always # Nacos Server Service 配置 --- apiVersion: v1 kind: Service metadata: name: nacos-server spec: ports: - port: 8848 protocol: TCP targetPort: 8848 selector: app: nacos-server type: ClusterIP

动手实践

场景一:对经过机器的流量进行自动染色,实现全链路灰度

apiVersion: v1 kind: Service metadata: name: spring-cloud-a-base spec: ports: - name: http port: 20001 protocol: TCP targetPort: 20001 selector: app: spring-cloud-a --- apiVersion: v1 kind: Service metadata: name: spring-cloud-a-gray spec: ports: - name: http port: 20001 protocol: TCP targetPort: 20001 selector: app: spring-cloud-a-new

配置入口的 Ingress 规则,访问 base.com 路由到 A 应用的 base 版本,访问 gray.com 路由到 A 应用的 gray 版本。

apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: spring-cloud-a-base spec: rules: - host: base.com http: paths: - backend: serviceName: spring-cloud-a-base servicePort: 20001 path: / --- apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: spring-cloud-a-gray spec: rules: - host: gray.com http: paths: - backend: serviceName: spring-cloud-a-gray servicePort: 20001 path: /

结果验证

此时,访问 base.com 路由到 基线环境

curl -H"Host:base.com" http://106.14.155.223/a A[172.18.144.155] -> B[172.18.144.120] -> C[172.18.144.79]

此时,访问 gray.com 路由到灰度环境

curl -H"Host:gray.com" http://106.14.155.223/a Agray[172.18.144.160] -> Bgray[172.18.144.57] -> Cgray[172.18.144.157]

进一步的,如果入口应用 A 没有灰度环境,访问到 A 的 base 环境,又需要在 A -> B 的时候进入灰度环境,则可以通过增加一个特殊的 header x-mse-tag 来实现,header 的值是想要去的环境的标签,例如 gray。

curl -H"Host:base.com" -H"x-mse-tag:gray" http://106.14.155.223/a A[172.18.144.155] -> Bgray[172.18.144.139] -> Cgray[172.18.144.8]

可以看到第一跳,进入了 A 的 base 环境,但是 A->B 的时候又重新回到了灰度环境。 这种使用方式的好处是,配置简单,只需要在 Ingress 处配置好规则,某个应用需要灰度发布的时候,只需要在灰度环境中部署好应用,灰度流量自然会进入到灰度机器中,如果验证没问题,则将灰度的镜像发布到基线环境中;如果一次变更有多个应用需要灰度发布,则把他们都加入到灰度环境中即可。

最佳实践

给所有灰度环境的应用打上 gray 标,基线环境的应用默认不打标。  线上常态化引流 2% 的流量进去灰度环境中

场景二:通过给流量带上特定的 header 实现全链路灰度

apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: spring-cloud-a-base spec: rules: - host: demo.com http: paths: - backend: serviceName: spring-cloud-a-base servicePort: 20001 path: / --- apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: spring-cloud-a-gray annotations: nginx.ingress.kubernetes.io/canary: "true" nginx.ingress.kubernetes.io/canary-by-header: "x-mse-tag" nginx.ingress.kubernetes.io/canary-by-header-value: "gray" nginx.ingress.kubernetes.io/canary-weight: "0" spec: rules: - host: base.com http: paths: - backend: serviceName: spring-cloud-a-gray servicePort: 20001 path: /

结果验证

此时,访问 demo.com 路由到基线环境

curl -H"Host:demo.com" http://106.14.155.223/a A[172.18.144.155] -> B[172.18.144.56] -> C[172.18.144.156]

如何访问灰度环境呢?只需要在请求中增加一个header x-mse-tag:gray 即可。

curl -H"Host:demo.com" -H"x-mse-tag:gray" http://106.14.155.223/a Agray[172.18.144.82] -> Bgray[172.18.144.57] -> Cgray[172.18.144.8]

可以看到 Ingress 根据这个header直接路由到了 A 的 gray 环境中。

更进一步

apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: spring-cloud-a-base spec: rules: - host: demo.com http: paths: - backend: serviceName: spring-cloud-a-base servicePort: 20001 path: / --- apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: spring-cloud-a-base-gray annotations: nginx.ingress.kubernetes.io/canary: "true" nginx.ingress.kubernetes.io/canary-by-header: "x-user-id" nginx.ingress.kubernetes.io/canary-by-header-value: "100" nginx.ingress.kubernetes.io/canary-weight: "0" spec: rules: - host: demo.com http: paths: - backend: serviceName: spring-cloud-a-gray servicePort: 20001 path: /

访问的时候带上特殊的 header ,满足条件进入灰度环境

curl -H"Host:demo.com" -H"x-user-id:100" http://106.14.155.223/a Agray[172.18.144.93] -> Bgray[172.18.144.24] -> Cgray[172.18.144.25]

不满足条件的请求,进入基线环境:

curl -H"Host:demo.com" -H"x-user-id:101" http://106.14.155.223/a A[172.18.144.91] -> B[172.18.144.22] -> C[172.18.144.95]

相比场景一这样的好处是,客户端的域名不变,只需要通过请求来区分。

场景三:通过自定义路由规则来进行全链路灰度

apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: spring-cloud-a-base spec: rules: - host: base.com http: paths: - backend: serviceName: spring-cloud-a-base servicePort: 20001 path: / --- apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: annotations: nginx.ingress.kubernetes.io/canary: 'true' nginx.ingress.kubernetes.io/canary-by-header: x-user-id nginx.ingress.kubernetes.io/canary-by-header-value: '100' nginx.ingress.kubernetes.io/canary-weight: '0' name: spring-cloud-a-gray spec: rules: - host: base.com http: paths: - backend: serviceName: spring-cloud-a-gray servicePort: 20001 path: /

结果验证

测试验证,访问灰度环境,带上满足条件的 header,路由到 B 的灰度环境中。

curl 120.77.215.62/a -H "Host: base.com" -H "x-user-id: 100" Agray[192.168.86.42] -> Bgray[192.168.74.4] -> C[192.168.86.33]

访问灰度环境,带上不满足条件的 header,路由到 B 的base环境中。

curl 120.77.215.62/a -H "Host: base.com" -H "x-user-id: 101" A[192.168.86.35] -> B[192.168.73.249] -> C[192.168.86.33]

去掉 Ingress Canary 配置,访问 base A 服务(基线环境入口应用需要加上 alicloud.service.header 环境变量),带上满足条件的 header,路由到 B 的灰度环境中。

curl 120.77.215.62/a -H "Host: base.com" -H "x-user-id: 100" A[192.168.86.35] -> Bgray[192.168.74.4] -> C[192.168.86.33]

访问 base 环境,带上不满足条件的 header,路由到 B 的 base 环境中。

curl 120.77.215.62/a -H "Host: base.com" -H "x-user-id: 101" A[192.168.86.35] -> B[192.168.73.249] -> C[192.168.86.33]

总结

20 分钟快速实践完具有很大技术难度的全链路灰度能力,全链路灰度其实并不是那么难! 基于 MSE 服务治理的全链路灰度能力,我们可以快速落地企业级的全链路灰度能力,以上三种场景是我们在生产实践中大规模落地的标准场景,当然我们可以基于 MSE 服务治理的能力根据自己的业务个性化定制与适配;即使在多种流量来源的背景下,也能做到按照业务定制精准引流。 同时 MSE 服务治理专业版的可观测性能力使得灰度有效性得到可衡量,灰没灰到,灰得咋样,做到“心里有数”。

灰度流量秒级监控

规范发布流程

日常发布中,我们常常会有如下一些错误的想法:

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:SpringBoot中shiro过滤器的重写与配置详解
下一篇:ALB Ingress 发布!轻松应对云原生应用流量管理
相关文章

 发表评论

暂时没有评论,来抢沙发吧~