文|林育智(花名:源三 )
螞蟻集團高階專家 專注微服務/服務發現相關領域
校對|李旭東
本文 8624 字 閱讀 18 分鐘
|引 言|
服務發現是構建分散式系統的最重要的依賴之一, 在螞蟻集團承擔該職責的是註冊中心和 Antvip,其中註冊中心提供機房內的服務發現能力,Antvip 提供跨機房的服務發現能力。
本文討論的重點是註冊中心和多叢集部署形態(IDC 維度),叢集和叢集之間不涉及到資料同步。
PART. 1 背 景
回顧註冊中心在螞蟻集團的演進,大概起始於 2007/2008 年,至今演進超過 13 年。時至今日,無論是業務形態還是自身的能力都發生了巨大的變化。
簡單回顧一下注冊中心的歷代發展:
V1:引進淘寶的 configserver
V2:橫向擴充套件
從這個版本開始,螞蟻和阿里開始獨立的演進,最主要的差異點是在資料儲存的方向選擇上。螞蟻選擇了橫向擴充套件,資料分片儲存。阿里選擇了縱向擴充套件,加大 data 節點的記憶體規格。
這個選擇影響到若干年後的 SOFARegistry 和 Nacos 的儲存架構。
V3 / V4:LDC 支援和容災
V3 支援 LDC 單元化。
V4 增加了決策機制和執行時列表,解決了單機當機時需要人工介入處理的問題,一定程度上提升高可用和減少運維成本。
V5:SOFARegistry
前四個版本是 confreg,17 年啟動 V5 專案 SOFARegistry,目標是:
1.程式碼可維護性:confreg 程式碼歷史包袱較重
- 少量模組使用 guice 做依賴管理,但大部分模組是靜態類互動,不容易分離核心模組和擴充套件模組,不利於產品開源。
- 客戶端與服務端的互動模型巢狀複雜,理解成本極高且對多語言不友好。
2.運維痛點:引入 Raft 解決 serverlist 的維護問題,整個叢集的運維包括 Raft,通過 operator 來簡化。
3.魯棒性:在一致性 hash 環增加多節點備份機制(預設 3 副本),2 副本當機業務無感。
4.跨叢集服務發現:站內跨叢集服務發現額外需要 antvip 支撐,希望可以統一 2 套設施的能力,同時商業化場景也有跨機房資料同步的需求。
這些目標部分實現了,部分實現的還不夠好,例如運維痛點還殘留一部分,跨叢集服務發現在面對主站的大規模資料下穩定性挑戰很大。
V6:SOFARegistry 6.0
2020 年 11 月,SOFARegistry 總結和吸收內部/商業化打磨的經驗,同時為了應對未來的挑戰,啟動了 6.0 版本大規模重構計劃。
歷時 10 個月,完成新版本的開發和升級工作,同時鋪開了應用級服務發現。
PART. 2 挑 戰
當下面臨的問題
叢集規模的挑戰
- 資料增長:隨著業務的發展,業務的例項數在不斷增長,pub/sub 的數量也相應增長。以其中一個叢集為例,2019 年的資料為基準資料,在 2020 年 pub 接近千萬級。
下圖是該叢集歷年雙十一時的資料對比和切換應用級的優化效果。相比 2019 年雙十一,2021 年雙十一介面級的 pub 增長 200%,sub 增長 80%。
- 故障爆炸半徑增長:叢集接入的例項越多,故障影響的業務和例項數也就越多,保障業務的穩定是最基礎也是優先順序最高的要求。
- 考驗橫向擴充套件能力:叢集達到一定的規模後,是否還具備繼續橫向擴充套件的能力,需要叢集具備良好的橫向擴充套件能力,從 10 擴到 100 和從 100 擴到 500 是不一樣的難度。
- HA 能力:叢集例項數多了後,面臨的節點總體的硬體故障率也相應增高,各種機器故障叢集是否能快速恢復?有運維經驗的同學都知道,運維一個小叢集和運維一個大叢集面臨的困難簡直是指數級增長。
- 推送效能:大多數服務發現的產品都選擇了資料的最終一致性,但是這個最終在不同叢集的規模下到底是多久?相關的產品其實都沒有給出明確的資料。
但是實際上,我們認為這個指標是服務發現產品的核心指標。這個時長對呼叫有影響:新加的地址沒有流量;刪除的地址沒有及時摘除等。螞蟻集團的 PaaS 對註冊中心的推送時延是有 SLO 約束的:如果變更推送列表延時超過約定值,業務端的地址列表就是錯誤的。我們歷史上也曾發生過因推送不及時導致的故障。
業務例項規模增加的同時也帶來推送的效能壓力:釋出端 pub 下面的例項數增加;訂閱端業務例項數增加;一個簡單的估算,pub/sub 增長 2 倍,推送的資料量是 2*2,增長 4 倍,是一個乘積的關係。同時推送的效能也決定了同一時間可以支援的最大運維業務例項數,例如應急場景下,業務大規模重啟。如果這個是瓶頸,就會影響故障的恢復時間。
叢集規模可以認為是最有挑戰性的,核心的架構決定了它的上限,確定後改造成本非常高。而且往往等到發現瓶頸的時候已經是兵臨城下了,我們要選擇能拉高產品技術天花板的架構。
運維的挑戰
SOFARegistryX 立項時的一個主要目標是具備比 confreg 更好的運維能力:引入 meta 角色,通過 Raft 選舉和儲存元資訊,提供叢集的控制面能力。但是事實證明,我們還是低估了可運維的重要性,正如魯迅先生說:【程式設計師的工作只有兩件事情,一件是運維,另一件還是運維】。
三年前的目標放到今天已經嚴重滯後了。
- 叢集數增長:螞蟻集團內部的業務是分站點部署的(簡單理解為每個站點是一塊相對比較獨立的業務,需要不同級別的隔離),同時一個站點需要部署多套叢集:容災需要分機房部署;開發需要分多環境。部署站點的數目增長超出我們的想像。現在已經達到數百個叢集了,還在迅速增長中,增長速度參考最近幾年美聯儲的貨幣供應量增長速度。以前認為有些運維工作可以苟且,人肉頂一下,叢集數增長後,苟且次數太多了,擠佔了開發/運維同學的精力,完全沒資源去規劃詩和遠方。
- 業務打擾:業務的運維是全天候 7*24 的,容量自適應/自愈/MOSN 每月一版本把全站應用犁一遍等等。下圖是每分鐘運維的機器批數,可以看到,就算是週末和深夜,運維任務也是不斷的。
螞蟻集團的同學對註冊中心的運維公告應該是比較熟悉和痛恨的。因為業務的敏感性,註冊中心之前一直是停機發布和運維,這個時候需要鎖定全站的釋出/重啟動作。為了儘量少影響業務,註冊中心相關的同學只能獻祭一頭黑髮,在深夜低峰期做相關的操作。即使這樣,仍然沒辦法做到對業務零打擾。
雲原生時代 naming 的挑戰
雲原生的技術時代下,可以觀察到一些趨勢:
- 微服務/FaaS 的推廣導致輕型應用增多:例項數增多,需要能支撐更大的業務規模
- 應用例項的生命週期更短:FaaS 按需使用,autoscale 容量自適應等手段導致例項的漲潮退潮更頻繁,註冊中心的效能主要體現在例項變更的響應速度上
- 多語言支援:在過去,螞蟻集團主要的開發體系是 Java,非 Java 語言對接基礎設施都是二等公民,隨著 AI 和創新性業務的需求,非 Java 體系的場景越來越多。如果按照每種語言一個 SDK,維護成本會是個噩夢,當然 sidecar(MOSN)是個解法,但是自身是否能支援低侵入性的接入方式,甚至 sdk-free 的能力?
- 服務路由:在過去絕大部分的場景都可以認為 endpoint 是平等的,註冊中心只提供通訊的地址列表是可以滿足需求的。在 Mesh 的精確路由場景裡面,pilot 除了提供 eds(地址列表)也同時提供 rds(routing),註冊中心需豐富自身的能力。
- K8s:K8s 當前已經成為事實上的分散式作業系統,K8s-service 如何和註冊中心打通?更進一步,是否能解決 K8s-service 跨 multi-cluster 的問題?
「總結」
綜上,除了腳踏實地,解決當下的問題,還需要仰望星空。具備解決雲原生趨勢下的 naming 挑戰的可能性,也是 V6 重構的主要目標。
PART. 3 SOFARegistry 6.0:面向效能
SOFARegistry 6.0 不只是一個註冊中心引擎,需要和周邊的設施配合,提升開發、運維、應急的效能,解決以下的問題。(紅色模組是比較有挑戰性的領域)
SOFARegistry 6.0 相關的工作包括:
架構優化
架構的改造思路:在保留 V5 的儲存分片架構的同時,重點的目標是優化元資訊 meta 一致性和確保推送正確的資料。
元資訊 meta 一致性
V5 在 meta 角色中引入 Raft 的強一致性進行選舉 leader 和存放元資訊,其中元資訊包括節點列表和配置資訊。資料的分片通過獲取 meta 的節點列表做一致性 hash,這裡面存在兩個問題:
Raft/operator 運維複雜
(1)定製運維流程:需要支援 change peer 等編排。在螞蟻集團,特化的運維流程成本較高,同時也不利於輸出。
(2)實現一個生產健壯的 operator 成本非常高,包括接入變更管控 operator 自身的變更三板斧等。
(3)對於網路/磁碟的可用性比較敏感。在輸出的場景,會面臨比較惡劣的硬體情況,排查成本較高。
- 脆弱的強一致性
meta 資訊的使用建立在滿足強一致性的情況下,如果出現網路問題,例如有 session 網路分割槽連不上 meta,錯誤的路由表會導致資料分裂。需要機制確保:即使 meta 資訊不一致也能在短時間內維持資料的正確性,留有應急的緩衝時間。
推送正確的資料
當 data 節點大規模運維時,節點列表劇烈變化導致資料不斷遷移,推送出去的資料存在完整性/正確性的風險。V5 通過引 3 副本來避免這種情況,只要有一個副本可用,資料就是正確的,但是該限制對運維流程負擔很大,我們要確保每次操作少於兩個副本或者挑選出滿足約束的運維序列。
對於 V5 及之前的版本,運維操作是比較粗糙的,一刀切做停機發布,通過鎖 PaaS 禁止業務變更,等 data 節點穩定後,再開啟推送能力來確保避免推送錯誤資料的風險。
此外,預期的運維工作可以這樣做,但是對於突發的多 data 節點當機,這個風險仍然是存在的。
我們需要有機制確保:data 節點列表變化導致資料遷移時,容忍接受額外的輕微推送時延,確保推送資料正確。
「成果」
- meta 儲存/選舉 元件外掛化,站內去 Raft,使用 db 做 leader 選舉和儲存配置資訊,降低運維成本。
- 資料使用固定 slot 分片,meta 提供排程能力,slot 的排程資訊通過 slotTable 儲存,session/data 節點可容忍該資訊的弱一致性,提升魯棒性。
- 多副本排程減少 data 節點變動時資料遷移的開銷,當前線上的資料量 follower 升級 leader 大概 200ms (follower 持有絕大部分的資料),直接分配 leader 資料同步耗時 2s-5s。
- 優化資料通訊/複製鏈路,提升效能和擴充套件能力。
- 大規模運維不需要深夜鎖 PaaS,減少對業務打擾和保住運維人員頭髮,提升幸福感。
資料鏈路和 slot 排程:
- slot 分片參考 Redis Cluster 的做法,採用虛擬雜湊槽分割槽,所有的 dataId 根據雜湊函式對映到 0 ~ N 整數槽內。
- meta 的 leader 節點,通過心跳感知存活的 data 節點列表,儘可能均勻的把 slot 的多副本分配給 data 節點,相關的對映關係儲存在 slotTable,有變更後主動通知給 session/data。
- session/data 同時通過心跳獲取最新的 slotTable,避免 meta 通知失效的風險。
- slot 在 data 節點上有狀態機 Migrating -> Accept -> Moved。遷移時確保 slot 的資料是最新的才進入 Accept 狀態,才可以用於推送,確保推送資料的完整性。
data 節點變動的資料遷移:
對一個接入 10w+ client 的叢集進行推送能力壓測,分鐘級 12M 的推送量,推送延遲 p999 可以保持在 8s 以下。session cpu20%,data cpu10%,物理資源水位較低,還有較大的推送 buffer。
同時我們也線上上驗證橫向擴充套件能力,叢集嘗試最大擴容到 session370,data60,meta*3 ;meta 因為要處理所有的節點心跳,CPU 達到 50%,需要 8C 垂直擴容或者進一步優化心跳開銷。按照一個 data 節點的安全水位支撐 200w pub,一個 pub 大概 1.5K 開銷,考慮容忍 data 節點當機 1/3 仍然有服務能力,需要保留 pub 上漲的 buffer,該叢集可支撐 1.2 億的 pub,如果配置雙副本則可支撐 6kw 的 pub。
應用級服務發現
註冊中心對 pub 的格式保留很強的靈活性,部分 RPC 框架實現 RPC 服務發現時,採用一個介面一個 pub 的對映方式,SOFA/HSF/Dubbo2 都是採用這種模式,這種模型比較自然,但是會導致 pub/sub 和推送量膨脹非常厲害。
Dubbo3 提出了應用級服務發現和相關原理【1】。在實現上,SOFARegistry 6.0 參考了 Dubbo3,採用在 session 端整合服務的後設資料中心模組的方案,同時在相容性上做了一些適配。
「應用級服務 pub 資料拆分」
「相容性」
應用級服務發現的一個難點是如何低成本的相容介面級/應用級,雖然最後大部分的應用都能升級到應用級,升級過程中會面臨以下問題:
- 應用數多,同時各個應用升級到應用級的時間點差距比較大
- 部分應用無法升級,例如一些遠古應用
我們採用以應用級服務為主,同時相容介面級的解決方案:
在升級時同時存在新舊版本的兩個 SOFARegistry,不同版本的 SOFARegistry 對應到不同的域名。升級後的應用端(圖中的 MOSN)採用雙訂閱雙釋出的方式逐步灰度切換,確保切換過程中,沒有升級接入 MOSN 或者沒有開啟開關的應用不受影響。
在完成絕大多數應用的應用級遷移後,升級後的應用都已經到了 SOFARegistry 6.0 版本的註冊中心上,但仍然存在少量應用因為沒有接入 MOSN,這些餘留的 old app 也通過域名切換到 SOFARegistry 6.0,繼續以介面級訂閱釋出和註冊中心互動。為了確保已升級的和沒升級的應用能夠互相訂閱,做了一些支援:
- 提供應用級 Publisher 轉接到口級 Publisher 的能力:介面級訂閱端是無法直接訂閱應用級釋出資料的,針對介面級訂閱按需從 AppPublisher 轉換出 InterfacePublisher,沒有接入 MOSN 的應用可以順利的訂閱到這部分資料,因為只有少量應用沒有接入 MOSN,需要轉化的應用級 Publisher 很少。
- 應用級訂閱端在訂閱的時候額外發起一個介面級的訂閱,用於訂閱沒有接入升級的應用釋出資料。由於這部分應用非常少,實際絕大多數的服務級訂閱都不會有推送任務,因此對推送不會造成壓力。
「效果」
上圖是一個叢集切換應用級後的效果,其中切換後剩餘部分介面級 pub 是為了相容轉換出來的資料,介面級 sub 沒減少也是為了相容介面級釋出。如果不考慮相容性,pub 資料減少高達 97%。極大的減輕了資料規模的對叢集的壓力。
SOFARegistryChaos:自動化測試
註冊中心的最終一致性的模型一直是個測試難題:
- 最終是多久?
- 達到最終前有沒有推送錯誤的資料
- 達到最終前有沒有推送少資料
- 叢集發生故障/資料遷移時對資料的正確性和時延的影響
- client 端頻繁按照各種順序呼叫 API 的影響
- client 端頻繁連線斷連的影響
針對該系列問題,我們開發了 SOFARegistryChaos,特別針對最終一致性提供完備的測試能力,除此還提供功能/效能/大規模壓測/混沌測試等能力。同時,通過外掛化的機制,也支援接入測試其他服務發現產品的能力,基於 K8s 的部署能力,能讓我們快速的部署測試元件。
具備以上的能力後,不單可以測試自身的產品能力,例如還可以快速的測試 zookeeper 在服務發現方面的相關效能來做產品比較。
測試觀測性
提供的關鍵資料的觀測能力,通過 metrics 透出,對接 Prometheus 即可提供視覺化能力:
- 推送時延
- 設定時間內的最終一致性檢測
- 發生故障注入的時間點
- 最終一致期間推送資料的完整性
該能力的測試是一個比較有意思的創新點,通過固化一部分的 client 和對應的 pub,校驗每次其他各種變更導致的推送資料,這部分資料都必須是要完整和正確的。 - 推送次數
- 推送資料體積
失敗 case 的排查
測試場景中,client 操作時序和故障注入都是隨機編排的,我們在 SOFARegistryChaos master 端記錄和收集了所有的操作命令時序。當 case 失敗時,可通過失敗的資料明細和各個 client 的 API 呼叫情況來快速定位問題。
例如下圖的失敗 case 顯示在某個 Node 上的訂閱者對某個 dataId 的訂閱資料沒通過校驗,預期是應該要推空, 但是推送了一條資料下來。同時顯示了該 dataId 所有相關的 publisher 在測試期間的相關操作軌跡。
黑盒探測
大家是否經歷過類似的 case:
- 突然被業務告知系統出現問題,一臉懵的我:系統沒異常啊
- 發現系統出現故障時,實際已經對業務造成了嚴重影響
註冊中心因為本身的特性,對業務的影響往往是滯後的,例如 2K 個 IP 只推送了 1K 個,這種錯誤不會導致業務馬上感知到異常。但是實際本身已經出問題了。對於註冊中心,更需要有提前發現治末病的能力。
這裡我們引入黑盒探測的方式:模擬廣義上的使用者行為,探測鏈路是否正常。
SOFARegistryChaos 實際上就可以作為一個註冊中心的使用者,並且是加強版的,提供端到端的告警能力。
我們把 SOFARegistryChaos 部署到線上,開啟小流量作為一個監控項。當註冊中心異常但還沒對業務造成可感知的影響時,我們有機會及時介入,減少風險事件升級成大故障的風險。
磨刀不誤砍柴工
通過 SOFARegistryChaos,核心能力的驗證效率極大提升,質量得到保障的同時,開發同學寫程式碼也輕鬆了許多。從 7 月份到 10 月中的 3 個半月時間裡,我們迭代併發布了 5 個版本,接近 3 周 1 個版本。這個開發效率在以前是不敢想象的,同時也獲得完善的端到端告警能力。
運維自動化
nightly build
雖然我們叢集數目非常多,但是因為是區分了多環境,部分環境對於穩定性的要求相對生產流量要求稍微低一些,例如灰度以下的環境。這些環境的叢集是否可以在新版本保證質量的情況下,快速低成本的 apply 。結合 SOFARegistryChaos,我們和質量/SRE 的同學正在建設 nightly build 設施。
SOFARegistryChaos 作為變更門禁,新版本自動化的部署,接受 SOFARegistryChaos 的測試通過後,自動化部署到灰度以下的叢集,僅在生產釋出時候人工介入。
通過 nightly build,極大的減輕非生產環境的釋出成本,同時新版本能儘早接受業務流量的檢驗。
故障演練
雖然我們做了大量的質量相關的工作,但是線上上面對各種故障時究竟表現如何?是騾子還是馬,還是要拉出來溜一溜。
我們和 SRE 的同學線上上會定期做故障容災演練,包括但不限於網路故障;大規模機器當機等。另外演練不能是一錘子買賣的事情,沒有保鮮的容災能力其實等於 0。在模擬/灰度叢集,進行容災常態化,演練-迭代迴圈。
定位診斷
故障容災演練常態化後,如何快速的定位到故障源成了擺在桌子上的一個問題,否則每次演練都雞飛狗跳的,效率太低了。
SOFARegistry 各個節點做了大量的可觀測性的改進,提供豐富的可觀測能力,SRE 的診斷系統通過相關資料做實時診斷,例如這個 case 裡就是一個 session 節點故障導致 SLO 破線。有了定位能力後,自愈系統也可以發揮作用,例如某個 session 節點被診斷出網路故障,自愈系統可以觸發故障節點的自動化替換。
目前,我們的容災演練應急絕大部分 case 已經不需要人肉介入,也只有這樣低成本的演練才能常態化。
「收益」
通過不斷的演練暴露問題和快速迭代修復,SOFARegistry 的穩定性逐步提升。
「總結」
SOFARegistry 6.0 除了自身的優化,在測試/運維/應急方面做了大量的工作,目標是提升研發/質量/運維人員的效能,讓相關同學擺脫低效的人肉工作,提升幸福感。
PART. 4 開源:一個人可以走得很快,但一群人可以走的更遠
SOFARegistry 是一個開源專案,也是開源社群 SOFAStack 重要的一環,我們希望用社群的力量推動 SOFARegistry 的前進,而不是隻有螞蟻集團的工程師去開發。
在過去一年,SOFARegistry 因為重心在 6.0 重構上,社群幾乎處於停滯狀態,這個是我們做得不夠好的地方。
我們制定了未來半年的社群計劃,在 12 月份會基於內部版本開源 6.0,開源的程式碼包含內部版本的所有核心能力,唯一區別是內部版本多了對 confreg-client 的相容性支援。
另外從 6.1 後,我們希望後繼的方案設計/討論也是基於社群來開展,整個研發程式更透明和開放。
PART. 5 我們仍在路上
2021 年是 SOFARegistry 審視過去,全面夯實基礎,提升效能的一年。
當然,我們當前還仍然處在初級階段,前面還有很長的路要走。例如今年雙十一的規模面臨一系列非常棘手的問題:
- 一個叢集內單應用例項數過多(熱點應用單叢集高達 7K 個例項)導致業務端收到地址推送時 CPU/memory 開銷過大。
- 全地址列表推送,導致的連線數過多等。
還有其他的挑戰:
- 增量推送,減少推送資料量和 client 端的資源開銷
- 統一服務發現,支援跨叢集
- 適應雲原生下的新趨勢
- 社群的運營
- 產品易用性
「參 考」
【1】Dubbo3 提出了應用級服務發現和相關原理:
https://dubbo.apache.org/zh/b...
關於我們:
螞蟻應用服務團隊是服務於整個螞蟻集團的核心技術團隊,打造了世界領先的金融級分散式架構的基礎設施平臺,是 Service Mesh 等雲原生領域的領先者,開發運維著全球最大的 Service Mesh 叢集,螞蟻集團的訊息中介軟體每天支撐上萬億的訊息流轉。
歡迎對 Service Mesh/微服務/服務發現 等領域感興趣的同學加入我們。
聯絡郵箱: yuzhi.lyz@antgroup.com
本週推薦閱讀
穩定性大幅度提升:SOFARegistry v6 新特性介紹