流量泳道
流量泳道概述
流量泳道是一种流量管理技术,用于将流量按照不同的需求或优先级分配到不同的服务或应用程序中。具体来说,流量泳道将流量划分为不同的路径或“泳道”,每个泳道对应一个特定的服务或应用程序的版本。
服务网格CSM支持根据预设的规则和策略,流量将被动态地分配到不同的泳道中,以满足不同的业务需求和性能目标。
在CSM中,您仅需确保创建一条基线泳道,该泳道需涵盖调用链路中的所有服务。其他泳道则无需包含完整的调用链路服务。当服务在某一泳道内相互调用时,若目标服务不在当前泳道内,请求将被重定向至基线泳道中的对应服务。一旦目标服务在当前泳道内被发现,请求将再次被转发回原泳道。若您选择使用灵活的流量泳道模式,您的应用程序必须包含一个能够在整个调用链路中持续传递的请求头,即链路透传请求头,并确保每个请求都具有独特的请求头值。
前提条件
- 已创建CSM网格实例,且版本为1.14.6及以上。具体操作,请参见CSM实例管理
- 已添加CCE集群到CSM实例。
功能介绍
您的应用程序必须包含一个能够在整条调用链路中透传的请求头(链路透传请求头),且链路透传请求头的值对于每条请求都各不相同。同时,您需要指定一个引流的请求头,CSM网关将会根据引流请求头的内容将流量发往不同的流量泳道,保障链路完整性,简化流量管理。
- 链路透传请求头:在服务链路中沿着请求路径传递的HTTP头部信息,这些header信息通常包括用于跟踪、日志记录或安全目的的数据,如跟踪ID(trace ID)。透传header有助于在跨多个服务的调用中维持请求上下文的连续性。
- 引流请求头:专门用于控制和管理流量分配的HTTP头部信息,特别是在灰度发布或A/B测试中。通过设置引流header,可以根据特定的标识(如版本号或用户群组)将流量引导至不同的服务版本或实例。可以在不影响所有用户的情况下测试新功能,或将特定用户群体的流量引入特定的处理路径。引流header是实现精细流量控制的关键工具。
以service-a、service-b、service-c三个服务为例,您可以创建三个泳道:s1、s2和s3,它们分别代表了服务调用链的三个不同版本。其中,s1作为基线泳道,完整地包含了这三个服务;s2则仅包含service-a和service-c两个服务;而s3仅包含service-b一个服务。通过这样的配置,CSM网关可以根据引流请求头的内容,将流量导向相应的泳道,从而实现对服务调用的灵活控制和流量的有效管理。
注:流量泳道功能既支持CSM Gateway的外部请求,也支持K8s集群内服务的内部请求
步骤一:部署示例服务
- 为default命名空间启用Sidecar网格代理自动注入。具体操作,请参见注入配置。
- 在纳管CCE集群中执行以下命令,部署示例服务。
kubectl apply -f service-a.yaml
kubectl apply -f service-b.yaml
nkubectl apply -f service-c.yaml
service-a/b/c.yaml 文件如下:
apiVersion: v1
kind: Service
metadata:
name: service-a
spec:
selector:
app: service-a
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: service-a-v1
spec:
replicas: 1
selector:
matchLabels:
app: service-a
version: v1
template:
metadata:
labels:
app: service-a
version: v1
spec:
containers:
- name: service-a
image: registry.baidubce.com/csm-offline/service-a:dev
imagePullPolicy: Always
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
ports:
- containerPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: service-a-v2
spec:
replicas: 1
selector:
matchLabels:
app: service-a
version: v2
template:
metadata:
labels:
app: service-a
version: v2
spec:
containers:
- name: service-a
image: registry.baidubce.com/csm-offline/service-a:dev
imagePullPolicy: Always
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
ports:
- containerPort: 8080
apiVersion: v1
kind: Service
metadata:
name: service-b
spec:
selector:
app: service-b
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: service-b-v1
spec:
replicas: 1
selector:
matchLabels:
app: service-b
version: v1
template:
metadata:
labels:
app: service-b
version: v1
spec:
containers:
- name: service-b
image: registry.baidubce.com/csm-offline/service-b:dev
imagePullPolicy: Always
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
ports:
- containerPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: service-b-v3
spec:
replicas: 1
selector:
matchLabels:
app: service-b
version: v3
template:
metadata:
labels:
app: service-b
version: v3
spec:
containers:
- name: service-b
image: registry.baidubce.com/csm-offline/service-b:dev
imagePullPolicy: Always
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
ports:
- containerPort: 8080
apiVersion: v1
kind: Service
metadata:
name: service-c
spec:
selector:
app: service-c
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: LoadBalancer
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: service-c-v1
spec:
replicas: 1
selector:
matchLabels:
app: service-c
version: v1
template:
metadata:
labels:
app: service-c
version: v1
spec:
containers:
- name: service-c
image: registry.baidubce.com/csm-offline/service-c:dev
imagePullPolicy: Always
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
ports:
- containerPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: service-c-v2
spec:
replicas: 1
selector:
matchLabels:
app: service-c
version: v2
template:
metadata:
labels:
app: service-c
version: v2
spec:
containers:
- name: service-c
image: registry.baidubce.com/csm-offline/service-c:dev
imagePullPolicy: Always
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
ports:
- containerPort: 8080
步骤二:创建泳道组和对应泳道
1. 创建泳道组
a. 登录百度智能云控制台,选择“产品服务 > 云原生 > 服务网格 CSM”。
b. 在服务网格控制台,点击期望操作的服务网格实例名称,点击"流量泳道"。
c. 在流量泳道页面,单击立即新建,在创建泳道组面板,配置相关信息,然后单击下一步。
配置项 | 说明 |
---|---|
泳道组名称 | 本示例配置为lane-demo。 |
请求头设定 | 链路透传请求头:由于示例应用在调用链路中透传了请求头baidu-request-id,因此本示例配置为baidu-request-id。 引流请求头:用于网关根据请求头内容向不同泳道引流及泳道上下文保持,可任意指定。本示例配置为color。 |
泳道服务 | 列表包含网格实例下所有纳管cce集群的k8s服务和ServiceEntry。在下方列表中选中servce-a、service-b和service-c服务,点击勾选☑️,添加目标服务到已选择区域。 |
在链路中未透传引流请求头,引流请求头与链路透传请求头并不相同(分别为version和baidu-request-id)。在这种情况下,需要链路透传请求头中的内容针对每次请求都不相同(即每次调用链路都有唯一的链路ID)。如果同时将链路透传请求头指定为引流请求头,则针对链路透传请求头不再需要上述的限制,只需用链路透传请求头的内容向不同泳道引流即可。
d. 创建基线泳道,在"基线泳道"页面,配置泳道名称和标签信息,然后单击确定。
注:基线泳道必须包含所有泳道组服务,因此创建基线泳道时,默认选中所有服务且不能修改。
配置项 | 说明 |
---|---|
泳道名称 | 基线泳道配置为s1。泳道名称需要与当前泳道的引流请求头 value 值保持一致。 例如:在本示例中只要请求中包含"color: s1"信息的请求,都会匹配到基线泳道s1(泳道名称)中来。 |
标签 | 标签名称:本示例选择verison。 标签值:本示例选择选择v1。 |
泳道服务 | 基线泳道(默认全选):选择service-a(default)、service-b(default)、service-c(default)。 |
成功创建完泳道组和基线泳道后,可以在"istio资源管理"中查看泳道相关的virtualService 和 DestinationRule。示例如下:
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: vs-delegate-service-a
namespace: {namespace}
spec:
hosts:
- service-a.default.svc.cluster.local
http:
- delegate:
name: vs-l-i4ew8u31-service-a
namespace: {namespace}
match:
- headers:
color:
exact: s1
- route:
- destination:
host: service-a.default.svc.cluster.local
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: vs-l-i4ew8u31-service-a
namespace: {namespace}
spec:
http:
- match:
- headers:
color:
exact: s1
route:
- destination:
host: service-a.default.svc.cluster.local
subset: s1
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: dr-l-i4ew8u31-service-a
namespace: {namespace}
spec:
host: service-a.default.svc.cluster.local
subsets:
- labels:
version: v1
name: s1
2.新建泳道
配置项 | 说明 |
---|---|
泳道名称 | 两条泳道分别配置为s2、s3。 |
标签 | 标签名称:选择verison 标签值:两条泳道分别选择v2、v3。 |
泳道服务 | s2泳道:选择service-a(default)、service-c(default)。 s3泳道:选择service-b(default)。 |
3. 创建引流规则
a. 在流量泳道页面,单击目标泳道右侧操作列下的添加引流规则。
b. 在添加引流规则弹出框,配置相关信息,然后单击确定。本文以泳道服务对应匹配请求的URL均为/test为例,为service-a服务配置引流规则。
配置项 | 说明 |
---|---|
入口服务 | 选择service-a。 |
规则名称 | 本示例为test。 |
匹配请求的URI | 配置匹配方式为精确,匹配内容为/test。 |
Header匹配规则 | 每一条引流规则都默认添加一个Header匹配规则—— "{引流请求头}:{泳道名称}" 本示例中默认添加"color:s1" |
创建成功后,示例效果如下:
c. 创建成功后,会自动生成对应的引流规则,即虚拟服务VirtualService。例如,针对泳道s1的service-a服务会生成如下的虚拟服务VirtualService。
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: vs-l-i4ew8u31-service-a
namespace: {namespace}
spec:
http:
- match:
- headers:
color:
exact: s1
name: test
uri:
exact: /test
- headers:
color:
exact: s1
route:
- destination:
host: service-a.default.svc.cluster.local
subset: s1
步骤三:验证全链路灰度功能是否生效
集群内服务访问
- 在cce集群内default命名空间下导入sleep服务,sleep服务见istio官方文档 sleep服务。
kubectl apply -f samples/sleep/sleep.yaml
- 等待sleep服务pod创建成功,验证全链路灰度功能是否生效。
a. 执行以下命令,查看s1泳道的访问效果。
for i in {1..20}; do kubectl exec -it {sleep-pod-name} -c sleep -- curl -H 'color: s1' -H'baidu-request-id: x0000'$i http://service-a/test ; echo ''; sleep 0.2; done;
预期结果如下
Handled by: service-a-v1-548cb45968-fj2rc -> service-b-v1-64c976c64c-n2m6w -> service-c-v1-8f9c58dcd-ctjhl
由预期输出得到,通过设置HTTP标头color: s1声明的流量流向s1泳道下的相关服务,符合预期。
b. 执行以下命令,查看s2泳道的访问效果。
for i in {1..20}; do kubectl exec -it {sleep-pod-name} -c sleep -- curl -H 'color: s2' -H'baidu-request-id: x0001'$i http://service-a/test ; echo ''; sleep 0.2; done;
预期结果如下
Handled by: service-a-v2-585865d66f-z76zl -> service-b-v1-64c976c64c-n2m6w -> service-c-v2-579b589cf9-v4wcp
由预期输出得到,通过设置HTTP标头color: s2声明的流量流向s2泳道下的相关服务。当流量发往泳道s2中不存在的服务service-b时,流量发往基线泳道s1中的service-b服务,后续流量发往service-c服务时,目标重新设定为s2泳道中的service-c服务,符合预期。
c. 执行以下命令,查看s3泳道的访问效果。
for i in {1..20}; do kubectl exec -it {sleep-pod-name} -c sleep -- curl -H 'color: s3' -H'baidu-request-id: x0000'$i http://service-a/test ; echo ''; sleep 0.2; done;
预期结果如下
Handled by: service-a-v1-548cb45968-fj2rc -> service-b-v3-6bdc685945-r97zt -> service-c-v1-8f9c58dcd-ctjhl
由预期输出得到,通过设置HTTP标头color: s3声明的流量流向s3泳道下的相关服务。当流量发往泳道s3中不存在的服务service-a、service-c时,流量发往基线泳道s1中的service-a、service-c服务,符合预期。
托管网关验证
前提:必须为托管网格实例,并且创建了托管网关。创建网关详情见网关管理。
- 获取托管网关的公网IP(来源于网关入口中的公网地址)。
- 设置环境变量,xxx.xxx.xxx.xxx为上一步获取的IP。
export GATEWAY_IP=xxx.xxx.xxx.xxx
- 在"istio资源管理"页面中,中,修改gateway-vs,添加域名和相应的网关引流规则。例如:配置一个s1泳道到service-a服务的网关引流规则如下:
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: gateway-vs
namespace: {namespace}
spec:
gateways:
- gateway-gw
hosts:
- service-a.com
http:
- match:
- headers:
color:
exact: s1
uri:
exact: /test
route:
- destination:
host: service-a.default.svc.cluster.local
port:
number: 80
subset: s1
- 验证全链路灰度功能是否生效。本示例只演示托管网关请求泳道s1,执行以下命令,查看s1泳道的访问效果。
for i in {1..20}; do curl -H "baidu-request-id: 11" -H "color:s1" http://{service-a服务对应域名}/test --resolve "{service-a服务对应域名}:80:${GATEWAY_IP}" ; echo ''; sleep 0.2; done;
预期结果
Handled by: service-a-v1-548cb45968-fj2rc -> service-b-v1-64c976c64c-n2m6w -> service-c-v1-8f9c58dcd-ctjhl
由预期输出得到,通过设置HTTP标头color: s1声明的流量流向s1泳道下的相关服务,符合预期。