基于 Istio 的全链路灰度方案探索和实践
点击上方“服务端思维”,选择“设为星标”
回复”669“获取独家整理的精选资料集
回复”加群“加入全国服务端高端社群「后端圈」
1
背景
ASM Pro 产品功能架构图:

2
场景说明

入口流量的 tag 标签,一般是在网关层面基于类似 tag 插件的方式,将请求流量进行打标。 比如将 userid 处于一定范围的打上代表灰度的 tag,考虑到实际环境网关的选择和实现的多样性,网关这块实现不在本文讨论的范围内。
3
实现原理

Inbound 是指请求发到 App 的入口流量,Outbond 是指 App 向外发起请求的出口流量。

4
实现流量打标
apiVersion: istio.alibabacloud.com/v1beta1kind: TrafficLabelmetadata:name: defaultspec:rules:- labels:- name: trafficLabelvalueFrom:- $getContext(x-request-id) //若使用aliyun arms,对应为x-b3-traceid- $(localLabel)attachTo:- opentracing# 表示生效的协议,空为都不生效,*为都生效protocols: "*"
获取逻辑:先根据协议上下文或者头(Header 部分)中的定义的字段获取流量标签,如果没有,会根据 traceId 通过 Sidecar 本地记录的 map 获取, 该 map 表中保存了 traceId 对应流量标识的映射。若 map 表中找到对应映射,会将该流量打上对应的流量标,若获取不到,会将流量标取值为本地部署对应环境的 localLabel。localLabel 对应本地部署的关联 label,label 名为 ASM_TRAFFIC_TAG。
本地部署对应环境的标签名为"ASM_TRAFFIC_TAG",实际部署可以结合 CI/CD 系统来关联。

存储逻辑:attachTo 指定存储在协议上下文的对应字段,比如 HTTP 对应 Header 字段,Dubbo 对应 rpc context 部分,具体存储到哪一个字段中可配置。
5
按流量标签路由
在 DestinationRule 中定义 Subset
自定义分组 subset 对应的是 trafficLabel 的 value
apiVersion: networking.istio.io/v1alpha3kind: DestinationRulemetadata:name: myappspec:host: myapp/*subsets:- name: myproject # 项目环境labels:env: abc- name: isolation # 隔离环境labels:env: xxx # 机器分组- name: testing-trunk # 主干环境labels:env: yyy- name: testing # 日常环境labels:env: zzz---apiVersion: networking.istio.io/v1alpha3kind: ServiceEntrymetadata:name: myappspec:hosts:- myapp/*ports:- number: 12200name: httpprotocol: HTTPendpoints:- address: 0.0.0.0labels:env: abc- address: 1.1.1.1labels:env: xxx- address: 2.2.2.2labels:env: zzz- address: 3.3.3.3labels:env: yyy
labels 用于匹配应用中带特定标记的节点(endpoint);
通过 ServiceEntry 用于指定属于特定 subset 的 IP 地址,注意这种方式与labels指定逻辑不同,它们可以不是从注册中心(K8s 或者其他)拿到的地址,直接通过配置的方式指定。适用于 Mock 环境,这个环境下的节点并没有向服务注册中心注册。
在 VirtualService 中基于 subset
route 部分可以按顺序指定多个 destination,多个 destination 之间按照 weight 值的比例来分配流量。
每个 destination 下可以指定 fallback 策略,case 标识在什么情况下执行 fallback,取值:noinstances(无服务资源)、noavailabled(有服务资源但是服务不可用),target 指定 fallback 的目标环境。如果不指定 fallback,则强制在该 destination 的环境下执行。
按标路由逻辑,我们通过改造 VirtualService,让 subset 支持占位符 $trafficLabel, 该占位符 $trafficLabel 表示从请求流量标中获取目标环境, 对应 TrafficLabel CR 中的定义。
全局默认模式对应泳道,也就是单个环境内封闭,同时指定了环境级别的 fallback 策略。自定义分组 subset 对应的是 trafficLabel 的 value
apiVersion: networking.istio.io/v1alpha3kind: VirtualServicemetadata:name: default-routespec:hosts: # 对所有应用生效- */*http:- name: default-routeroute:- destination:subset: $trafficLabelweight: 100fallback:case: noinstancestarget: testing-trunk- destination:host: */*subset: testing-trunk # 主干环境weight: 0fallback:case: noavailabledtarget: testing- destination:subset: testing # 日常环境weight: 0fallback:case: noavailabledtarget: mock- destination:host: */*subset: mock # Mock中心weight: 0
先打到日常环境,当日常环境没有服务资源时,再打到主干环境。
apiVersion: networking.istio.io/v1alpha3kind: VirtualServicemetadata:name: projectx-routespec:hosts: # 只对myapp生效- myapp/*http:- name: dev-x-routematch:trafficLabel:- exact: dev-x # dev环境: xroute:- destination:host: myapp/*subset: testing # 日常环境weight: 100fallback:case: noinstancestarget: testing-trunk- destination:host: myapp/*subset: testing-trunk # 主干环境weight: 0
将打了主干环境标并且本机环境是 dev-x 的流量,80% 打到主干环境,20% 打到日常环境。当主干环境没有可用的服务资源时,流量打到日常。
sourceLabels 为本地 workload 对应的 label
apiVersion: networking.istio.io/v1alpha3kind: VirtualServicemetadata:name: dev-x-routespec:hosts: # 对哪些应用生效(不支持多应用配置)- myapp/*http:- name: dev-x-routematch:trafficLabel:- exact: testing-trunk # 主干环境标sourceLabels:- exact: dev-x # 流量来自某个项目环境route:- destination:host: myapp/*subset: testing-trunk # 80%流量打向主干环境weight: 80fallback:case: noavailabledtarget: testing- destination:host: myapp/*subset: testing # 20%流量打向日常环境weight: 206
按(环境)标路由
K8s 场景,通过业务部署时自动带上对应环境/分组 label 标识即可,也就是采用K8s 本身作为元数据管理中心。
非 K8s 场景,可以通过微服务已集成的服务注册中心或者元数据配置管理服务(metadata server)来集成实现。

7
应用场景延伸

8
总结
支持多语言、多协议。
统一配置模板 TrafficLabel, 配置简单且灵活,支持多级别的配置(全局、namespace 、pod 级别)。
支持路由 fallback 实现降级。
大促前的性能压测。在线上压测的场景中,为了让压测数据和正式的线上数据实现隔离,常用的方法是对于消息队列,缓存,数据库使用影子的方式。这就需要流量打标的技术,通过 tag 区分请求是测试流量还是生产流量。当然,这需要 Sidecar 对中间件比如 Redis、RocketMQ 等进行支持。
单元化路由。常见的单元化路由场景,可能是需要根据请求流量中的某些元信息比如 uid,然后通过配置得出对应所属的单元。在这个场景中,我们可以通过扩展 TrafficLabel 定义获取“单元标”的函数来给流量打上“单元标”,然后基于“单元标”将流量路由到对应的服务单元。
— 本文结束 —

关注我,回复 「加群」 加入各种主题讨论群。
对「服务端思维」有期待,请在文末点个在看
喜欢这篇文章,欢迎转发、分享朋友圈


评论
