作者:十眠、立衡
OpenKruise 是一个基于 Kubernetes 的扩展套件,主要聚焦于云原生应用的自动化,比如部署、发布、运维以及可用性防护。本文介绍通过 OpenKruise 构建自动化运维的方式实现全链路灰度功能。
在发布应用的过程中,我们通常希望用少量特定流量来验证新版本的发布是否正常,以保障整体稳定性。这个过程被称为灰度发布。关于灰度发布,我们通过逐步增加发布的范围,来验证新版本的稳定性。如果新版本出现问题,我们也能及时发现,控制影响范围,保障整体的稳定性。
渐进式发布一般具有以下特点:
据调研数据 70% 的线上问题都是由于变更导致,我们常说安全生产三板斧,可灰度、可观测、可回滚,也是为了控制变更带来的风险与影响面。通过采用灰度发布的方式,我们能够更加稳健地发布新版本,避免因发布过程中出现的问题而带来的损失。
在微服务架构的场景下,传统的灰度发布模式往往不能满足微服务交付的复杂、多样化的需求。这是因为:
为了解决这些问题,我们需要采用更加灵活、可控并且适用于微服务场景的发布方式,全链路灰度发布的场景也就应运而生。通常每个微服务都会有灰度环境或分组来接受灰度流量。我们希望进入上游灰度环境的流量也能进入下游灰度的环境中,确保1个请求始终在灰度环境中传递,从而形成流量“泳道”。在“泳道”内的流量链路中,即使这个调用链路上有一些微服务应用不存在灰度环境,那么这些微服务应用在请求下游应用的时候依然能够回到下游应用的灰度环境中。
全链路灰度为微服务发布保驾护航
这种方式可以根据服务的实际情况,可以对单个服务可以进行独立的发布和流量控制,也可以控制多个服务同时进行发布变更,从而保证整个系统的稳定性。同时,还可以采用自动化的部署方式,实现快速、可靠的发布过程,提高发布效率和稳定性。
在 K8s 中实现微服务全链路灰度发布是一个非常复杂的过程,需要涉及多个组件和配置的修改与协调。以下是具体的一些步骤和问题:
另外一方面,生产的流量是端到端的,那么意味着我们需要控制流量在前端、网关、后端各个微服务等组件中闭环。不仅仅是 RPC/Http 的流量,对于异步调用比如 MQ 流量我们也需要符合全链路“泳道”调用的规则,这整个过程中涉及到的流量控制的复杂度也是非常高的。
为了简化微服务全链路灰度发布的过程,可以使用一些自动化工具和产品,如 MSE、Kruise Rollout 等。这些工具和产品可以帮助我们更加便捷地实现微服务全链路灰度发布,并提高发布的效率和稳定性。
Kruise Rollout [ 1] 是 OpenKruise 社区开源提出的一个渐进式交付框架。其设计理念是提供一组能够将流量发布与实例灰度相结合,支持金丝雀、蓝绿、A/B Testing 等多样化发布形式,以及支持基于 Prometheus Metrics 等自定义 Metrics 实现发布过程自动化,无感对接、易扩展的旁路式标准 Kubernetes 发布组件。主要特性如下:
Kruise Rollout 本身就支持各种灰度发布的能力(金丝雀、A/B Testing、蓝绿发布),深入了解后发现它的发布模型非常契合 MSE 全链路灰度,因此与 Kruise Rollout 结合后可以非常方便的让用户实现 MSE 全链路灰度发布能力。
我们可以参考 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 来定义全链路灰度发布的流程。
# 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
# 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 的流量为灰度流量。
接下来,我们快速进行一次灰度发布与验证吧。
# 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
GitOps 是一种持续交付的方式,它的核心思想是将应用系统的声明性基础架构和应用程序存放在 Git 版本库中。将 Git 作为交付流水线的核心,每个开发人员都可以提交拉取请求(Pull Request)并使用 Git 来加速和简化 Kubernetes 的应用程序部署和运维任务。通过使用像 Git 这样的简单工具,开发人员可以更高效地将注意力集中在创建新功能而不是运维相关任务上(例如,应用系统安装、配置、迁移等)。
试想一下,做为Developer,我们希望提交的 YAML 编写的应用程序定义(Deployment)可以先进行自动化的灰度环境发布,流量经过充分验证后,确定新版本的应用程序没有问题后,再进一步进行全量的应用发布。如何可以做到呢?接下来我们演示通过整合 ArgoCD 来实现的全链路灰度能力。
安装 ArgoCD,参考 ArgoCD [ 3] ,ArgoCD 是用于 Kubernetes的 声明性 GitOps 连续交付工具。
在 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。
✗ 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
spring-cloud-b-7bc9db878f-n7pzp 1/1 Running 0 16h 192.168.0.193 us-west-1.192.168.0.189
spring-cloud-c-554458c696-2vp74 1/1 Running 0 137m 192.168.0.200 us-west-1.192.168.0.145
spring-cloud-c-554458c696-g8vbg 1/1 Running 0 136m 192.168.0.192 us-west-1.192.168.0.188
spring-cloud-c-md42b-74858b7c4-qzdxz 1/1 Running 0 53m 192.168.0.165 us-west-1.192.168.0.147
架构图如下:
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 微服务引擎一直致力于帮助企业打造永远在线的应用,相信只有经过客户场景持续打磨的产品才会愈发历久弥新。
最后,非常欢迎你通过 Github/Slack/钉钉/微信 等方式加入我们来参与 OpenKruise 开源社区。
如果您觉得 Higress 对您有帮助,欢迎前往 github: Higress [ 4] 为我们 star 一下!
相关链接:
[1] Kruise Rollout
https://openkruise.io/rollouts/introduction
[2] MSE云原生网关全链路灰度
https://help.aliyun.com/zh/mse/configure-an-end-to-end-canary-release-based-on-mse-ingress-gateways#p-omu-6xu-8wm
[3] ArgoCD
https://argo-cd.readthedocs.io/en/stable/getting_started/
[4] github: Higress
https://github.com/alibaba/higress