通过 OpenKruise 实现基于 Higress 的全链路灰度
作者:十眠、立衡
OpenKruise 是一个基于 Kubernetes 的扩展套件,主要聚焦于云原生应用的自动化,比如部署、发布、运维以及可用性防护。本文介绍通过 OpenKruise 构建自动化运维的方式实现全链路灰度功能。
灰度发布提高应用交付的稳定性和效率
在发布应用的过程中,我们通常希望用少量特定流量来验证新版本的发布是否正常,以保障整体稳定性。这个过程被称为灰度发布。关于灰度发布,我们通过逐步增加发布的范围,来验证新版本的稳定性。如果新版本出现问题,我们也能及时发现,控制影响范围,保障整体的稳定性。
渐进式发布一般具有以下特点:
- 逐步增加发布的影响范围,拒绝一次性全部发布;
- 阶段性的发布过程,可以通过金丝雀发布方式小心验证,以验证新版本的稳定性;
- 可暂停、可回滚、可继续、可自动化状态流转,以便灵活地控制发布过程并确保稳定性。
据调研数据 70% 的线上问题都是由于变更导致,我们常说安全生产三板斧,可灰度、可观测、可回滚,也是为了控制变更带来的风险与影响面。通过采用灰度发布的方式,我们能够更加稳健地发布新版本,避免因发布过程中出现的问题而带来的损失。
微服务架构对灰度发布提出了更高的要求
在微服务架构的场景下,传统的灰度发布模式往往不能满足微服务交付的复杂、多样化的需求。这是因为:
- 微服务调用链路比较长,比较复杂。在微服务架构中,服务之间的调用链路比较复杂,一个服务的改动可能会影响到整个调用链路,从而影响整个应用的稳定性。
- 一次灰度可能涉及多个模块,整个链路都要调用新版本。由于微服务架构中服务之间相互依赖,一个服务的修改可能需要其他服务的相应调整。这就导致了在进行灰度发布时,需要同时调用多个服务的新版本,增加了发布的复杂度和不确定性。
- 多个项目并行,需要部署多套环境,环境构建不灵活、成本高。在微服务架构中,往往会有多个项目并行开发,需要部署多套环境来支持不同的项目。这就增加了环境构建的难度和成本,从而导致发布效率低下。
为了解决这些问题,我们需要采用更加灵活、可控并且适用于微服务场景的发布方式,全链路灰度发布的场景也就应运而生。通常每个微服务都会有灰度环境或分组来接受灰度流量。我们希望进入上游灰度环境的流量也能进入下游灰度的环境中,确保1个请求始终在灰度环境中传递,从而形成流量“泳道”。在“泳道”内的流量链路中,即使这个调用链路上有一些微服务应用不存在灰度环境,那么这些微服务应用在请求下游应用的时候依然能够回到下游应用的灰度环境中。
全链路灰度为微服务发布保驾护航
这种方式可以根据服务的实际情况,可以对单个服务可以进行独立的发布和流量控制,也可以控制多个服务同时进行发布变更,从而保证整个系统的稳定性。同时,还可以采用自动化的部署方式,实现快速、可靠的发布过程,提高发布效率和稳定性。
实践全链路灰度的挑战
在 K8s 中实现微服务全链路灰度发布是一个非常复杂的过程,需要涉及多个组件和配置的修改与协调。以下是具体的一些步骤和问题:
- 在微服务架构中,网关是服务的入口,需要根据灰度发布的要求,调整网关配置,实现路由匹配和流量特征(比如 Header 修改)。
- 为了实现全链路灰度发布,需要新部署一套灰度应用环境,并为其打上灰度标记(新部署一套 Gray 应用以及 Gray 灰度标)。这样可以将流量流向灰度环境,从而实现灰度发布。
- 验证流量正常,将基线环境升级,销毁灰度环境,恢复网关配置。在灰度发布过程中,需要对流量进行验证,确保流量的正常流向和服务的正常运行。如果验证通过,可以将基线环境升级到灰度版本,并销毁灰度环境。最后,需要恢复网关的配置,以确保流量正常流向。
- 如果发生异常,需要快速回滚。由于微服务架构复杂,可能会出现各种异常情况,比如服务崩溃、流量异常等。在这种情况下,需要快速回滚,以避免产生更大的损失。因此,需要预先设计好回滚方案,并在发生异常时快速执行回滚操作。
另外一方面,生产的流量是端到端的,那么意味着我们需要控制流量在前端、网关、后端各个微服务等组件中闭环。不仅仅是 RPC/Http 的流量,对于异步调用比如 MQ 流量我们也需要符合全链路“泳道”调用的规则,这整个过程中涉及到的流量控制的复杂度也是非常高的。
为了简化微服务全链路灰度发布的过程,可以使用一些自动化工具和产品,如 MSE、Kruise Rollout 等。这些工具和产品可以帮助我们更加便捷地实现微服务全链路灰度发布,并提高发布的效率和稳定性。
Kruise Rollout+MSE 端到端的全链路灰度发布实践
为什么要 Kruise Rollout?
Kruise Rollout [ 1] 是 OpenKruise 社区开源提出的一个渐进式交付框架。其设计理念是提供一组能够将流量发布与实例灰度相结合,支持金丝雀、蓝绿、A/B Testing 等多样化发布形式,以及支持基于 Prometheus Metrics 等自定义 Metrics 实现发布过程自动化,无感对接、易扩展的旁路式标准 Kubernetes 发布组件。主要特性如下:
- 非侵入性: 不对用户的应用交付配置做任何的侵入,使用旁路的方式来扩展渐进式交付的能力,并且能够做到即插即用的效果。
- 可扩展性: 充分考虑了对多种类似的工作负载的支持(Deployment、StatefulSet、CloneSet 以及自定义 CRD 工作负载);在流量调度方面,通过 lua 脚本的方案能够支持 Nginx、Alb、Mse、Gateway API 等多种流量调度方案。
Kruise Rollout 本身就支持各种灰度发布的能力(金丝雀、A/B Testing、蓝绿发布),深入了解后发现它的发布模型非常契合 MSE 全链路灰度,因此与 Kruise Rollout 结合后可以非常方便的让用户实现 MSE 全链路灰度发布能力。
MSE 全链路灰度发布的最佳实践
01. 部署应用 & 配置全链路灰度发布 CRD
我们可以参考 MSE 云原生网关全链路灰度 [ 2] 文档部署 Demo 应用。
➜ ~ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
demo-mysql 1/1 1 1 30h
nacos-server 1/1 1 1 46h
spring-cloud-a 2/2 2 2 30h
spring-cloud-b 2/2 2 2 30h
spring-cloud-c 2/2 2 2 30h
部署完应用之后,我们首先要区分线上流量和灰度流量。
我们通过创建 Rollout CRD 来定义全链路灰度发布的流程。
- 将整个链路涉及到的应用创建 Rollout 配置
# a rollout configuration
---
apiVersion: rollouts.kruise.io/v1alpha1
kind: Rollout
metadata:
name: rollout-a
spec:
objectRef:
workloadRef:
apiVersion: apps/v1
kind: Deployment
name: spring-cloud-a
...
# b rollout configuration
---
apiVersion: rollouts.kruise.io/v1alpha1
kind: Rollout
metadata:
name: rollout-b
annotations:
rollouts.kruise.io/dependency: rollout-a
spec:
objectRef:
workloadRef:
apiVersion: apps/v1
kind: Deployment
name: spring-cloud-b
...
# c rollout configuration
---
apiVersion: rollouts.kruise.io/v1alpha1
kind: Rollout
metadata:
name: rollout-c
annotations:
rollouts.kruise.io/dependency: rollout-a
spec:
objectRef:
workloadRef:
apiVersion: apps/v1
kind: Deployment
name: spring-cloud-c
...
- 然后为了表明线上流量与灰度流量的特征,即在入口服务上配置灰度流量规则
canary:
steps:
- matches:
- headers:
- type: Exact
name: x-user-id
value: '100'
requestHeaderModifier:
set:
- name: x-mse-tag
value: gray
trafficRoutings:
- service: spring-cloud-a
ingress:
name: spring-cloud-a
classType: mse
- 针对灰度发布中的应用,Kruise 会自动给他标识 gray 版本
# only support for canary deployment type
patchCanaryMetadata:
labels:
alicloud.service.tag: gray
安装完成 Rollout CRD 后,我们可以查看一下:
➜ ~ kubectl get Rollout
NAME STATUS CANARY_STEP CANARY_STATE MESSAGE AGE
rollout-a Healthy 1 Completed workload deployment is completed 4s
rollout-b Healthy 1 Completed workload deployment is completed 4s
rollout-c Healthy 1 Completed workload deployment is completed 4s
到目前为止我们定义了这样一组全链路灰度发布的规则,发布链路涉及 MSE 云原生网关、A、B、C 应用,其中 x-user-id=100 的流量为灰度流量。
接下来,我们快速进行一次灰度发布与验证吧。
02. 灰度发布&验证流量
- 本次发布涉及到应用 A、C 的改动,因此我们直接编辑应用 A、C 的 Deployment yaml,进行变更操作
# a application
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-cloud-a
spec:
...
template:
...
spec:
# 修改 mse-1.0.0 -> mse-2.0.0,触发应用A发布,以及MSE全链路灰度
image: registry.cn-hangzhou.aliyuncs.com/mse-demo-hz/spring-cloud-a:mse-2.0.0
# c application
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-cloud-c
spec:
...
template:
...
spec:
# 修改 mse-1.0.0 -> mse-2.0.0,触发应用A发布,以及MSE全链路灰度
image: registry.cn-hangzhou.aliyuncs.com/mse-demo-hz/spring-cloud-c:mse-2.0.0
- 变更完成之后,我们查看我们当前应用的形态
➜ ~ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
demo-mysql 1/1 1 1 30h
nacos-server 1/1 1 1 46h
spring-cloud-a 2/2 0 2 30h
spring-cloud-a-84gcd 1/1 1 1 86s
spring-cloud-b 2/2 2 2 30h
spring-cloud-c 2/2 0 2 30h
spring-cloud-c-qzh9p 1/1 1 1 113s
我们发现,Kruise Rollout 并没有直接修改我们原先的 deployment,而是先给我们创建了两个灰度应用 spring-cloud-a-84gcd、spring-cloud-c-qzh9p。
- 等应用启动就绪我们分别来验证线上正常流量以及灰度流量
a. 访问网关,如果不符合灰度规则,走基线环境:
➜ ~ curl -H "Host: example.com" http://39.98.205.236/a
A[192.168.42.115][config=base] -> B[192.168.42.118] -> C[192.168.42.101]%
b. 如何符合灰度规则,走灰度环境:
➜ ~ curl -H "Host: example.com" http://39.98.205.236/a -H "x-user-id: 100"
Agray[192.168.42.119][config=base] -> B[192.168.42.118] -> Cgray[192.168.42.116]%
- 如果碰到预期外的行为,我们如何进行快速回滚?
我们尝试回滚一下 C 应用,只需将 C 应用的 Deployment 改回原先配置即可。
# c application
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-cloud-c
spec:
...
template:
...
spec:
# 修改 mse-2.0.0 -> mse-1.0.0,回滚c应用
image: registry.cn-hangzhou.aliyuncs.com/mse-demo-hz/spring-cloud-c:mse-2.0.0
修改完成后,我们发现 C 应用的灰度 Deployment 已经没了。
➜ ~ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
demo-mysql 1/1 1 1 30h
nacos-server 1/1 1 1 46h
spring-cloud-a 2/2 0 2 30h
spring-cloud-a-84gcd 1/1 1 1 186s
spring-cloud-b 2/2 2 2 30h
spring-cloud-c 2/2 0 2 30h
- 如何完成发布?只需 kubectl-kruise rollout approve rollouts/rollout-a 将灰度中的应用进行完成发布
03. 整合 ArgoCD 实现基于 GitOps 的全链路灰度
GitOps 是一种持续交付的方式,它的核心思想是将应用系统的声明性基础架构和应用程序存放在 Git 版本库中。将 Git 作为交付流水线的核心,每个开发人员都可以提交拉取请求(Pull Request)并使用 Git 来加速和简化 Kubernetes 的应用程序部署和运维任务。通过使用像 Git 这样的简单工具,开发人员可以更高效地将注意力集中在创建新功能而不是运维相关任务上(例如,应用系统安装、配置、迁移等)。
试想一下,做为Developer,我们希望提交的 YAML 编写的应用程序定义(Deployment)可以先进行自动化的灰度环境发布,流量经过充分验证后,确定新版本的应用程序没有问题后,再进一步进行全量的应用发布。如何可以做到呢?接下来我们演示通过整合 ArgoCD 来实现的全链路灰度能力。
前提条件
安装 ArgoCD,参考 ArgoCD [ 3] ,ArgoCD 是用于 Kubernetes的 声明性 GitOps 连续交付工具。
通过 ArgoCD 配置部署应用资源
-
在 ArgoCD 中创建 spring-cloud-c 应用
-
在 ArgoCD 管理界面,单击 NEW APP,进行如下配置。
a. 在 GENERAL 区域,配置 Application为spring-cloud-c,Project 为 default。
b. 在 SOURCE 区域,配置 Repository URL 为 https://github.com/aliyun/alibabacloud-microservice-demo.git,Revision 为 argocd-samples,Path 为 argocd-samples/spring-cloud-c。
c. 在 DESTINATION 区域,配置 Cluster URL 为 https://kubernetes.default.svc,Namespace为 default。
d. 配置完成,单击页面上方的 CREATE。
- 创建完成后,在 ArgoCD 管理界面,即可查看 spring-cloud-c 应用状态
- 单击对应应用即可查看资源部署状态
- 我们修改 argocd-samples/spring-cloud-c 中的 spring-cloud-c.yaml,并通过 git 提交
- 我们可以发现,自动部署了 spring-cloud-c 应用的灰度版本
✗ kubectl get pods -o wide | grep spring-cloud
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
spring-cloud-a-69d577cc9-g7sbc 1/1 Running 0 16h 192.168.0.191 us-west-1.192.168.0.187 <none> <none>
spring-cloud-b-7bc9db878f-n7pzp 1/1 Running 0 16h 192.168.0.193 us-west-1.192.168.0.189 <none> <none>
spring-cloud-c-554458c696-2vp74 1/1 Running 0 137m 192.168.0.200 us-west-1.192.168.0.145 <none> <none>
spring-cloud-c-554458c696-g8vbg 1/1 Running 0 136m 192.168.0.192 us-west-1.192.168.0.188 <none> <none>
spring-cloud-c-md42b-74858b7c4-qzdxz 1/1 Running 0 53m 192.168.0.165 us-west-1.192.168.0.147 <none> <none>
架构图如下:
- 等应用启动就绪我们分别来验证线上正常流量以及灰度流量
a. 访问网关,如果不符合灰度规则,走基线环境:
➜ ~ curl -H "Host: example.com" http://39.98.205.236/a
A[192.168.0.191][config=base] -> B[192.168.0.193] -> C[192.168.0.200]%
b. 如何符合灰度规则,走灰度环境:
➜ ~ curl -H "Host: example.com" http://39.98.205.236/a -H "x-user-id: 100"
A[192.168.0.191][config=base] -> B[192.168.0.193] -> Cgray[192.168.0.165]%
-
回滚只需我们通过 git 回滚 argocd-samples/spring-cloud-c 中的 spring-cloud-c.yaml 的上一次提交即可。
-
结束发布通过 kubectl-kruise rollout approve rollouts/rollout-c 将灰度中的应用进行完成发布。
展望与总结
Kruise Rollout 是 OpenKruise 社区在渐进式交付领域的探索,此次与 MSE 合作在云原生领域落地了微服务场景的灰度发布场景。未来,Kruise Rollout 将在可扩展性方面做出持续的努力,比如:基于 Lua 脚本的可扩展流量调度方案,从而兼容社区更多的网关与架构(Istio、Apifix 等)。
在微服务治理架构中,全链路灰度功能能提供流量泳道,极大的方便了测试、发布时的快速验证,通过精确的引流规则将“爆炸半径”控制到最小,能够帮助 DevOps 提升线上稳定性。
MSE 的全链路灰度能力也在随着客户场景的深入而不断扩展与迭代。我们除了通过 MSE 全链路灰度能力保障发布态的稳定性外,还可以在运行态通过 MSE 解决流量、依赖、基础设施等不稳定的风险,MSE 微服务引擎一直致力于帮助企业打造永远在线的应用,相信只有经过客户场景持续打磨的产品才会愈发历久弥新。
相关社区
加入 OpenKruise 社区
最后,非常欢迎你通过 Github/Slack/钉钉/微信 等方式加入我们来参与 OpenKruise 开源社区。
- 加入社区 Slack channel (English)
- 加入社区钉钉群:搜索群号 23330762 (Chinese)
- 加入社区微信群(新):添加用户 openkruise 并让机器人拉你入群 (Chinese)
如果您觉得 Higress 对您有帮助,欢迎前往 github: Higress [ 4] 为我们 star 一下!
相关链接:
[1] Kruise Rollout
https://openkruise.io/rollouts/introduction
[2] MSE云原生网关全链路灰度
[3] ArgoCD
https://argo-cd.readthedocs.io/en/stable/getting_started/
[4] github: Higress