得物社群 golang 灰度環境探索和實踐

碼農談IT發表於2023-03-14

1

背景

灰度釋出可以在服務正式上線前,提前用小流量對新功能進行驗證,提前發現問題,避免故障影響所有使用者,對業務穩定性非常有價值。

得物社群後端技術棧以 golang 為主,本文記錄了社群後端在灰度環境建設過程中遇到的挑戰,以及對應的探索和實踐。

  • 名詞解釋


    • 小得物:得物內部小流量灰度環境。
    • ARK:得物內部配置中心。
    • DLB:得物內部負載均衡中介軟體。
    • DMQ:得物內部訊息中介軟體。
    • DRPC:golang 後端 RPC 系統。


本文對涉及內部敏感資訊部分做了打碼和脫敏處理,敬請理解。

2

小得物灰度引流架構最佳化

2.1    小得物 V1

跟 Java 閘道器對接註冊中心不同,社群 HTTP 是依賴容器 Service 和 Ingress。
對社群來說,因為只有 C 端有外部流量的應用才有部署小得物的價值,所以希望:

  • 小得物可以只部署部分應用。
  • 未部署小得物的 HTTP 入口應用,HTTP 流量導向生產。
  • 未部署小得物的 gRPC 下游應用,gRPC 流量導向生產。

gRPC 流量比較簡單,透過 RPC 系統流量路由功能即可實現,這個在後面流量路由部分會介紹。

要實現小得物環境只部署部分應用,正確路由流量而不報錯,需要閘道器層、RPC 等呼叫層感知叢集內後端服務有沒有部署。

Ingress 這層,其實相當於接了 k8s 的註冊中心,它是可以感知到叢集是否有可用 upstream。但開源配置無法支援這個需求,二開比較複雜,這個也不在社群的控制範圍內。

這個時期社群應用正在進行容器新老叢集遷移,在容器 Ingress 之前加了一層 DLB(可以簡單理解為 Nginx),透過 location 來區分應用是否部署新叢集,以及新老叢集流量灰度。

於是參考生產環境小得物 Ingress 之前加了一層 DLB,透過 location 和 upstream 配置實現流量有生產應用兜底。雖然依賴人工配置,但中介軟體都是現成的,而且這部分配置變化頻率較低,只有應用上下架時需要修改。
得物社群 golang 灰度環境探索和實踐

年中時社群第一批約 15 個 C 端應用上線小得物,同時對小得物環境的監控告警等基礎設施進行了完善。

2.2    小得物 V2

V1 的核心問題在於引流機制是 DNS。DNS 的優勢在於它是在客戶端生效,是去中心化的。但也有很多缺點,比如控制維度單一,只有客戶端 IP、地域。只依靠這個,灰度流量大小難以精準控制,想要的基於 UID、header 的灰度規則也沒法實現。

想要做 UID 灰度引流,一般都是在入口閘道器上做。灰度配置可能經常需要開關、調整流量大小,如果配置錯誤或出現 bug,則影響所有流量。

因此想到一個折中的方案,從生產 DLB 根據 UID 引入 5% 灰度流量至小得物 DLB,小得物上再透過二次灰度規則控制流量大小在 0-5%。最大流量限定為 5%,生產只配置一次,後續開關、規則調整均在小得物 DLB 上進行。雖然多用了一個 DLB,但減少生產 DLB 配置變更頻率,縮小了爆炸半徑。

之前做新老叢集遷移的生產 DLB,本來準備下掉,現在正好可以利用起來。對 DLB 進行了版本升級,配置好灰度規則後,就有了現在 V2 的架構。

得物社群 golang 灰度環境探索和實踐

架構升級後:

  • 灰度流量可按 uid 規則引入,灰度使用者流量總是進小得物,使用者範圍可控,規則清晰
  • 灰度流量入口與交易互不影響,流量大小可在 0-5% 範圍內靈活調整。灰度流量規則是透過旁路控制,不在生產主鏈路 DLB 上進行,最大流量值限制為 5%,縮小爆炸半徑。
  • xdw DLB 配置透過 openAPI 控制,且與釋出平臺打通,小得物新版本釋出可 0-5% 梯度引流驗證。
  • UID 規則外,新增 header 頭引流規則,測試驗證方便。App 可一鍵切換至小得物,由使用者自由選擇。驗證小得物 api 時帶上 header 頭即可路由至小得物,再加上 trace 2.0 全面覆蓋,方便定位流量路徑。









# uid 路由規則uidRoute:    start: 2000    end: 2500
# header 頭路由規則headerRoute:    X-Flow-Flag: xdw

3

釋出流程最佳化

3.1   依賴佇列自動生成

每個版本版本 owner 都需要整理版本清單,標記出應用的依賴關係,最後手動匯入到釋出平臺,生產依賴梯隊。
組內大佬覺得這些工作可以自動化完成,便寫了一個程式碼靜態分析工具來解決:

  • 對版本分支、線上分支分別進行靜態掃描。
  • 使用 go 標準庫的 parser 包將其解析 為 AST 語法樹,根據查詢 proto client 樁程式碼包引用生成單應用 RPC 呼叫依賴圖。
  • 將兩個版本依賴圖進行 diff,找出版本變化部分。
  • 將版本所有應用的依賴圖進行關聯,最終生成版本依賴圖。


可以有人會問,為什麼不基於 trace 來做?原因新功能可能沒有流量,或是有些路徑執行不到,trace 資料需要線上流量跑一段時間才能完整。而透過靜態分析,原始碼中沒有秘密,只要是寫在程式碼中的依賴都能覆蓋到。這套靜態分析工具還可以實現迴圈呼叫分析,RPC 圈複雜度分析,幫助開發進行微服務治理。

同時與釋出平臺打通,釋出時觸發靜態分析,自動生成釋出依賴狀態圖。以前都是版本 owner 手動畫這個圖,在辦公溝通群眾同步。透過自動化手段,大幅提高了效率和使用者體驗。

流程圖:

得物社群 golang 灰度環境探索和實踐

效果圖:

得物社群 golang 灰度環境探索和實踐


3.2   批次釋出、梯度引流、灰度分析
在釋出平臺和穩定生產小得物團隊的幫助下,社群小得物釋出使用了新的批次釋出流程。
釋出時同時支援同時釋出 ARK 配置,版本變更在釋出平臺內完成閉環不必喊應用 owner 去 ARK 修改配置,再人工確認後,再發布程式程式碼。

得物社群 golang 灰度環境探索和實踐

在前文提到的小得物 V2 架構中,灰度流量在社群小得物 DLB 中控制。因此在小得物釋出過程中,可以直接透過 openAPI 將小得物流量摘除。沒有了流量,就可以無視應用間依賴,直接批次將所有應用併發部署,大幅提高小得物環境部署效率

同時摘流後,再透過 API 將流量梯度拉昇,從 0% 緩慢提升至 5%,每次引流都會觸發穩定生產 SOS 事件中心的自動巡檢,根據配置的巡檢規則,計算出得分,展示與七天平均值偏差較大的異常點,幫助版本 owner 提前發現灰度問題。

效果圖:

得物社群 golang 灰度環境探索和實踐

得物社群 golang 灰度環境探索和實踐

4

全鏈路灰度

4.1  RPC 呼叫路由

RPC 路由這個功能,大多資料 RPC 呼叫系統都有。社群目前的 RPC 是基於 grpc-go 擴充套件實現的,很多人都說 grpc 沒有服務治理功能,但實際上 grpc 有著良好的擴充套件性和豐富的生態。得物 go 框架基於 grpc-go 只用了千餘行程式碼即可實現擁有服務發現、多註冊中心、多服務名、地址路由、自定義 interceptor 等完備功能的 RPC 呼叫系統。

得物社群 golang 灰度環境探索和實踐

在 grpc resolver 擴充套件點,在服務發現階段根據規則過濾呼叫不包含 xdw 後設資料的地址,即可實現服務路由功能。
在 drpc pickers 配置項中配置註冊中心後設資料表示式 env == "xdw"優先路由至小得物節點,在下游服務未部署小得物時兜底至生產節點,保證可用性。
同時為了解決業務應用 RPC 服務名、註冊中心地址、路由規則等配置維護困難、且不統一的痛點,我們做了點微創新,參考 Istio 做了一箇中心配置下發,懶載入的功能
在所有應用中都相同的註冊中心地址、服務名配置維護在控制中心配置中。server 會查詢與 target 同名的 service 作為服務名註冊,client 根據 target 名來查詢服務名,只有被客戶端樁程式碼實際呼叫的服務才會被 watch。
應用配置只需要引用 drpc 控制中心配置地址即可,pickers 路由規則可以統一下發到所有服務。而像超時等個性化配置應用端可以覆蓋遠端,框架會將其做合併處理。
控制中心遠端配置:































































metadata:  env: xdwregistries:  # grpc 協議  nacos-grpc:    type: nacos-grpc    url: 協議  nacos-http:    type: nacos-http    url: 備用註冊中心  nacos-bak:    type: nacos-grpc    url: 服務  java-nacos:    type: java-dubbo    url: 多註冊中心引用      - nacos-grpc      - nacos-bak  direct:    type: direct    priority: 15client:  requestTimeout: 700  pickers:    - target: "*"      desc: "優先使用小得物地址"      match:        tag: env == "xdw"    - target: "*"      desc: "兜底,無小得物地址時使用所有地址"      match:        tag: "*"  targetMap:    sns-aaa:      services: # 多服務名        - registryName: muilti-nacos          serviceName: sns-aaa        - registryName: nacos-http          serviceName: sns-aaa-http    sns-bbb:      services:        - registryName: muilti-nacos          serviceName: sns-bbb    # java dubbo 服務    java-ccc:      services:        - registryName: java-nacos          serviceName: "com.xxx.DubboTestGrpcServiceGrpc$ITestGrpcService:1.0:"    # 直連地址    direct-ddd:      services:        - registryName: direct          serviceName: ddd.xxx.com:8080
應用端配置:














drpc:  remoteConfig:    type: ark    url:   client:    targetMap:      sns-aaa:        # 超時配置        methodTimeout:          AaaService/FooMethod: 100      sns-bbb:        methodTimeout:          BbbService/BarMethod: 50
4.2  MQ 訊息路由

社群小得物與生產環境公用一套 DB、 MQ 中介軟體。應用程式碼中 MQ  producer、comsuer,HTTP、GRPC API 是在一個程式中。如果訊息沒有隔離邏輯,小得物開啟消費,則會與生產節點成為同級消費者,消費生產訊息。而小得物環境機器配置較低,消費速度慢會影響業務。

在沒有 MQ 訊息隔離前,採取一個笨辦法,直接關閉小得物 MQ 消費。但這樣小得物的訊息是靠生產處理,在小得物有 MQ 相關新版本變更時,需要考慮新老相容的問題。

隨著社群阿里雲 MQ 遷移 DMQ 進入收尾階段,DMQ Go SDK 也趨於穩定,開始嘗試使用程式化方案解決 MQ 灰度消費的問題。

最開始跟小得物團隊瞭解了一下最初的方案,小得物和生產使用不同的 MQ 例項,這樣就要求 producer、consumer 在小得物全量部署。對於跨業務域的 topic 需要訊息同步機制。感覺複雜度過高,資源成本和維護成本都很高。

後面看到一篇 阿里雲分享的 RocketMQ 灰度方案,其採用訊息打標、group 隔離、SQL 屬性過濾實現訊息灰度,感覺這才是理想的方案。

這裡說一下 tag 過濾和 SQL 過濾,tag 過濾大家比較常用,但一條訊息只能有一個 tag,常被業務佔用,且不能支援 != 這樣的條件。而 SQL 過濾就靈活得多,可以使用訊息 properties 自定義 kv 鍵值對,SQL 的 NOT、BETWEEN、IN 等關鍵詞都可以使用。

得物社群 golang 灰度環境探索和實踐

找中介軟體團隊溝通,他們表示 SQL 過濾效能較差,暫不支援。建議使用 Java 染色環境類似的方案,在客戶端過濾。雖然客戶端過濾,有很多無效的網路傳輸,但成本較低,只需要改造一下業務框架中 MQ SDK 即可,也能解決 MQ 灰度的問題。經過壓測,小得物環境過濾生產環境高 QPS 生產的訊息或是 group 積壓的大量訊息, 對應用不會造成較大的效能影響,於是採用了此方案。

得物社群 golang 灰度環境探索和實踐

4.2.1   訊息消費 consumer 隔離

consumer 消費的隔離比較簡單,MQ 的機制是不同 group 訊息消費都是獨立的,每個 group 都能收到topic 全量訊息。

在業務框架中根據染色環境配置,增加不同的處理邏輯。

如果是染色環境(小得物):

    • producer 傳送訊息時,在訊息 properties 中新增流量標 X-Flow-Flag=[prefix]。

    • consumer 啟動時自動給配置的 group 新增 [prefix]。消費時過濾掉 properties 不包含流量標 X-Flow-Flag=[prefix] 的訊息,直接 ack



trafficRoute:    colorEnv: xdw
如果是基準環境(生產):

    • producer 傳送訊息時,無特殊處理。
    • consumer 啟動時使用配置中的 group。消費時過濾掉 properties 包含流量標 X-Flow-Flag=[prefix] 的訊息,直接 ack



trafficRoute:    excludeEnvList: [xdw]

4.2.2   事務訊息 producer 隔離

事務訊息比較特殊,主要體現在 trans producer 有一個回查邏輯。trans producer 不光會向 server 發訊息,還會接受 server 傳送的回查訊息。

得物社群 golang 灰度環境探索和實踐

檢視了一下 DMQ 的 Java 原始碼,發現 Boroker 回查時是透過訊息 properties 中的 group 來查詢線上 producer。那麼跟 consumer 類似,給 trans producer 配上 group ,給小得物 group 加上環境字首即可實現事務回查隔離。用於 trans producer 的  group 只是一個標識,甚至不需要在 DMQ 後臺申請。

5

總結

目前社群已經透過小得物灰度環境的運營取得一些收益:

  • 在業務穩定性上,能在正式上線前發現了一些測試、預發環境難以發現的問題,縮小影響範圍,減少上線出問題後匆忙排查、緊急回滾的緊張時刻,降低了系統風險。


  • 在開發效率上,透過摘流批次釋出、依賴梯隊自動生成、釋出流程編排等手段,大大降低了版本釋出人力和時間成本。以前版本十來個應用釋出,需要多個開發介入,前後依賴等待、觀察,耗費較大人力,生產釋出需要 4 個小時以上;現在由一個版本 owner 負責,在小得物驗收透過後,一鍵釋出至生產環境,小得物加生產在 2 個小時內能搞定。幾乎解放了 0.5 天的時間,開發可以把這個時間投入到下個版本的技術方案設計上去。



但社群灰度環境只解決了部分問題,還有很多技術難點、體驗最佳化、流程規範待完善,例如:

  • 和前端同學合作,打通中後臺、H5 頁面前後端灰度鏈路。
  • 涉及外部業務域、資料同步中介軟體等場景的 MQ 訊息和灰度流量閉環。
  • 擴大灰度視窗期,下探“深水區”,最佳化QA驗證和產品走查流程。
  • 最佳化開發使用者體驗,降低小得物環境維護成本。例如:抽出小得物、生產公共配置,只維護一份。

革命尚未成功,同志仍需努力!

*文/ 無風

 


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024924/viewspace-2939561/,如需轉載,請註明出處,否則將追究法律責任。

相關文章