当您需要在多个服务间实现全链路的灰度发布时,可以通过配置TrafficLabel来识别流量特征,将网关入口流量分为正常流量和灰度流量。灰度流量特征会在请求调用链经过的各个服务间进行传递,从而实现全链路灰度发布。本文介绍如何通过TrafficLabel能力来实现微服务的全链路灰度发布。
前提条件
- 已创建ASM企业版或旗舰版实例,且版本为v1.17.2.22或以上。具体操作,请参见创建ASM实例。
- 已创建ACK集群, 并已添加集群到ASM实例。具体操作,请参见添加集群到ASM实例。
- 已创建ASM网关。具体操作,请参见创建入口网关服务。
背景信息
灰度发布有多种实现方式,例如基于ASM完成蓝绿和灰度发布。该灰度方式偏重于单个服务的发布,通过使用Istio原生提供的VirtualService标签路由和权重分流进行实现。
某些场景下,仅限于两个服务间的灰度不能满足需求,例如多个服务同时发布了灰度版本。这种情况下对业务进行功能灰度验证:入口流量分为正常流量和灰度流量。通常情况下, 可以针对请求流量做流量特征识别,若是灰度流量则需要请求灰度的应用服务。该场景不再是简单的按流量比例灰度分发到后端不同的版本,而且灰度流量特征会在请求调用链经过的各个服务间进行传递。
ASM全链路灰度功能基于流量打标和标签路由功能。更多信息,请参见流量打标和标签路由。
示例介绍
您可以单击文件,直接下载示例部署及相关配置文件。
示例中应用服务调用链路如下:
本示例服务中的应用A在调用B时, 已经包括了传播HTTP标头my-request-id的代码实现逻辑, 具体可以参见代码文件src/mock-abc/go/main.go。
**注意: 如果使用其他的应用示例, 务必确保在调用中包括传播HTTP标头的逻辑, 后续的功能依赖于这个传播的HTTP标头。
应用Base版本对应的部署文件application-base.yaml如下:
apiVersion: v1
kind: Service
metadata:
name: mocka
labels:
app: mocka
service: mocka
spec:
ports:
- port: 8000
name: http
selector:
app: mocka
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mocka-base
labels:
app: mocka
version: base
spec:
replicas: 1
selector:
matchLabels:
app: mocka
version: base
template:
metadata:
labels:
app: mocka
version: base
spec:
containers:
- name: default
image: registry.cn-beijing.aliyuncs.com/aliacs-app-catalog/go-http-sample:1.0
imagePullPolicy: Always
env:
- name: version
value: base
- name: app
value: mocka
- name: upstream_url
value: "http://mockb:8000/"
ports:
- containerPort: 8000
---
apiVersion: v1
kind: Service
metadata:
name: mockb
labels:
app: mockb
service: mockb
spec:
ports:
- port: 8000
name: http
selector:
app: mockb
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mockb-base
labels:
app: mockb
version: base
spec:
replicas: 1
selector:
matchLabels:
app: mockb
version: base
template:
metadata:
labels:
app: mockb
version: base
spec:
containers:
- name: default
image: registry.cn-beijing.aliyuncs.com/aliacs-app-catalog/go-http-sample:1.0
imagePullPolicy: Always
env:
- name: version
value: base
- name: app
value: mockb
ports:
- containerPort: 8000
应用Canary版本对应的部署文件application-canary.yaml如下:
apiVersion: v1
kind: Service
metadata:
name: mocka
labels:
app: mocka
service: mocka
spec:
ports:
- port: 8000
name: http
selector:
app: mocka
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mocka-canary
labels:
app: mocka
version: canary
spec:
replicas: 1
selector:
matchLabels:
app: mocka
version: canary
template:
metadata:
labels:
app: mocka
version: canary
spec:
containers:
- name: default
image: registry.cn-beijing.aliyuncs.com/aliacs-app-catalog/go-http-sample:1.0
imagePullPolicy: Always
env:
- name: version
value: canary
- name: app
value: mocka
- name: upstream_url
value: "http://mockb:8000/"
ports:
- containerPort: 8000
---
apiVersion: v1
kind: Service
metadata:
name: mockb
labels:
app: mockb
service: mockb
spec:
ports:
- port: 8000
name: http
selector:
app: mockb
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mockb-canary
labels:
app: mockb
version: canary
spec:
replicas: 1
selector:
matchLabels:
app: mockb
version: canary
template:
metadata:
labels:
app: mockb
version: canary
spec:
containers:
- name: default
image: registry.cn-beijing.aliyuncs.com/aliacs-app-catalog/go-http-sample:1.0
imagePullPolicy: Always
env:
- name: version
value: canary
- name: app
value: mockb
ports:
- containerPort: 8000
步骤一:在ACK集群下部署示例应用
在指定的命名空间下创建部署示例应用, 例如指定命名空间default。 确认该命名空间已经开启自动注入, 具体可以参见https://help.aliyun.com/document_detail/150501.html?spm=a2c4g.377563.0.0.54fb6de6GSrl08#section-30o-vil-3n7
使用ACK集群的kubeconfig执行以下命令,部署示例应用:
kubectl apply -n default -f application-base.yaml
kubectl apply -n default -f application-canary.yaml
步骤二:初始化路由规则配置
使用以下内容,创建名为routing.yaml的文件。
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: simple-gateway
spec:
selector:
istio: ingressgateway
servers:
- hosts:
- "*"
port:
name: http
number: 80
protocol: HTTP
---
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: dr-mocka
spec:
host: mocka
trafficPolicy:
loadBalancer:
simple: ROUND_ROBIN
subsets:
- labels:
version: base
name: version-base
- labels:
version: canary
name: version-canary
---
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: dr-mockb
spec:
host: mockb
trafficPolicy:
loadBalancer:
simple: ROUND_ROBIN
subsets:
- labels:
version: base
name: version-base
- labels:
version: canary
name: version-canary
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: vs-gateway-mocka
spec:
gateways:
- simple-gateway
hosts:
- "*"
http:
- route:
- destination:
host: mocka
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: vs-mockb
spec:
hosts:
- mockb
http:
- route:
- destination:
host: mockb
使用ASM实例的kubeconfig执行以下命令,配置路由。
kubectl apply -n default -f routing.yaml
验证服务访问是否可以连通。
- 通过ASM控制台获取网关的公网IP,执行以下命令。
执行以下命令,验证服务访问是否可以连通。export ASM_GATEWAY_IP=xxx.xxx.xxx.xxx
预期输出:for i in { 1..200}; do curl -H'my-asm-prefer-tag: version-base' -H'my-request-id: x000'{ mathJaxContainer[0]}{ ASM_GATEWAY_IP}/; echo; sleep 1; done;
可以看到, 从ASM入口网关到应用服务A, 以及从应用A调用B的过程, 负载均衡是随机的路由策略。 通过curl命令指定的my-asm-prefer-tag或者其他请求头都不会改变随机路由的策略。-> mocka(version: canary, ip: 172.17.0.129)-> mockb(version: base, ip: 172.17.0.70) -> mocka(version: base, ip: 172.17.0.132)-> mockb(version: base, ip: 172.17.0.70) -> mocka(version: base, ip: 172.17.0.132)-> mockb(version: canary, ip: 172.17.0.54) -> mocka(version: canary, ip: 172.17.0.129)-> mockb(version: canary, ip: 172.17.0.54) -> mocka(version: canary, ip: 172.17.0.129)-> mockb(version: base, ip: 172.17.0.70) -> mocka(version: base, ip: 172.17.0.132)-> mockb(version: base, ip: 172.17.0.70) -> mocka(version: canary, ip: 172.17.0.129)-> mockb(version: canary, ip: 172.17.0.54)
步骤三:为示例应用设置流量标签
ASM支持通过TrafficLabel CRD设置流量标签,然后根据流量标签将流量路由到不同的工作负载, 具体定义参考:https://help.aliyun.com/document_detail/375313.html
1)使用以下内容,创建文件trafficlabel.yaml。
注意:
- 本示例中用于区分版本的请求头为my-asm-prefer-tag, 因此getExternalInboundRequestHeader中的第一个参数也相应地设置为my-asm-prefer-tag。实际使用中, 用户可以自行设置为其他值。
- 本示例中传播的HTTP标头为my-request-id,因此getExternalInboundRequestHeader中的第二个参数也相应地设置为my-request-id。
apiVersion: istio.alibabacloud.com/v1
kind: TrafficLabel
metadata:
name: trafficlabel-ns
namespace: default
spec:
rules:
- labels:
- name: asm-labels-sample
valueFrom:
- $getExternalInboundRequestHeader(my-asm-prefer-tag, my-request-id)
---
apiVersion: istio.alibabacloud.com/v1
kind: TrafficLabel
metadata:
name: ingressgateway
namespace: istio-system
spec:
rules:
- labels:
- name: asm-labels-sample
valueFrom:
- $getInboundRequestHeader(my-asm-prefer-tag)
workloadSelector:
labels:
istio: ingressgateway
2)执行以下命令,使用ASM实例的kubeconfig进行部署。
kubectl apply -f trafficlabel.yaml
上述定义的TrafficLabel trafficlabel-ns针对命名空间default下所有的应用服务生效,也就是示例中部署的mocka和mockb。同时定义的TrafficLabel ingressgateway则是对网关ingressgateway生效。
步骤四:定义标签路由规则
1. 验证服务提供侧的灰度
首先验证应用A→B的调用是否符合预期,其中包括流向A的灰度流量打到灰度版本,以及Base流量打到Base版本。
配置应用B的基于流量标签的路由规则,生效的效果对应如下:
1)使用以下内容,创建文件vs-tf-mockb.yaml。
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: vs-mockb
spec:
hosts:
- mockb
http:
- route:
- destination:
host: mockb
subset: $asm-labels-sample
2) 执行以下命令,在A服务侧生效。
kubectl apply -n default -f vs-tf-mockb.yaml
3) 执行以下命令,验证流向A的灰度流量打到灰度版本。
for i in {
1..200}; do curl -H'my-asm-prefer-tag: version-canary' -H'my-request-id: x000'{
mathJaxContainer[1]}{
ASM_GATEWAY_IP}/; echo; sleep 1; done;
预期输出:
-> mocka(version: canary, ip: 172.17.0.129)-> mockb(version: canary, ip: 172.17.0.54)
-> mocka(version: base, ip: 172.17.0.132)-> mockb(version: canary, ip: 172.17.0.54)
-> mocka(version: base, ip: 172.17.0.132)-> mockb(version: canary, ip: 172.17.0.54)
-> mocka(version: canary, ip: 172.17.0.129)-> mockb(version: canary, ip: 172.17.0.54)
-> mocka(version: canary, ip: 172.17.0.129)-> mockb(version: canary, ip: 172.17.0.54)
-> mocka(version: base, ip: 172.17.0.132)-> mockb(version: canary, ip: 172.17.0.54)
4) 执行以下命令,验证流向A的Base流量打到Base版本。
for i in {
1..200}; do curl -H'my-asm-prefer-tag: version-base' -H'my-request-id: x000'{
mathJaxContainer[2]}{
ASM_GATEWAY_IP}/; echo; sleep 1; done;
预期输出:
-> mocka(version: canary, ip: 172.17.0.129)-> mockb(version: base, ip: 172.17.0.70)
-> mocka(version: base, ip: 172.17.0.132)-> mockb(version: base, ip: 172.17.0.70)
-> mocka(version: base, ip: 172.17.0.132)-> mockb(version: base, ip: 172.17.0.70)
-> mocka(version: canary, ip: 172.17.0.129)-> mockb(version: base, ip: 172.17.0.70)
-> mocka(version: base, ip: 172.17.0.132)-> mockb(version: base, ip: 172.17.0.70)
-> mocka(version: canary, ip: 172.17.0.129)-> mockb(version: base, ip: 172.17.0.70)
-> mocka(version: canary, ip: 172.17.0.129)-> mockb(version: base, ip: 172.17.0.70)
-> mocka(version: base, ip: 172.17.0.132)-> mockb(version: base, ip: 172.17.0.70)
说明 此时, 入口流量访问A服务并未流转到指定版本,需要再配置A服务的基于流量标签的路由规则, 下一步将进行配置。
- 验证全链路灰度
配置A服务的TrafficLabel路由vs-tf-mocka.yaml,在ASM网关侧生效。预期结果为入口请求灰度流量打到A的灰度版本,Base流量打到A的Base版本,并传递到B服务。
配置应用A的基于流量标签的路由规则之后,生效的效果对应如下:
1) 使用以下示例,创建文件vs-tf-mocka.yaml。
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: vs-gateway-mocka
spec:
gateways:
- simple-gateway
hosts:
- "*"
http:
- route:
- destination:
host: mocka
subset: $asm-labels-sample
2) 执行以下命令,在ASM网关侧生效。
kubectl apply -n default -f vs-tf-mocka.yaml
3) 执行以下命令,验证入口请求灰度流量打到A的灰度版本。
for i in {
1..200}; do curl -H'my-asm-prefer-tag: version-canary' -H'my-request-id: x000'{
mathJaxContainer[3]}{
ASM_GATEWAY_IP}/; echo; sleep 1; done;
预期输出:
-> mocka(version: canary, ip: 172.17.0.69)-> mockb(version: canary, ip: 172.17.0.130)
-> mocka(version: canary, ip: 172.17.0.69)-> mockb(version: canary, ip: 172.17.0.130)
-> mocka(version: canary, ip: 172.17.0.69)-> mockb(version: canary, ip: 172.17.0.130)
-> mocka(version: canary, ip: 172.17.0.69)-> mockb(version: canary, ip: 172.17.0.130)
-> mocka(version: canary, ip: 172.17.0.69)-> mockb(version: canary, ip: 172.17.0.130)
-> mocka(version: canary, ip: 172.17.0.69)-> mockb(version: canary, ip: 172.17.0.130)
-> mocka(version: canary, ip: 172.17.0.69)-> mockb(version: canary, ip: 172.17.0.130)
4) 执行以下命令,验证Base流量打到A的Base版本,并传递到B服务。
for i in {
1..200}; do curl -H'my-asm-prefer-tag: version-base' -H'my-request-id: x000'{
mathJaxContainer[4]}{
ASM_GATEWAY_IP}/; echo; sleep 1; done;
预期输出:
-> mocka(version: base, ip: 172.17.0.90)-> mockb(version: base, ip: 172.17.0.93)
-> mocka(version: base, ip: 172.17.0.90)-> mockb(version: base, ip: 172.17.0.93)
-> mocka(version: base, ip: 172.17.0.90)-> mockb(version: base, ip: 172.17.0.93)
-> mocka(version: base, ip: 172.17.0.90)-> mockb(version: base, ip: 172.17.0.93)
-> mocka(version: base, ip: 172.17.0.90)-> mockb(version: base, ip: 172.17.0.93)
-> mocka(version: base, ip: 172.17.0.90)-> mockb(version: base, ip: 172.17.0.93)
-> mocka(version: base, ip: 172.17.0.90)-> mockb(version: base, ip: 172.17.0.93)
3. 验证路由权重
当在应用A上配置了基于权重与基于流量标签的路由规则之后,生效的效果对应如下:
1) 使用以下示例,创建文件vs-tf-mocka-90-10.yaml。
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: vs-gateway-mocka
spec:
gateways:
- simple-gateway
hosts:
- "*"
http:
- route:
- destination:
host: mocka
subset: version-base
weight: 90
- destination:
host: mocka
subset: $asm-labels-sample
weight: 10
2) 执行以下命令,在ASM网关侧生效。
kubectl apply -n default -f vs-tf-mocka-90-10.yaml
3) 执行以下命令,验证入口请求灰度流量打到应用A的版本。
for i in {
1..200}; do curl -H'my-asm-prefer-tag: version-canary' -H'my-request-id: x000'{
mathJaxContainer[5]}{
ASM_GATEWAY_IP}/; echo; sleep 1; done;
可以通过执行结果看到, 入口请求灰度流量大约90%左右流向了应用A的Base版本, 10%左右则是流向到了Canary版本。这样因为上述生效的路由规则中, 90%的请求进入应用A的Base版本, 剩下的10%请求按照指定的请求头my-asm-prefer-tag的值来决定去向, 而下面的请求中指定version-canary为请求头my-asm-prefer-tag的值, 即这10%的请求将转向Canary版本。
预期输出:
-> mocka(version: base, ip: 172.17.0.90)-> mockb(version: canary, ip: 172.17.0.130)
-> mocka(version: base, ip: 172.17.0.90)-> mockb(version: canary, ip: 172.17.0.130)
-> mocka(version: base, ip: 172.17.0.90)-> mockb(version: canary, ip: 172.17.0.130)
-> mocka(version: base, ip: 172.17.0.90)-> mockb(version: canary, ip: 172.17.0.130)
-> mocka(version: base, ip: 172.17.0.90)-> mockb(version: canary, ip: 172.17.0.130)
-> mocka(version: base, ip: 172.17.0.90)-> mockb(version: canary, ip: 172.17.0.130)
-> mocka(version: base, ip: 172.17.0.90)-> mockb(version: canary, ip: 172.17.0.130)
-> mocka(version: base, ip: 172.17.0.90)-> mockb(version: canary, ip: 172.17.0.130)
-> mocka(version: base, ip: 172.17.0.90)-> mockb(version: canary, ip: 172.17.0.130)
-> mocka(version: canary, ip: 172.17.0.69)-> mockb(version: canary, ip: 172.17.0.130)
4) 执行以下命令,验证入口请求的Base版本流量将全部转到应用A的Base版本。这样因为上述生效的路由规则中, 90%的请求进入Base版本, 剩下的10%请求按照指定的请求头my-asm-prefer-tag的值来决定去向, 而下面的请求中指定version-base为请求头my-asm-prefer-tag的值, 即这10%的请求也将转向Base版本。
for i in {
1..200}; do curl -H'my-asm-prefer-tag: version-base' -H'my-request-id: x000'{
mathJaxContainer[6]}{
ASM_GATEWAY_IP}/; echo; sleep 1; done;
预期输出:
-> mocka(version: base, ip: 172.17.0.90)-> mockb(version: base, ip: 172.17.0.93)
-> mocka(version: base, ip: 172.17.0.90)-> mockb(version: base, ip: 172.17.0.93)
-> mocka(version: base, ip: 172.17.0.90)-> mockb(version: base, ip: 172.17.0.93)
-> mocka(version: base, ip: 172.17.0.90)-> mockb(version: base, ip: 172.17.0.93)
-> mocka(version: base, ip: 172.17.0.90)-> mockb(version: base, ip: 172.17.0.93)
-> mocka(version: base, ip: 172.17.0.90)-> mockb(version: base, ip: 172.17.0.93)
-> mocka(version: base, ip: 172.17.0.90)-> mockb(version: base, ip: 172.17.0.93)
-> mocka(version: base, ip: 172.17.0.90)-> mockb(version: base, ip: 172.17.0.93)
-> mocka(version: base, ip: 172.17.0.90)-> mockb(version: base, ip: 172.17.0.93)
-> mocka(version: base, ip: 172.17.0.90)-> mockb(version: base, ip: 172.17.0.93)