自 17 年入職華為之后,一直在使用配置中心,4年期間經(jīng)歷了自研配置中心到 Apollo 再到自研配置中心和 Apollo 并存的場景??偨Y(jié)了一下這幾年的配置中心演進流程,想把我們在配置中心上的一些實踐分享給大家,實現(xiàn)共同進步。Apollo 是一款非常優(yōu)秀的開源軟件,如果對 Apollo 存在理解錯誤,還望大家不吝賜教,謝謝。
1 使用到的配置分類
1.1 從場景分類
1.1.1 運維配置,即程序只讀的配置
人工配置。通過人工在配置中心界面進行配置,而程序只進行讀取,如 數(shù)據(jù)庫 配置、郵箱服務器配置、網(wǎng)卡配置、子網(wǎng)地址配置等。這部分配置數(shù)據(jù)不要求代碼動態(tài)寫入。
1.1.2 業(yè)務配置,即程序可寫的配置
我們是一個 SaaS 服務,每個用戶在上面都有一些業(yè)務配置。如用戶的證書配置、用戶服務器的流控配置等,這些業(yè)務配置相對運維配置來說更加復雜,且可能會有唯一性限制,如按用戶 id 唯一。這部分配置數(shù)據(jù)一般由用戶操作觸發(fā),代碼動態(tài)寫入,并且通知到各個微服務實例。通常,我們希望這些配置能在界面展示,且支持人為修改。上述邏輯如果由各微服務自己實現(xiàn),會存在大量重復代碼,并且質(zhì)量無法保證。我們希望由一個公共組件來統(tǒng)一實現(xiàn)這個能力。
1.2 從配置是否會有列表可分為單值配置或多值配置
1.2.1 單值配置
整個配置下只是多對 key、value。value 不是很復雜的格式,往往是整數(shù)或字符串。
1.2.2 多值配置
多值配置更加復雜,往往是單值配置在不同的 key 下,有不同的值。比如下面的配置,用戶一和用戶二的線程池大小和隊列不同
2 第一階段自研配置中心
在做云服務之前,我們的配置中心層級數(shù)較少。我們以軟件的形式交付給客戶,軟件運行時分為管理面和業(yè)務面,配置中心管理著管理面和業(yè)務面的配置,最為復雜的場景是多套業(yè)務面,這個時候需要保證不同集群、不同微服務下的配置不沖突,配置層級為集群、微服務、配置。

此時的配置中心是完全自研的,不包含藍綠、灰度配置這些功能,它獨具特色的地方有以下兩點:
2.1 單配置單表
- 在存儲模型上,每個配置對應一張數(shù)據(jù)表。
- 對多值配置比較友好,尤其是復雜業(yè)務配置,可以支持各種主鍵約束。對單值配置,稍微重型了一些。
- 配置的強 Schema 限制。這些限制包括類型、大小、長度、是否敏感等限制。這種限制既能為界面修改配置提供良好的體驗(如:不同格式不同的輸入框、敏感字段,前臺輸入明文,后臺入庫加密等),也能在通過接口寫入配置時做充分的校驗。
2.2 通過回調(diào)方式來確保配置的可靠
舉個例子,添加一個配置的流程是這樣的
可能這里,有讀者想要問了,這個流程能確保什么可靠呢。這個流程通過調(diào)用微服務接口來校驗配置是否可靠,如 IP 地址是否合法、對端地址是否可達、配置數(shù)量是否超過規(guī)格等等,來保證配置基本可用。
總的來說,這個自研的配置中心在當時綜合體驗還是不錯的。但是也有一些問題有待改進,比如單配置下配置項數(shù)量過多時,因為底層有部分接口單配置下所有數(shù)據(jù)都通過一個 http 請求來承載,會導致響應超時等問題。
3 第二階段 Apollo
開始第二階段實踐的原因主要是,我們進行了組織切換,業(yè)務重心轉(zhuǎn)向做云服務,同時團隊進行 DevOps 轉(zhuǎn)型。原先的老配置中心是由另一個團隊維護的,組織切換完之后,如果還要使用,就要我們自己維護。所以我們需要在繼續(xù)維護老配置中心和引入開源 Apollo 中間進行選擇。除了上文中提到的運維配置和業(yè)務配置,這個時候我們的需求還有改變:
- 配置的層級愈發(fā)豐富了
- 要構(gòu)建灰度發(fā)布微服務的能力
老配置中心一方面由于組織切換原因不提供維護了,另一方面不能支撐豐富的配置層級,也不具備灰度發(fā)布的能力。這個時候,Apollo 的一些特性吸引了我們,這些特性正是老配置中心所缺乏的,例如(部分引用自 Apollogithub 主頁)
- 豐富的層級,從 app_id 到 cluster,namespace,key-value 的層級能滿足我們 region、集群、微服務的層級訴求;
- 支持配置的灰度發(fā)布,比如點了發(fā)布后,只對部分應用實例生效,等觀察一段時間沒問題后再推給所有應用實例;
- 所有的配置發(fā)布都有版本概念,從而可以方便的支持配置的回滾;
- 應用和配置的管理都有完善的權(quán)限管理機制,對配置的管理還分為了編輯和發(fā)布兩個環(huán)節(jié),從而減少人為的錯誤;
- 所有的操作都有審計日志。
因此我們選型引入了 Apollo,我和我的主管,還有一個其他同事參與了這項工作。我們在 Apollo 開源代碼的基礎上做了比較大的改動,主要原因有以下幾點:
- 節(jié)約成本,將注冊中心、數(shù)據(jù)庫替換成我們當前正在使用的組件,因為這兩個依賴不是 Apollo 的核心依賴
- 繼承老配置中心強 Schema 的優(yōu)點
- 保留回調(diào)確認配置的流程,提前攔截錯誤的配置,降低代碼處理異常配置的復雜度
- 通過 spi 或環(huán)境變量的方式兼容存量老局點使用老配置中心的場景
結(jié)合上述原因,我們最終是這么實踐的:
- 數(shù)據(jù)庫切換為 postgre 數(shù)據(jù)庫、注冊中心切換到 servicecomb
- 在 namespace 上實現(xiàn)了 Schema,每個 namespace 都可以注冊對應的 Schema,Schema 要求數(shù)據(jù)必須是 json 格式,且 json 內(nèi)對應的 value 必須滿足 Schema 定義的規(guī)范(如 ip 地址、小數(shù)、整數(shù)等)
Schema 舉例:
|
[ { "name":"name", "type":"string" }, { "name":"age", "type":"int", "max":120 }, { "name":"ip", "type":"ipv4" } ] |
那么數(shù)據(jù)應該是這樣的:
|
{ "name":"hezhangjian", "age":23, "ip":"127.0.0.1" } |
- 在添加或修改配置的時候,實現(xiàn)了回調(diào)功能,由回調(diào)業(yè)務服務確認配置能否添加或修改;
- 配置分層:云服務對應 Apollo 的 app_id,把內(nèi)部的環(huán)境對應到 Apollo 上的集群,然后將微服務名+配置名拼接成配置名稱。
下圖展示了業(yè)務概念和 Apollo 概念的對應關(guān)系,有些配置是單值配置,有些是多值配置,所以配置項這一層級是可選的。
在這段時間的實踐中,我們也發(fā)現(xiàn)了如下的一些問題。
3.1 并發(fā)問題
首先 Apollo 所有配置都存在一張表中,其次由于 Apollo 設計之初主要考慮的是運維人員手動在界面上操作,代碼無并發(fā)語義(或者說沒給客戶端并發(fā)語義),使得我們通過代碼寫入配置時難以解決并發(fā)問題。
3.2 性能問題
打開 namespace 列表頁面,需要顯示這個 app_id 下的所有 namespace,因為我們單 app_id 會存放單個云服務的所有配置,這個量很大,且界面不支持分頁,導致頁面加載緩慢。
3.3 體驗問題
Apollo 的 namespace 界面未提供搜索功能(可能 Apollo 設計之初也沒想支持這么多),想要從 namespace 中定位到我們想要查看或修改的 namespace,只能借助瀏覽器的搜索能力。
4 第三階段 Apollo 與自研配置中心并存
除了上述幾個問題,還有一些原因使得我們開始了第三階段的實踐:
- 原來自上而下的配置分層模型,微服務間配置沒隔離,不僅不易進行權(quán)限管理,而且不適合 DevOps 單微服務自治的發(fā)布理念;
- 第二階段對 Apollo 改動太多,組織結(jié)構(gòu)變動,沒有足夠的人力維護;
- 隨著集群越來越多,回調(diào)功能需要網(wǎng)絡的雙向打通,網(wǎng)絡維護不太方便;
- 我們對 Apollo 界面以及接口基于業(yè)務做的改動較多,導致其他兄弟部門難以共用 Apollo
當時大家對是否保留 Schema、回調(diào)檢查、代碼寫配置這三個功能點有較大的爭議。我個人最希望保留 Schema、回調(diào)檢查,因為它們優(yōu)點顯著,而且接口是兼容的,可以與其他部門共用,但是增加了 Schema 這個概念和回調(diào)檢查這個流程,會增加學習成本。而代碼寫配置,由于要解決并發(fā)問題,代碼改動量較大,我不建議保留。
大家經(jīng)過激烈的討論,最終還是廢棄了 Schema、回調(diào)檢查、代碼寫配置這三個功能點,僅僅把運維配置放在 Apollo。
然后,我們把業(yè)務配置,放在了一個自研的強 Schema 的配置中心上,這個配置中心,僅負責單集群的配置,每個集群部署一套,滿足了我們的業(yè)務需求。自研強 Schema 配置中心的核心要點有,單配置單表、通過注冊中心回調(diào)來檢測配置是否合法、借助 mqtt 協(xié)議來實現(xiàn)長鏈接推送,無單點瓶頸。
而我們的運維配置中心 Apollo 回歸到了開源的版本,重整了配置的結(jié)構(gòu),

對運維配置而言好處有:
- 配置模型適合單微服務發(fā)布;
- 配置按微服務組織,一個頁面上的 namespace 不會很多。
缺點:
- Schema 缺失后,不會對操作人員在界面的配置進行校驗,即使配置格式或者內(nèi)容錯誤也能配置成功。界面上配置密碼不支持明文(Apollo 無法感知是否為敏感字段),必須提前使用其他工具將明文轉(zhuǎn)換為密文,然后再進行配置;
- 回調(diào)檢查功能去掉后,有些配置,如網(wǎng)卡網(wǎng)段配錯,操作人員不能即時得到響應。
4.1 最佳實踐
業(yè)務配置經(jīng)過我們的實踐,確實不適合使用開源的 Apollo。運維配置使用原生的 Apollo,但是現(xiàn)在還不具備回調(diào)檢查和 Schema 的功能,希望 Apollo 能在后續(xù)版本中支持 Schema,或者弱化的 json 格式檢查功能。下面是我們在如下場景下的最佳實踐。
4.1.1 SRE 在界面上的運維配置
通過 Apollo 來實現(xiàn)功能,至于配置如何組織,根據(jù)大家的組織結(jié)構(gòu)、技術(shù)架構(gòu)來對應 Apollo 上的概念,可按照微服務->部署環(huán)境或部署環(huán)境->微服務的層級來組織配置。
4.1.2 復雜的參數(shù)校驗
建議在 Apollo 上面自建 portal 包裹一層,后端服務可先進行一層處理,這一層處理可以做比較復雜的格式化校驗甚至回調(diào)檢查,再調(diào)用 Apollo OpenApi 將配置寫入 Apollo。
4.1.3 業(yè)務配置的技術(shù)選型
最大的挑戰(zhàn)是業(yè)務配置由用戶觸發(fā),請求的并發(fā)不易處理。思路有兩個,一個是在 Apollo 原生代碼的基礎上,通過數(shù)據(jù)庫分布式鎖來解決并發(fā)問題。第二個是借鑒我們的思路,通過單配置單表、mqtt 協(xié)議實現(xiàn)通知等核心技術(shù)點,自研業(yè)務配置中心。
4.1.4 業(yè)務配置的部署
需要根據(jù)業(yè)務配置的數(shù)量來考慮是否合設業(yè)務配置中心。單集群場景下,毫無疑問只需要一個業(yè)務配置中心,甚至如果使用 Apollo 實現(xiàn),可以考慮和運維配置中心合設。多集群場景下,部署一個業(yè)務配置中心,還是多個業(yè)務配置中心,我們自己的實踐中,一個集群往往要支撐數(shù)萬用戶,我們采取了每個業(yè)務集群部署一套業(yè)務配置中心的策略。