得物社區 Golang 灰度環境探索和實踐
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 配置實現流量有生產應用兜底。雖然依賴人工配置,但中間件都是現成的,而且這部分配置變化頻率較低,只有應用上下架時需要修改。
年中時社區第一批約 15 個 C 端應用上線小得物,同時對小得物環境的監控告警等基礎設施進行了完善。
2.2 小得物 V2
V1 的核心問題在于引流機制是 DNS。DNS 的優勢在于它是在客戶端生效,是去中心化的。但也有很多缺點,比如控制維度單一,只有客戶端 IP、地域。只依靠這個,灰度流量大小難以精準控制,想要的基于 UID、header 的灰度規則也沒法實現。
想要做 UID 灰度引流,一般都是在入口網關上做?;叶扰渲每赡芙洺P枰_關、調整流量大小,如果配置錯誤或出現 bug,則影響所有流量。
因此想到一個折中的方案,從生產 DLB 根據 UID 引入 5% 灰度流量至小得物 DLB,小得物上再通過二次灰度規則控制流量大小在 0-5%。最大流量限定為 5%,生產只配置一次,后續開關、規則調整均在小得物 DLB 上進行。雖然多用了一個 DLB,但減少生產 DLB 配置變更頻率,縮小了爆炸半徑。
之前做新老集群遷移的生產 DLB,本來準備下掉,現在正好可以利用起來。對 DLB 進行了版本升級,配置好灰度規則后,就有了現在 V2 的架構。?
架構升級后:?
- 灰度流量可按 uid 規則引入,灰度用戶流量總是進小得物,用戶范圍可控,規則清晰。
- 灰度流量入口與交易互不影響,流量大小可在 0-5% 范圍內靈活調整?;叶攘髁恳巹t是通過旁路控制,不在生產主鏈路 DLB 上進行,最大流量值限制為 5%,縮小爆炸半徑。
- xdw DLB 配置通過 openAPI 控制,且與發布平臺打通,小得物新版本發布可 0-5% 梯度引流驗證。
- UID 規則外,添加 header 頭引流規則,測試驗證方便。App 可一鍵切換至小得物,由用戶自由選擇。驗證小得物 api 時帶上 header 頭即可路由至小得物,再加上 trace 2.0 全面覆蓋,方便定位流量路徑。
3、發布流程優化?
3.1 依賴隊列自動生成
每個版本版本 owner 都需要整理版本清單,標記出應用的依賴關系,最后手動導入到發布平臺,生產依賴梯隊。
組內大佬覺得這些工作可以自動化完成,便寫了一個代碼靜態分析工具來解決:
- 對版本分支、線上分支分別進行靜態掃描。
- 使用 go 標準庫的 parser 包將其解析 為 AST 語法樹,根據查找 proto client 樁代碼包引用生成單應用 RPC 調用依賴圖。
- 將兩個版本依賴圖進行 diff,找出版本變化部分。
- 將版本所有應用的依賴圖進行關聯,最終生成版本依賴圖。
可以有人會問,為什么不基于 trace 來做?原因新功能可能沒有流量,或是有些路徑執行不到,trace 數據需要線上流量跑一段時間才能完整。而通過靜態分析,源碼中沒有秘密,只要是寫在代碼中的依賴都能覆蓋到。這套靜態分析工具還可以實現循環調用分析,RPC 圈復雜度分析,幫助開發進行微服務治理。
同時與發布平臺打通,發布時觸發靜態分析,自動生成發布依賴狀態圖。以前都是版本 owner 手動畫這個圖,在辦公溝通群眾同步。通過自動化手段,大幅提高了效率和用戶體驗。
流程圖:
效果圖:
3.2 批量發布、梯度引流、灰度分析
在發布平臺和穩定生產小得物團隊的幫助下,社區小得物發布使用了新的批量發布流程。
發布時同時支持同時發布 ARK 配置,版本變更在發布平臺內完成閉環。不必喊應用 owner 去 ARK 修改配置,再人工確認后,再發布程序代碼。
在前文提到的小得物 V2 架構中,灰度流量在社區小得物 DLB 中控制。因此在小得物發布過程中,可以直接通過 openAPI 將小得物流量摘除。沒有了流量,就可以無視應用間依賴,直接批量將所有應用并發部署,大幅提高小得物環境部署效率。
同時摘流后,再通過 API 將流量梯度拉升,從 0% 緩慢提升至 5%,每次引流都會觸發穩定生產 SOS 事件中心的自動巡檢,根據配置的巡檢規則,計算出得分,展示與七天平均值偏差較大的異常點,幫助版本 owner 提前發現灰度問題。
效果圖:
4、全鏈路灰度
4.1 RPC 調用路由
RPC 路由這個功能,大多數據 RPC 調用系統都有。社區目前的 RPC 是基于 grpc-go 擴展實現的,很多人都說 grpc 沒有服務治理功能,但實際上 grpc 有著良好的擴展性和豐富的生態。得物 go 框架基于 grpc-go 只用了千余行代碼即可實現擁有服務發現、多注冊中心、多服務名、地址路由、自定義 interceptor 等完備功能的 RPC 調用系統。
在 grpc resolver 擴展點,在服務發現階段根據規則過濾調用不包含 xdw 元數據的地址,即可實現服務路由功能。
在 drpc pickers 配置項中配置注冊中心元數據表達式 env == "xdw" ,優先路由至小得物節點,在下游服務未部署小得物時兜底至生產節點,保證可用性。
同時為了解決業務應用 RPC 服務名、注冊中心地址、路由規則等配置維護困難、且不統一的痛點,我們做了點微創新,參考 Istio 做了一個中心配置下發,懶加載的功能。
在所有應用中都相同的注冊中心地址、服務名配置維護在控制中心配置中。server 會查找與 target 同名的 service 作為服務名注冊,client 根據 target 名來查找服務名,只有被客戶端樁代碼實際調用的服務才會被 watch。
應用配置只需要引用 drpc 控制中心配置地址即可,pickers 路由規則可以統一下發到所有服務。而像超時等個性化配置應用端可以覆蓋遠端,框架會將其做合并處理。
控制中心遠端配置:
應用端配置:
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 等關鍵詞都可以使用。
找中間件團隊溝通,他們表示 SQL 過濾性能較差,暫不支持。建議使用 Java 染色環境類似的方案,在客戶端過濾。雖然客戶端過濾,有很多無效的網絡傳輸,但成本較低,只需要改造一下業務框架中 MQ SDK 即可,也能解決 MQ 灰度的問題。經過壓測,小得物環境過濾生產環境高 QPS 生產的消息或是 group 積壓的大量消息, 對應用不會造成較大的性能影響,于是采用了此方案。
4.2.1 消息消費 consumer 隔離
consumer 消費的隔離比較簡單,MQ 的機制是不同 group 消息消費都是獨立的,每個 group 都能收到topic 全量消息。
在業務框架中根據染色環境配置,增加不同的處理邏輯。
如果是染色環境(小得物):
- producer 發送消息時,在消息 properties 中添加流量標 X-Flow-Flag=[prefix]。
- consumer 啟動時自動給配置的 group 添加 [prefix]。消費時過濾掉 properties 不包含流量標 X-Flow-Flag=[prefix] 的消息,直接 ack。
如果是基準環境(生產):
- producer 發送消息時,無特殊處理。
- consumer 啟動時使用配置中的 group。消費時過濾掉 properties 包含流量標 X-Flow-Flag=[prefix] 的消息,直接 ack。
4.2.2 事務消息 producer 隔離
事務消息比較特殊,主要體現在 trans producer 有一個回查邏輯。trans producer 不光會向 server 發消息,還會接受 server 發送的回查消息。
查看了一下 DMQ 的 Java 源碼,發現 Boroker 回查時是通過消息 properties 中的 group 來查找在線 producer。那么跟 consumer 類似,給 trans producer 配上 group ,給小得物 group 加上環境前綴即可實現事務回查隔離。用于 trans producer 的 group 只是一個標識,甚至不需要在 DMQ 后臺申請。
5、總結
目前社區已經通過小得物灰度環境的運營取得一些收益:
- 在業務穩定性上,能在正式上線前發現了一些測試、預發環境難以發現的問題,縮小影響范圍,減少上線出問題后匆忙排查、緊急回滾的緊張時刻,降低了系統風險。
- 在開發效率上,通過摘流批量發布、依賴梯隊自動生成、發布流程編排等手段,大大降低了版本發布人力和時間成本。以前版本十來個應用發布,需要多個開發介入,前后依賴等待、觀察,耗費較大人力,生產發布需要 4 個小時以上;現在由一個版本 owner 負責,在小得物驗收通過后,一鍵發布至生產環境,小得物加生產在 2 個小時內能搞定。幾乎解放了 0.5 天的時間,開發可以把這個時間投入到下個版本的技術方案設計上去。
但社區灰度環境只解決了部分問題,還有很多技術難點、體驗優化、流程規范待完善,例如:
- 和前端同學合作,打通中后臺、H5 頁面前后端灰度鏈路。
- 涉及外部業務域、數據同步中間件等場景的 MQ 消息和灰度流量閉環。
- 擴大灰度窗口期,下探“深水區”,優化QA驗證和產品走查流程。
- 優化開發用戶體驗,降低小得物環境維護成本。例如:抽出小得物、生產公共配置,只維護一份。
革命尚未成功,同志仍需努力!?