介绍
要想实现标题的功能,需要先了解什么是 Kruise Rollout 和什么是全链路灰度。
Kruise Rollout
官方文档:https://openkruise.io/zh/rollouts/introduction
Kruise Rollout 是一个 Bypass(旁路) 组件,提供 高级渐进式交付功能。支持金丝雀、蓝绿和A/B测试交付模式,同时它兼容 Gateway API 和各种Ingress 实现,以及发布过程能够基于 Prometheus Metrics 指标自动化分批与暂停,并提供旁路的无感对接、兼容已有的多种工作负载(Deployment、CloneSet、DaemonSet)。
主要特点
-
丰富的发布策略
- 用于 Deployment、CloneSet、StatefulSet、Advanced StatefulSet、Advanced DaemonSet 的多批次更新策略。
- 用于 Deployment 的金丝雀(Canary)更新策略。
-
丰富的流量路由管理策略
- 在更新工作负载时进行流量细粒度、加权流量转移。
- 流量A/B测试,基于HTTP头和Cookie进行流量转移。
- 端到端流量灰度
-
丰富的流量协议支持
- Ingress 控制器集成:NGINX、ALB、Higress。
- 通过 GatewayAPI 进行服务网格集成。
- 可插拔的Lua脚本,以便轻松扩展到其他 Kubernetes 流量协议(甚至CRD)。
和其他组件对比
Kruise Rollouts 与 Argo Rollout 和 Flux Flagger 的对比。
全链路灰度
在发布应用的过程中,我们通常希望用少量特定流量来验证新版本的发布是否正常,以保障整体稳定性。这个过程被称为灰度发布。在微服务架构场景下,传统的灰度发布模式往往不能满足交付的复杂需求,全链路灰度发布的场景也就应运而生,此时每个微服务都会有灰度环境或分组来接受灰度流量。
一个微服务系统中的多个应用可以共用一个流量网关,上游应用的灰度副本会把流量传递到下游应用的灰度副本中, 从而保证一个请求的处理尽可能的保持在一个端到端灰度环境中。 这种灰度环境往往被叫做流量泳道, 在这样的泳道中,如果某个应用不存在灰度实例, 请求会引流到稳定版本的应用实例中。 但当下游应用又存在灰度实例的时候,发往下游的请求又会被导流到灰度实例上。 全链路灰度往往被用来在需要多个应用协同的场景下进行业务验证和灰度。示例图如下:
Istio全链路灰度
Istio 也可以实现全链路灰度。首先,需要创建灰度的 Deployment 并打上灰度的节点标识。其次,还需要配置 Istio 的流量路由 CRD,包括每一跳请求的 VirtualService
和 DestinationRule
规则,并且这些流量规则还需要与请求标识相配合。
假设有2个服务A和B,流量路径如下:
流量-->Gateway-->VirtualService-A-->DestinationRule-A-->Service-A-->APP-A-->VirtualService-B-->DestinationRule-B-->Service-B-->APP-B
灰度时,在应用A和B上添加比如x-mse-tag=gray
的 header,和version=canary
的 label。然后包含指定 header 的流量会走灰度环境。这里可以利用 EnvoyFilter
在入口网关进行染色。对包含 system=ios
的流量染色,为其添加 x-mse-tag=gray
,而其它流量则添加 x-mse-tag=base
。
综上所述,可以看到通过 Istio 来实现全链路灰度操作非常复杂且成本较高。如果在配置过程中出现错误,可能会导致生产流量出现问题,对业务造成重大影响。
header透传
在对微服务进行全链路灰度的过程中,一个最需要考虑的问题是流量中 header 透传的问题,一些微服务会仅保留特定的 header 进行透传,而去除其它 header。
分布式链路追踪技术对大型分布式系统中请求调用链路进行详细记录,核心思想就是通过一个全局唯一的 traceid 和每一条的 spanid 来记录请求链路所经过的节点以及请求耗时,其中 traceid 是需要整个链路传递的。当然我们也可以传递一些自定义信息,比如灰度标识。
在OpenTelemetry、Skywalking、Jaeger 等分布式链路追踪产品都支持链路传递用户自定义的数据。通过 Tracing Baggage 机制在全链路中传递对应染色标识。首先添加x-mse-tag=gray
的 key 到 Baggage 中,然后在 outbound 出口处读取 Baggage 中的 key 并注入到 header 中,继续流量路由。
实例
下面来实际操作一下 Kruise Rollout 和 Higress 的全链路灰度。
安装Kruise Rollout
安装文档:https://openkruise.io/zh/rollouts/installation
这里使用helm安装。
helm repo add openkruise https://openkruise.github.io/charts/
helm repo update
helm install kruise-rollout openkruise/kruise-rollout --version 0.5.0 --set image.repository=openkruise-registry.cn-shanghai.cr.aliyuncs.com/openkruise/kruise-rollout
安装kubectl-kruise
kubectl krew install kruise
# 或
wget https://github.com/openkruise/kruise-tools/releases/download/v1.1.3/kubectl-kruise-linux-amd64.tar.gz
tar xvf kubectl-kruise-linux-amd64.tar.gz
mv linux-amd64/kubectl-kruise /usr/local/bin/
rm -rf linux-amd64/
部署demo
demo的流量如图所示:红色代表灰度流量。
首先部署应用。
# 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
然后创建ingress。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: spring-cloud-a
spec:
ingressClassName: higress
rules:
- http:
paths:
- backend:
service:
name: spring-cloud-a
port:
number: 20001
path: /
pathType: Prefix
host: springa.com
登录nacos可以看到服务列表。用户名密码都是nacos。
测试访问。这里用higress-gateway的nodePort访问。有LoadBalancer的用EXTERNAL_IP。
curl http://ip:31779/a -H 'host: springa.com'
定义灰度规则
定义 Kruise Rollout 的灰度发布规则。
# 用于圈定全链路的应用
# a rollout configuration
apiVersion: rollouts.kruise.io/v1alpha1
kind: Rollout
metadata:
# 其它微服务应用类似配置,例如:rollout-b, rollout-c
name: rollout-a
annotations:
rollouts.kruise.io/trafficrouting: higress-traffic
spec:
objectRef:
workloadRef:
apiVersion: apps/v1
kind: Deployment
name: spring-cloud-a
strategy:
canary:
steps:
- pause: {}
replicas: 1
# 针对灰度发布中的应用,Kruise 会自动给他标识 gray 版本
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: higress-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: higress-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
---
# 全链路泳道配置。在入口服务即spring-cloud-a上配置灰度流量规则,标记灰度流量和线上流量。
apiVersion: rollouts.kruise.io/v1alpha1
kind: TrafficRouting
metadata:
name: higress-traffic
spec:
objectRef:
- service: spring-cloud-a
ingress:
classType: higress
name: spring-cloud-a
strategy:
matches:
# A/B Testing发布,基于header的流量灰度方式。
- headers:
- type: Exact
name: User-Agent
value: xiaoming
# 此批次灰度的流量比例50%。
#weight: 30 , 灰度30%流量
requestHeaderModifier:
set:
- name: x-mse-tag
value: gray
上面定义的灰度发布规则发布分为三批:
- A/B Testing发布,具有header[x-user-id]=100的流量将导入到新版本,其它则为老版本。
- 按照流量比例进行灰度,此批次将灰度50%的实例及流量。
- 将灰度完成所有的实例。
将该Rollout资源下发到K8s集群。
kubectl apply -f rollout.yaml
查看rollout。
STATUS=Healthy
,表明 Rollout 资源正常工作。
灰度发布
Kruise Rollouts 是一个常态化的配置,将其下发到集群后,后续业务版本发布只需调整Deployment配置,无需再对 Kruise Rollouts 进行额外操作。将spring-cloud-a
服务和spring-cloud-c
镜像版本升级到mse-2.0.1
。
k set image deploy spring-cloud-a spring-cloud-a=registry.cn-hangzhou.aliyuncs.com/mse-demo-hz/spring-cloud-a:mse-2.0.1
k set image deploy spring-cloud-c spring-cloud-c=registry.cn-hangzhou.aliyuncs.com/mse-demo-hz/spring-cloud-a:mse-2.0.1
# 或者直接修改deployment
查看应用。
可以看到 Kruise Rollout 并没有直接修改原先的 deployment,而是创建了两个灰度应用 spring-cloud-a-2h5kc
、spring-cloud-c-btk88
。自动添加了tag=gray
的label。
查看rollout。
输出STATUS=Progressing
:表明已经在灰度发布过程中。
输出CANARY_STATE=StepPaused
:表明当前批次已经完成,是否需要继续,可通过人工确认。
测试流量访问。经过多次测试,发现流量并没有打到灰度版本中,还是轮询的方式。
发布。
kubectl-kruise rollout approve rollouts/rollout-a
kubectl-kruise rollout approve rollouts/rollout-c
如何回滚
只需要把镜像修改回去即可,灰度出来的deployment会自动删除。
为什么没有实现?
查看官方文档,当前只支持MSE,而MSE云原生网关是Higress的商业托管版本。具体差别参考文档:https://help.aliyun.com/zh/mse/product-overview/comparison-between-mse-cloud-native-gateways-and-open-source-higress-gateways?spm=a2c4g.11186623.help-menu-123350.d_0_1_6_1.4f486ca2dUxNFZ
我这里没有阿里云ACK和MSE环境,没法进行测试。