泳道&泳道组管理
在微服务架构中,一次业务迭代可能涉及到多个微服务,业务上线前为了测试和验证 搭建一套测试环境往往是一个比较头疼的问题,测试过程中跟其他迭代之间可能有相互影响或者冲突,导致迭代效率低下。全链路灰度能力将灰度和全链路进行结合,允许对一个调用链路上的微服务统一进行灰度,通过流量标签功能,实现在整个调用链透传流量标签;然后基于该标签配置对每个微服务的治理策略,即可实现对调用链路的统一治理。
泳道及泳道组
针对全链路灰度场景,我们首先定义了泳道组和泳道的概念,其中泳道组包含一组需要进行全链路灰度的微服务;泳道是泳道组内的概念,每个泳道代表一个虚拟的流量通道,全链路灰度时期望流量只在某个泳道内流动,如下图所示:
泳道组管理
从服务网格控制台选择流量管理中心-> 流量泳道-> 创建泳道组及泳道
选择集群和命名空间,选择要添加到泳道组的服务,如下图:
配置参数说明:
配置 | 说明 |
---|---|
泳道组名称 | 唯一标识一个泳道组 |
集群 | 泳道组服务所在的集群 |
命名空间 | 泳道组所在的集群命名空间 |
服务列表 | 泳道组包含的服务 |
在创建泳道组后,后台会创建一些资源说明:
资源名称 | 类型 | 说明 |
---|---|---|
trafficlabel-${命名空间} | TrafficLabel | 用于实现流量打标,默认从工作负载的CSM_TRAFFIC_TAG标签取值,赋值给userdefinelabel标签;可以根据业务需求参考流量标签章节修改流量打标逻辑 |
tl-命名空间}-{泳道组服务名称}{]()泳道组名称}-[${ | VirtualService | 基于上面的流量标签定义实现对泳道组内服务的路由控制,默认基于流量标签和泳道名称进行匹配,匹配成功则路由到对应泳道 |
tl-{泳道组名称}-{命名空间}-${泳道组服务名称} | DestinationRule | 用于实现泳道分组定义 |
编辑泳道组
您可以在泳道组页面修改泳道包含的服务,如下图:
删除泳道组
点击泳道组tab的删除按钮即可
泳道管理
创建泳道
在当前泳道组内可以定义多个泳道,选择创建泳道,填写泳道名称并选择泳道内的服务即可;需要注意:
- 泳道名称最终用于和流量标签进行匹配
- 默认按照服务的[]()CSM_TRAFFIC_TAG标签对服务进行分组,该标签值需要和泳道名称保持一致
删除泳道
在泳道组内的泳道列表操作栏,选择删除操作即可
全链路灰度验证
全链路灰度是基于流量标签功能,在微服务整个调用链中进行统一的流量打标和治理,从而实现全链路灰度能力。本文介绍如何实现全链路灰度发布能力。
前提条件
- 开通云容器引擎,容器实例上创建demo命名空间
- 开通服务网格实例
微服务调用关系
微服务架构中有三个服务,app1、app2、app3,调用关系如下图,入口由Ingress网关做流量分发,要实现全链路灰度,预期是可以实现微服务之间只在在base版本内和test版本内调用,实现虚拟泳道。
操作步骤
- 安装ingress gateway
登录控制台,选择对应的服务网格,进入网格页面后,通过左边栏的“网关”一栏选择“入口网关”。
进入网关页面后,选择集群和命名空间后可以创建网关。
填入详细的参数后,点确认即可。如图:
创建完成后可以看到网关列表
创建完Ingress网关后,默认生成了一个Gateway资源,用于实现网关监听相关配置,如下:
- 部署base版本应用
apiVersion: apps/v1
kind: Deployment
metadata:
name: app1-base
namespace: demo
labels:
app: app1
CSM_TRAFFIC_TAG: base
spec:
replicas: 1
selector:
matchLabels:
app: app1
name: app1
CSM_TRAFFIC_TAG: base
template:
metadata:
labels:
sidecar.istio.io/inject: "true"
app: app1
name: app1
source: CCSE
CSM_TRAFFIC_TAG: base
spec:
containers:
- name: default
image: registry-vpc-crs-huadong1.ctyun.cn/library/trace-demo:v1.0.0
imagePullPolicy: IfNotPresent
env:
- name: version
value: base
- name: app
value: app1
- name: upstream_url
value: "http://app2:8000/"
ports:
- containerPort: 8000
apiVersion: apps/v1
kind: Deployment
metadata:
name: app2-base
namespace: demo
labels:
app: app2
CSM_TRAFFIC_TAG: base
spec:
replicas: 1
selector:
matchLabels:
app: app2
name: app2
CSM_TRAFFIC_TAG: base
template:
metadata:
labels:
sidecar.istio.io/inject: "true"
app: app2
name: app2
source: CCSE
CSM_TRAFFIC_TAG: base
spec:
containers:
- name: default
image: registry-vpc-crs-huadong1.ctyun.cn/library/trace-demo:v1.0.0
imagePullPolicy: IfNotPresent
env:
- name: version
value: base
- name: app
value: app2
- name: upstream_url
value: "http://app3:8000/"
ports:
- containerPort: 8000
apiVersion: apps/v1
kind: Deployment
metadata:
name: app3-base
namespace: demo
labels:
app: app3
CSM_TRAFFIC_TAG: base
spec:
replicas: 1
selector:
matchLabels:
app: app3
name: app3
CSM_TRAFFIC_TAG: base
template:
metadata:
labels:
sidecar.istio.io/inject: "true"
app: app3
name: app3
source: CCSE
CSM_TRAFFIC_TAG: base
spec:
containers:
- name: default
image: registry-vpc-crs-huadong1.ctyun.cn/library/trace-demo:v1.0.0
imagePullPolicy: IfNotPresent
env:
- name: version
value: base
- name: app
value: app3
ports:
- containerPort: 8000
- 部署test版本应用
apiVersion: apps/v1
kind: Deployment
metadata:
name: app1-test
namespace: demo
labels:
app: app1
CSM_TRAFFIC_TAG: test
spec:
replicas: 1
selector:
matchLabels:
app: app1
name: app1
CSM_TRAFFIC_TAG: test
template:
metadata:
labels:
sidecar.istio.io/inject: "true"
app: app1
name: app1
source: CCSE
CSM_TRAFFIC_TAG: test
spec:
containers:
- name: default
image: registry-vpc-crs-huadong1.ctyun.cn/library/trace-demo:v1.0.0
imagePullPolicy: IfNotPresent
env:
- name: version
value: test
- name: app
value: app1
- name: upstream_url
value: "http://app2:8000/"
ports:
- containerPort: 8000
apiVersion: apps/v1
kind: Deployment
metadata:
name: app2-test
namespace: demo
labels:
app: app2
CSM_TRAFFIC_TAG: test
spec:
replicas: 1
selector:
matchLabels:
app: app2
name: app2
CSM_TRAFFIC_TAG: test
template:
metadata:
labels:
sidecar.istio.io/inject: "true"
app: app2
name: app2
source: CCSE
CSM_TRAFFIC_TAG: test
spec:
containers:
- name: default
image: registry-vpc-crs-huadong1.ctyun.cn/library/trace-demo:v1.0.0
imagePullPolicy: IfNotPresent
env:
- name: version
value: test
- name: app
value: app2
- name: upstream_url
value: "http://app3:8000/"
ports:
- containerPort: 8000
apiVersion: apps/v1
kind: Deployment
metadata:
name: app3-test
namespace: demo
labels:
app: app3
CSM_TRAFFIC_TAG: test
spec:
replicas: 1
selector:
matchLabels:
app: app3
name: app3
CSM_TRAFFIC_TAG: test
template:
metadata:
labels:
sidecar.istio.io/inject: "true"
app: app3
name: app3
source: CCSE
CSM_TRAFFIC_TAG: test
spec:
containers:
- name: default
image: registry-vpc-crs-huadong1.ctyun.cn/library/trace-demo:v1.0.0
imagePullPolicy: IfNotPresent
env:
- name: version
value: test
- name: app
value: app3
ports:
- containerPort: 8000
- 配置Service资源
apiVersion: v1
kind: Service
metadata:
name: app1
labels:
app: app1
service: app1
withServiceMesh: true
spec:
ports:
- port: 8000
name: http
selector:
app: app1
apiVersion: v1
kind: Service
metadata:
name: app2
labels:
app: app2
service: app2
withServiceMesh: true
spec:
ports:
- port: 8000
name: http
selector:
app: app2
apiVersion: v1
kind: Service
metadata:
name: app3
labels:
app: app3
service: app3
withServiceMesh: true
spec:
ports:
- port: 8000
name: http
selector:
app: app3
- 配置虚拟服务资源
我们还需要部署和微服务入口应用关联的虚拟服务配置,实现从Ingress网关到微服务的转发,虚拟服务配置如下:
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: vs-gateway-app1
spec:
gateways:
- default-ingressgateway-forrestergw
hosts:
- "*"
http:
- route:
- destination:
host: app1.demo.svc.cluster.local
进入天翼云弹性负载均衡控制台,找到Ingress网关绑定的ELB实例,可以看到对应的访问地址,确定网关的访问入口地址:
设置为GATEWAY_HOST环境变量为公网访问入口,执行如下语句访问:
for i in {1..100}; do curl http://${GATEWAY_HOST}/; echo; sleep 1; done
可以看到访问在两个版本之间跳变,微服务之间的调用也是在版本间穿插进行,此时的全链路没有统一的路由策略。
- 配置泳道****组和流量泳道
进入服务网格控制台 -> 流量管理中心 -> 全链路灰度菜单 -> 创建泳道组,其中包含三个应用。
进一步在泳道组内创建base和test两个泳道,每个泳道都包含三个应用。
创建完成后,可以看到网格内已经自动创建了目标规则和虚拟服务规则,实现按版本路由策略。
- 配置流量标签
为了实现全链路统一的灰度控制能力,我们还需要在Ingress网关和微服务sidecar上配置流量打标以及流量标签透传策略,这里设置基于前端访问的app-version-tag请求头部给流量打上标签,通过trace-ctx-id字段在调用链上下游传递流量标签。
Ingrss网关的流量标签策略如下(workloadSelector需要选中对应的Ingress网关):
apiVersion: networking.istio.io/v1beta1
kind: TrafficLabel
metadata:
name: gw-traffic-label
namespace: demo
spec:
workloadSelector:
labels:
gateway-unique-name: "demo.ingressgateway.forrestergw"
rule:
labels:
- name: userdefinelabel
valueFrom:
- $getInboundRequestHeader(app-version-tag)---
三个应用的sidecar对应的流量标签规则如下(workloadSelector需要选中工作负载的sidecar):
apiVersion: networking.istio.io/v1beta1
kind: TrafficLabel
metadata:
name: traffic-label-app1
namespace: demo
spec:
workloadSelector:
labels:
app: app1
rule:
labels:
- name: userdefinelabel
valueFrom:
- $getInboundRequestHeaderWithContext(app-version-tag, trace-ctx-id)
apiVersion: networking.istio.io/v1beta1
kind: TrafficLabel
metadata:
name: traffic-label-app2
namespace: demo
spec:
workloadSelector:
labels:
app: app2
rule:
labels:
- name: userdefinelabel
valueFrom:
- $getInboundRequestHeaderWithContext(app-version-tag, trace-ctx-id)
apiVersion: networking.istio.io/v1beta1
kind: TrafficLabel
metadata:
name: traffic-label-app3
namespace: demo
spec:
workloadSelector:
labels:
app: app3
rule:
labels:
- name: userdefinelabel
valueFrom:
- $getInboundRequestHeaderWithContext(app-version-tag, trace-ctx-id)
请求带上app-version-tag: base,同时指定请求级别的trace-ctx-id:
for i in {1..200}; do curl -H "app-version-tag: base" -H "trace-ctx-id: trace-$i" http://${GATEWAY_HOST}; echo; sleep 1; done
可以看到请求只在base版本之间调用
修改头部app-version-tag: test,可以看到请求只在test版本之间调用: