基于 Istio 的全鏈路灰度方案探索和實(shí)踐

作者|曾宇星(宇曾)
審核&校對(duì):曾宇星(宇曾)
編輯&排版:雯燕
1
背景
ASM Pro 產(chǎn)品功能架構(gòu)圖:

2
場(chǎng)景說(shuō)明

入口流量的 tag 標(biāo)簽,一般是在網(wǎng)關(guān)層面基于類似 tag 插件的方式,將請(qǐng)求流量進(jìn)行打標(biāo)。?比如將 userid 處于一定范圍的打上代表灰度的 tag,考慮到實(shí)際環(huán)境網(wǎng)關(guān)的選擇和實(shí)現(xiàn)的多樣性,網(wǎng)關(guān)這塊實(shí)現(xiàn)不在本文討論的范圍內(nèi)。
3
實(shí)現(xiàn)原理

Inbound 是指請(qǐng)求發(fā)到 App 的入口流量,Outbond 是指 App 向外發(fā)起請(qǐng)求的出口流量。

4
實(shí)現(xiàn)流量打標(biāo)
apiVersion: istio.alibabacloud.com/v1beta1kind: TrafficLabelmetadata:name: defaultspec:rules:- labels:- name: trafficLabelvalueFrom:????????-?$getContext(x-request-id)??//若使用aliyun?arms,對(duì)應(yīng)為x-b3-traceid- $(localLabel)attachTo:- opentracing# 表示生效的協(xié)議,空為都不生效,*為都生效protocols: "*"
獲取邏輯:先根據(jù)協(xié)議上下文或者頭(Header 部分)中的定義的字段獲取流量標(biāo)簽,如果沒(méi)有,會(huì)根據(jù) traceId 通過(guò) Sidecar 本地記錄的 map 獲取, 該 map 表中保存了 traceId 對(duì)應(yīng)流量標(biāo)識(shí)的映射。若 map 表中找到對(duì)應(yīng)映射,會(huì)將該流量打上對(duì)應(yīng)的流量標(biāo),若獲取不到,會(huì)將流量標(biāo)取值為本地部署對(duì)應(yīng)環(huán)境的 localLabel。localLabel 對(duì)應(yīng)本地部署的關(guān)聯(lián) label,label 名為 ASM_TRAFFIC_TAG。
本地部署對(duì)應(yīng)環(huán)境的標(biāo)簽名為"ASM_TRAFFIC_TAG",實(shí)際部署可以結(jié)合 CI/CD 系統(tǒng)來(lái)關(guān)聯(lián)。

存儲(chǔ)邏輯:attachTo 指定存儲(chǔ)在協(xié)議上下文的對(duì)應(yīng)字段,比如 HTTP 對(duì)應(yīng) Header 字段,Dubbo 對(duì)應(yīng) rpc context 部分,具體存儲(chǔ)到哪一個(gè)字段中可配置。
5
按流量標(biāo)簽路由
在 DestinationRule 中定義 Subset
自定義分組 subset 對(duì)應(yīng)的是 trafficLabel 的 value
apiVersion: networking.istio.io/v1alpha3kind: DestinationRulemetadata:name: myappspec:host: myapp/*subsets:- name: myproject # 項(xiàng)目環(huán)境labels:env: abc- name: isolation # 隔離環(huán)境labels:env: xxx # 機(jī)器分組- name: testing-trunk # 主干環(huán)境labels:env: yyy- name: testing # 日常環(huán)境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 用于匹配應(yīng)用中帶特定標(biāo)記的節(jié)點(diǎn)(endpoint);
通過(guò) ServiceEntry 用于指定屬于特定 subset 的 IP 地址,注意這種方式與labels指定邏輯不同,它們可以不是從注冊(cè)中心(K8s 或者其他)拿到的地址,直接通過(guò)配置的方式指定。適用于 Mock 環(huán)境,這個(gè)環(huán)境下的節(jié)點(diǎn)并沒(méi)有向服務(wù)注冊(cè)中心注冊(cè)。
在 VirtualService 中基于 subset
route 部分可以按順序指定多個(gè) destination,多個(gè) destination 之間按照 weight 值的比例來(lái)分配流量。
每個(gè) destination 下可以指定 fallback 策略,case 標(biāo)識(shí)在什么情況下執(zhí)行 fallback,取值:noinstances(無(wú)服務(wù)資源)、noavailabled(有服務(wù)資源但是服務(wù)不可用),target 指定 fallback 的目標(biāo)環(huán)境。如果不指定 fallback,則強(qiáng)制在該 destination 的環(huán)境下執(zhí)行。
按標(biāo)路由邏輯,我們通過(guò)改造 VirtualService,讓 subset 支持占位符? $trafficLabel, 該占位符 $trafficLabel 表示從請(qǐng)求流量標(biāo)中獲取目標(biāo)環(huán)境, 對(duì)應(yīng) TrafficLabel CR 中的定義。
全局默認(rèn)模式對(duì)應(yīng)泳道,也就是單個(gè)環(huán)境內(nèi)封閉,同時(shí)指定了環(huán)境級(jí)別的 fallback 策略。自定義分組 subset 對(duì)應(yīng)的是 trafficLabel 的 value
apiVersion: networking.istio.io/v1alpha3kind: VirtualServicemetadata:name: default-routespec:hosts: # 對(duì)所有應(yīng)用生效- */*http:- name: default-routeroute:- destination:subset: $trafficLabelweight: 100fallback:case: noinstancestarget: testing-trunk- destination:host: */*subset: testing-trunk # 主干環(huán)境weight: 0fallback:case: noavailabledtarget: testing- destination:subset: testing # 日常環(huán)境weight: 0fallback:case: noavailabledtarget: mock- destination:host: */*subset: mock # Mock中心weight: 0
先打到日常環(huán)境,當(dāng)日常環(huán)境沒(méi)有服務(wù)資源時(shí),再打到主干環(huán)境。
apiVersion: networking.istio.io/v1alpha3kind: VirtualServicemetadata:name: projectx-routespec:hosts: # 只對(duì)myapp生效- myapp/*http:- name: dev-x-routematch:trafficLabel:- exact: dev-x # dev環(huán)境: xroute:- destination:host: myapp/*subset: testing # 日常環(huán)境weight: 100fallback:case: noinstancestarget: testing-trunk- destination:host: myapp/*subset: testing-trunk # 主干環(huán)境weight: 0
將打了主干環(huán)境標(biāo)并且本機(jī)環(huán)境是 dev-x 的流量,80% 打到主干環(huán)境,20% 打到日常環(huán)境。當(dāng)主干環(huán)境沒(méi)有可用的服務(wù)資源時(shí),流量打到日常。
sourceLabels 為本地 workload 對(duì)應(yīng)的 label
apiVersion: networking.istio.io/v1alpha3kind: VirtualServicemetadata:name: dev-x-routespec:hosts: # 對(duì)哪些應(yīng)用生效(不支持多應(yīng)用配置)- myapp/*http:- name: dev-x-routematch:trafficLabel:- exact: testing-trunk # 主干環(huán)境標(biāo)sourceLabels:- exact: dev-x # 流量來(lái)自某個(gè)項(xiàng)目環(huán)境route:- destination:host: myapp/*subset: testing-trunk # 80%流量打向主干環(huán)境weight: 80fallback:case: noavailabledtarget: testing- destination:host: myapp/*subset: testing # 20%流量打向日常環(huán)境weight: 206
按(環(huán)境)標(biāo)路由
K8s 場(chǎng)景,通過(guò)業(yè)務(wù)部署時(shí)自動(dòng)帶上對(duì)應(yīng)環(huán)境/分組 label 標(biāo)識(shí)即可,也就是采用K8s 本身作為元數(shù)據(jù)管理中心。
非 K8s 場(chǎng)景,可以通過(guò)微服務(wù)已集成的服務(wù)注冊(cè)中心或者元數(shù)據(jù)配置管理服務(wù)(metadata server)來(lái)集成實(shí)現(xiàn)。

7
應(yīng)用場(chǎng)景延伸

8
總結(jié)
支持多語(yǔ)言、多協(xié)議。
統(tǒng)一配置模板 TrafficLabel, 配置簡(jiǎn)單且靈活,支持多級(jí)別的配置(全局、namespace 、pod 級(jí)別)。
支持路由 fallback 實(shí)現(xiàn)降級(jí)。
大促前的性能壓測(cè)。在線上壓測(cè)的場(chǎng)景中,為了讓壓測(cè)數(shù)據(jù)和正式的線上數(shù)據(jù)實(shí)現(xiàn)隔離,常用的方法是對(duì)于消息隊(duì)列,緩存,數(shù)據(jù)庫(kù)使用影子的方式。這就需要流量打標(biāo)的技術(shù),通過(guò) tag 區(qū)分請(qǐng)求是測(cè)試流量還是生產(chǎn)流量。當(dāng)然,這需要 Sidecar 對(duì)中間件比如 Redis、RocketMQ 等進(jìn)行支持。
單元化路由。常見(jiàn)的單元化路由場(chǎng)景,可能是需要根據(jù)請(qǐng)求流量中的某些元信息比如 uid,然后通過(guò)配置得出對(duì)應(yīng)所屬的單元。在這個(gè)場(chǎng)景中,我們可以通過(guò)擴(kuò)展 TrafficLabel 定義獲取“單元標(biāo)”的函數(shù)來(lái)給流量打上“單元標(biāo)”,然后基于“單元標(biāo)”將流量路由到對(duì)應(yīng)的服務(wù)單元。
評(píng)論
圖片
表情
