服务网格 ASM(Service Mesh)支持将应用的多个版本或者特征隔离成一个独立的运行环境(即泳道),然后通过设置泳道规则,将满足规则的请求流量路由到目标版本或特征的应用上。在生产环境中,开发者可能会希望使用泳道对稳定版本和灰度版本进行隔离,并根据用户身份路由至不同的泳道中。具体来说,您可能希望指定一部分特定身份的用户路由至灰度版本进行测试,其余用户则通过权重的方式随机匹配一定数量的请求路由至灰度版本。本文将介绍如何结合泳道和哈希打标来实现按用户身份的灰度测试。
前提条件
- 已创建并添加集群到ASM实例,实例版本为1.18及以上。具体操作,请参见添加集群到ASM实例。 
- 已部署入口网关。具体操作,请参见创建入口网关。 
操作步骤
本示例场景将创建三个应用,调用链如下图。
- mocka,包含v1版本。 
- mockb,包含v1版本。 
- mockc,包含v1版本和v2版本。 
其中应用通过x-user-id请求头来标识用户身份,同时应用之间也会透传此请求头。本场景将演示以下内容:
- 当 - x-user-id: jason时,请求流向新版本应用。
- 对于其余用户,根据 - x-user-id哈希,并按照哈希结果将指定比例的用户路由到新版本。
步骤一:部署示例应用
- 使用以下内容创建sample.yaml。 
- 使用数据面集群的kubeconfig,执行以下命令,部署示例应用。 - kubectl apply -f sample.yaml
步骤二:创建网关规则
使用以下内容创建ingressgateway且命名空间为istio-system的网关规则。具体操作,请参见管理网关规则。
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: ingressgateway
  namespace: istio-system
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 80
        name: http
        protocol: HTTP
      hosts:
        - '*'步骤三:创建泳道组和泳道
- 创建泳道组。 - 登录ASM控制台,在左侧导航栏,选择。 
- 在网格管理页面,单击目标实例名称,然后在左侧导航栏,选择。 
- 在流量泳道页面,单击创建泳道组,在创建泳道组面板,配置相关信息,然后单击确定。 - 配置项 - 说明 - 泳道组名称 - 本示例配置为canary。 - 入口网关 - 选择ingressgateway。 - 泳道模式 - 选择宽松模式。 - 调用链路上下文透传方式 - 选择透传trace id。 - trace id请求头 - 本示例配置为x-user-id。 - 引流请求头 - 用于网关根据请求头内容向不同泳道引流及泳道上下文保持,可任意指定。本示例配置为x-asm-prefer-tag。 - 泳道服务 - 选择目标Kubernetes集群和default命名空间,在下方列表中选中mocka、mockb和mockc服务,单击  图标,添加目标服务到已选择区域。 图标,添加目标服务到已选择区域。
 
- 创建s1和s2泳道,并分别绑定v1和v2版本。 - 在流量泳道页面的流量规则定义区域,单击创建泳道。 
- 在创建泳道对话框,配置相关信息,然后单击确定。 - 配置项 - 说明 - 泳道名称 - 分别配置为s1和s2。 - 配置服务标签 - 标签名称:选择ASM_TRAFFIC_TAG - 标签值:两条泳道分别选择v1、v2。 - 添加服务 - s1泳道:选择mocka(default)、mockb(default)、mockc(default)。 - s2泳道:选择mockc(default)。 - 创建s1泳道的示例图如下:  - 两条泳道创建完成后,示例效果如下:  说明 说明- 默认情况下,您在泳道组中创建的第一条泳道将被设定为基线泳道。您可以修改基线泳道,当流量发往其他泳道中不存在的服务时,通过回退机制将请求转发至基线泳道。具体操作,请参见在宽松模式中修改基线泳道。 
 
- 创建泳道对应的引流规则。 - 使用以下内容,为泳道创建网关引流规则,该引流规则可以分为三部分: - 包含 - x-user-id: jason的请求流向s2泳道,并为请求加入- x-asm-prefer-tag: s2请求头用于标识该请求流向s2泳道。
- 包含 - x-asm-prefer-tag: s2的请求流向s2泳道。
- 包含 - x-asm-prefer-tag: s1的请求流向s1泳道。
 
 
步骤四:部署哈希打标插件
- 使用以下内容,创建wasm.yaml。 - apiVersion: extensions.istio.io/v1alpha1 kind: WasmPlugin metadata: name: hash-tagging namespace: istio-system spec: imagePullPolicy: IfNotPresent selector: matchLabels: istio: ingressgateway url: registry-cn-hangzhou.ack.aliyuncs.com/acs/asm-wasm-hash-tagging:v1.22.6.2-g72656ba-aliyun phase: AUTHN pluginConfig: rules: - header: x-user-id modulo: 100 tagHeader: x-asm-prefer-tag policies: # 20% 的用户的请求流量会路由至泳道 s2 - range: 20 tagValue: s2 # 80% 的用户的请求流量会路由至泳道 s1 - range: 100 tagValue: s1
- 使用ASM实例的kubeconfig,执行以下命令,部署打标插件。 - kubectl apply -f wasm.yaml
步骤五:验证
- 执行以下命令,配置入口网关地址的临时环境变量。 - export GATEWAY_ADDRESS=`kubectl get svc -n istio-system | grep istio-ingressgateway | awk '{print $4}'`
- 执行以下命令,以Jason身份访问应用。 - curl ${GATEWAY_ADDRESS} -H 'x-user-id: jason'- 预期输出: - -> mocka(version: v1, ip: 10.0.0.15)-> mockb(version: v1, ip: 10.0.0.130)-> mockc(version: v2, ip: 10.0.0.133)%- 可以看到,请求直接路由到mockc应用的v2版本。 
- 执行以下命令,指定随机用户路由到新版本。 - for i in 'bob' 'stacy' 'jessie' 'vance' 'jack'; do curl ${GATEWAY_ADDRESS} -H "x-user-id: $i";echo " user $i requested"; done- 预期输出: - -> mocka(version: v1, ip: 10.0.0.15)-> mockb(version: v1, ip: 10.0.0.130)-> mockc(version: v1, ip: 10.0.0.131) user bob requested -> mocka(version: v1, ip: 10.0.0.15)-> mockb(version: v1, ip: 10.0.0.130)-> mockc(version: v1, ip: 10.0.0.131) user stacy requested -> mocka(version: v1, ip: 10.0.0.15)-> mockb(version: v1, ip: 10.0.0.130)-> mockc(version: v2, ip: 10.0.0.133) user jessie requested -> mocka(version: v1, ip: 10.0.0.15)-> mockb(version: v1, ip: 10.0.0.130)-> mockc(version: v1, ip: 10.0.0.131) user vance requested -> mocka(version: v1, ip: 10.0.0.15)-> mockb(version: v1, ip: 10.0.0.130)-> mockc(version: v2, ip: 10.0.0.133) user jack requested- 可以看到,用户Jessie和Jack的请求直接路由到了mockc应用的v2版本,其他用户路由到了v1版本。