搭建雲原生配置中心的技術選型和落地實踐

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"引言"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在從單體應用向微服務架構轉型的過程中,服務配置管理從只需要應對一個單體服務,變爲應對大量分佈式服務,難度呈幾何級增加。爲了解決這個難題,各種應對分佈式服務的配置中心應運而生,如何搭建一個高效合理的配置中心已經成爲每個大型分佈式系統必經的考驗。而在服務“上雲”的大趨勢下,如何讓配置中心在雲平臺順利落地,更進一步,如何藉助雲計算的優勢讓配置中心如虎添翼,目前業內對這一塊還處於探索階段。本文將介紹FreeWheel核心業務系統在AWS雲平臺上搭建配置中心的實戰,作爲搭建雲原生配置中心的參考,希望能給大家帶來啓發。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"更多相關內容參見系列文章:"},{"type":"link","attrs":{"href":"https:\/\/www.infoq.cn\/theme\/87","title":null,"type":null},"content":[{"type":"text","text":"雲原生環境下的微服務實踐全解"}],"marks":[{"type":"underline"}]}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"爲什麼需要配置中心?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"分佈式系統興起之後,配置管理變得尤爲複雜。1984年,業界就有兩位學者Kramer Jeff和Magee Jeff在IEEE發表了一篇文章《分佈式系統的動態配置》,其中闡述到:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"動態系統配置是在系統運行時對其進行修改和擴展的能力。在大型分佈式系統中,這是一個必不可缺的功能,因爲如果需要停止整個系統來對其部分硬件或軟件進行修改,在生產環境是難以接受的,或者會產生較大經濟損失。另外,動態配置也有助於系統在生產環境上組件的增量集成,以達成系統演進的目的。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這篇文章指出了動態配置對於分佈式系統的重要性,即在系統運行時,如何經濟安全地對系統進行調整。現在分佈式系統已經發展到了從前難以想象的複雜程度,除了動態配置,配置管理還面臨更多挑戰,例如:"}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如何統一管理數量衆多的服務配置"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如何在異地衆多機器節點上部署配置"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如何實現灰度"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如何確定配置是否生效"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如何對配置進行災備"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"…"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了解決這些挑戰,配置中心應運而生。配置中心就是分佈式服務中統一管理服務配置的系統,它具備高效和動態控制服務的能力。配置中心一般具備服務註冊、服務配置管理、部署服務配置等基本功能。下面是一個微服務架構下配置中心的示意圖:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":" "}]},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/ca\/ca40701809accaa17a3a04a7b975e1f0.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"配置中心一般會包含服務端、客戶端和界面這三個組件:每個微服務啓動時可以通過客戶端進行服務註冊;用戶可以通過界面創建、修改和部署配置;動態配置功能可以通過服務端實時推送、客戶端定期拉取或者兩者推拉結合來實現。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"FreeWheel雲原生配置中心實戰"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"痛點"}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"服務配置的數量大幅增加"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Freewheel核心業務系統中已拆分出數十個獨立的微服務,每個微服務都需要部署多個環境(Staging、Production、開發、測試環境),多個集羣,多個區域(Region)。開發人員需要維護多套服務配置。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"服務配置的部署方式不同"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Freewheel核心業務系統在雲平臺和數據中心的部署方式,以及在不同環境的部署方式各不相同。開發人員需要投入時間成本去學習和管理各種部署方式。"}]},{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"缺乏動態配置功能"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Freewheel核心業務系統在運行時修改服務配置的流程較爲繁瑣,包括提交、評審、合併分支、運行測試、打包、部署Staging和Production等。這個效率不能滿足團隊需求,例如Freewheel作爲面向企業級客戶提供廣告投放服務的系統,在廣告投放的高峯期處理的數據量遠高於平常,工程師團隊需要動態配置服務的超時參數;又如在生產環境對問題進行定位和調試時,需要儘快調高日誌級別,以便快速解決問題。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"保證Freewheel核心業務系統面向企業級用戶的高可用性"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"軟件系統總是會宕機的,當配置中心繫統被許多微服務依賴做配置管理時,一定得考慮到它宕機時其他服務該怎麼辦。所以配置中心需要實現爲弱依賴而非強依賴,即配置中心出現系統故障時,其他服務也能正常啓動和運行。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"保證配置中心的安全性"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"配置中心的管理對象是比較敏感的服務配置項,對安全性有較高要求,需要合理配置用戶和集羣的訪問權限。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"配置中心選型"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了解決上述痛點,我們開始爲Freewheel核心業務系統設計並搭建配置中心。在選型階段,我們參考了當時較爲成熟的幾個配置中心產品,如Apollo、Nacos、Consul等。配置中心的第一個版本中,我們選擇了Apollo作爲服務端和界面,因爲Apollo在用戶界面友好度、核心功能支持度、社區文檔完善度方面都較爲突出。Apollo產品架構主要包含Config Service(提供配置推送和拉取接口)、Admin Service(提供配置管理接口)、 Portal(用戶界面)和客戶端,如下圖所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/ac\/ac9fc7c13427f7e3ab91dbb3de08c663.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Freewheel核心業務系統當時正往AWS雲服務上遷徙,我們爲配置中心開發了客戶端,並在AWS開發環境部署了Apollo的相關服務。但隨後我們產生了一些顧慮。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"首先是學習維護成本"},{"type":"text","text":":Freewheel核心業務系統的微服務架構使用GO技術棧,與Apollo使用的Java不一致,工程師團隊需要投入額外的學習成本;使用Apollo還需要在AWS上維護四套非雲原生的服務:Config Service、Admin Service、Portal和DB,由於缺乏產品級的Apollo技術支持,會產生較大的維護成本。"},{"type":"text","marks":[{"type":"strong"}],"text":"其次是產品國際化的問題"},{"type":"text","text":":Freewheel對於開源產品的使用有嚴格的審計流程,需要提交代碼庫、英文版的架構設計和使用說明文檔。Apollo作爲一款國內自研產品,沒有發佈詳細的英文文檔。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因此我們開始考察其他產品如AWS AppConfig。調研發現,AppConfig的功能沒有Apollo那麼全面:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"embedcomp","attrs":{"type":"table","data":{"content":"

功能

AWS AppConfig

Apollo

配置創建部署

支持

支持

配置參數正確度校驗

支持

支持

多環境支持

支持

支持

用戶權限控制

需要定製

支持

版本管理

支持

支持

灰度發佈

支持

支持

用戶界面

需要定製

支持

服務端推送功能

不支持

支持

客戶端拉取功能

需要定製

支持

配置監控

支持

支持"}}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"配置中心一個重要的服務端推送功能不被AppConfig支持,這會影響配置中心的SLA,即配置生效的時延。然而作爲一家B2B公司,Freewheel對配置中心SLA的要求不太高。而配置中心其他幾個重要功能:客戶端拉取、用戶權限控制、用戶界面,我們能基於現有的微服務架構用較小的開發成本實現。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但使用AppConfig的好處是:作爲AWS雲原生服務,AppConfig跟我們的AWS服務集羣有很好的契合度,能方便獲得AWS技術團隊的支持,降低學習維護成本和使用成本。我們估算了配置中心的費用,主要包括 AppConfig API調用和S3費用。假設一個微服務部署兩個區域,啓動3個POD,配置文件大小爲10K,每天更新兩次配置,每分鐘輪詢一次AppConfig,那麼這個微服務使用配置中心的費用大約是0.6美元\/月。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"綜合考慮Freewheel的業務需求和使用成本後,我們採用了基於AWS AppConfig的配置中心架構。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"配置中心架構"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/8e\/8ed5e3d0e6196af2de64c308fb760a83.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"配置中心的核心模塊包括AWS AppConfig服務端、微服務客戶端、用戶界面。主要使用場景包括:"}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"各個微服務通過用戶界面管理配置:包括創建配置應用程序,向AWS S3讀寫配置文件, 通過AppConfig部署最新的配置,在數據庫中記錄用戶的操作歷史。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"各個微服務通過客戶端對AppConfig服務端進行定期輪詢,一旦發現配置更新,就從AppConfig服務端拉取配置並使之在微服務中生效。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"配置中心落地實現"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"AWS AppConfig服務端"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"AWS AppConfig是AWS開發用來創建、管理和快速部署應用配置的服務。 客戶端和用戶界面的實現與AppConfig提供服務的實體密切相關。AppConfig通過以下實體來管理應用配置:"}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"應用程序(Application):應用程序就是需要AppConfig提供配置管理的應用,如在EC2實例上運行的微服務,AWS Lambda的無服務器應用程序等等。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"環境(Environment):對於每個應用程序,可以定義一個或多個環境,例如 Staging 或 Production。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"配置(Configuration)和配置文件(Configuration Profile):每個環境下只有一個配置,配置文件就是記錄配置內容的文件對象。每次更新配置,實際更新的是配置文件的內容(版本)。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"配置策略(Deployment Strategy):配置策略定義了配置的部署方式,如部署節點是線性擴張還是指數擴張、部署時長、監控和回滾策略等。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下圖是Freewheel核心業務系統集羣的實際應用場景,由於Development、Staging 、Production三個環境的集羣互相隔離,我們爲不同環境的集羣創建了獨立的AppConfig服務端和用戶界面。微服務在用戶界面創建與之關聯的應用程序,這個應用程序僅包含一個環境。我們選擇了S3來存儲配置文件,可以通過用戶界面讀寫配置文件。目前配置中心在部署時使用的配置策略是每30秒部署50%的節點。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/b8\/b8e4b74d1ca5e2b3d539c1fda7173662.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"配置中心客戶端"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"客戶端是微服務進行配置輪詢和配置更新的重要組件。我們在配置中心客戶端做了災備處理,從而實現了微服務集羣對配置中心的弱依賴。即便配置中心的服務端或者用戶界面出現故障,微服務集羣的運行也並不受影響,只是不能使用配置管理的功能。配置中心客戶端的工作流程如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/93\/93a34ad4d603bae6ca7378722ab7c75a.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"微服務啓動後,我們會將備份配置文件加載到內存中,然後啓動一個Go Routine關聯配置中心,按照一定時間間隔來輪詢配置。如果發現配置更新,就把更新內容合併到內存配置和其他定製的配置中,否則等待下一次輪詢。客戶端使用Go語言開發,下面是關聯和查詢配置中心的示例代碼:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"func InitConfigCenter(serviceName string, callbacks ...CustomCallback) {\n\n    go func() {\n\n       for {\n\n           func() {\n\n               defer func() {\n\n               \/\/ 處理Panic\n\n               }()\n\n               appConfigClient := getAppConfigClient()\n\n               \/\/ 查詢配置更新,如有更新則使之生效\n\n               GetAndMergeAppConfig(appConfigClient, serviceName, callbacks...)\n\n               }()\n\n               time.Sleep(time.Duration(pollingDuration) * time.Second)\n\n           }\n\n    }()\n\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"參數serviceName:在服務端具有唯一性的標誌符,一般等同微服務名。管理人員在用戶界面需要輸入同樣的serviceName去創建AppConfig應用程序,然後客戶端和服務端通過該serviceName匹配應用程序。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"參數CustomCallback:微服務可定製的修改配置的接口。獲取配置更新後,客戶端會默認修改內存配置使配置生效。但有些配置不是從內存配置中讀取的,例如存儲在全局變量裏的配置,此時可以通過這個接口定製更新配置的方法。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"考慮到弱依賴的設計原則,客戶端內存配置的更新採用了合併策略(Merge)而非替代策略。初始內存配置從備份讀取,隨後從服務端不停拉取最新配置進行合併。服務端配置可以對內存配置進行全量覆蓋、部分覆蓋、或者新增配置。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/10\/10f2656052e31ff42be66202febc3164.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"客戶端的內存配置管理是基於Viper(\"github.com\/spf13\/viper\")實現的,合併配置時使用了Viper.MergeConfig方法:"}]},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"func mergeAppConfig(newConfig *appconfig.GetConfigurationOutput, callbacks ...CustomCallback) {\n\n        memoryConfig := config.Get()\n\n        if err := memoryConfig.MergeConfig(bytes.NewReader(newConfig.Content)); err != nil {\n\n        \/\/ 處理配置合併錯誤\n\n        }\n\n        \/\/ 更新內存配置\n\n        config.Set(memoryConfig)\n\n        \/\/ 更新定製配置\n\n        for i, callback := range callbacks {\n\n               callback.CustomCallbackFunc(newConfig)\n\n        }\n\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"客戶端的一個重要邏輯是配置版本的保存和比對。客戶端在本地存儲了之前輪詢獲得的服務端最新配置版本,每次調用AppConfig API查詢時都會輸入這個配置版本。AppConfig API會比較請求裏的配置版本和服務端最新的配置版本,兩者不一致時會返回最新的配置版本和配置內容,否則返回原來的配置版本。版本不一致時,調用API的費用會比平時高很多。客戶端收到服務端答覆後,再次比較本地和答覆裏的配置版本,如果不一致就會保存新的版本,並且進行配置合併。下面是調用AppConfig GetConfiguration API的代碼:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"import (\n\n        \"github.com\/aws\/aws-sdk-go\/aws\"\n\n        \"github.com\/aws\/aws-sdk-go\/aws\/session\"\n\n        \"github.com\/aws\/aws-sdk-go\/service\/appconfig\"\n\n)\n\nvar latestConfigVersion = \"-1\" \/\/ 第一次查詢時的初始版本\n\n \n\nfunc getAppConfig(region, env, clientId, configuration, applicationName string) (*appconfig.GetConfigurationOutput, error) {\n\n   awsConfig := &aws.Config{ Region: aws.String(region) }\n\n   mySession := session.Must(session.NewSession(awsConfig))\n\n   appConfigClient := appconfig.New(mySession, aws.NewConfig())\n\n   return appConfigClient.GetConfiguration(&appconfig.GetConfigurationInput{\n\n               Application: &applicationName,\n\n               Environment: &env,\n\n               Configuration: &configuration,\n\n               ClientConfigurationVersion: &latestConfigVersion, \/\/ 使用本地記錄的最新服務端配置版本\n\n               ClientId: &clientId,\n\n        })\n\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"GetConfiguration輸入包括AppConfig的應用程序名、環境名、配置名、配置文件版本和客戶端指定的唯一ClientId,輸出GetConfigurationOutput包括配置文件版本和配置內容(可選項)。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"配置中心用戶界面"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"配置中心用戶界面是用來統一管理各個微服務配置的窗口。我們在Freewheel內部業務數據查詢平臺Falcon上搭建了配置中心的用戶界面,僅允許LDAP賬戶開通配置中心的訪問或管理權限。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"AppConfig服務在AWS控制平臺也有自己的管理界面,但是不能滿足我們的需求。首先AWS控制平臺的權限管理比較嚴格,工程師團隊在生產環境只有部分讀權限,沒有寫權限,如果我們需要在AWS控制平臺上管理和部署配置,每次都需要單獨申請權限。另外AppConfig原生的管理界面比較簡單,不能看到具體的配置項內容,需要去相應的S3頁面下載配置文件,也不具備配置對比和查看用戶歷史操作的功能。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"配置中心用戶界面架構:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/2e\/2ec01f3d2b13180a96f013dcbedff42a.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"配置中心用戶界面包含了前端和後端模塊,前端模塊由React實現,包括以下頁面:"}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"主頁:展示所有微服務應用程序列表。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"應用頁面:展示單個微服務應用程序的詳細信息,由主頁進入。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"創建頁面:爲一個新的微服務創建應用程序,由主頁進入。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"配置上傳頁面:上傳新的配置文件,由應用頁面進入。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"配置部署頁面:選擇一個配置文件版本進行配置部署,由應用頁面進入。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"歷史記錄頁面:展示應用程序所有部署歷史和用戶,由應用頁面進入。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"後端模塊由Node.js實現,分爲配置管理和用戶管理兩個子模塊。在配置管理模塊調用JS SDK的AppConfig Client和S3 Client實現上述前端頁面功能;在用戶管理模塊實現了權限管理和歷史記錄功能,用戶的創建、上傳、部署行爲會被記錄到數據庫中。創建一個可用的AppConfig應用程序實際上包含了四個步驟:創建應用程序,創建環境,上傳初始配置文件,在應用程序中綁定配置文件。在應用程序中關聯配置文件後,會記錄配置文件的地址和版本。每次爲這個應用上傳並部署新的配置文件後,關聯配置文件的版本就會變動。在歷史記錄頁面可以看到歷次部署的狀態、開始時間、配置版本、部署時長和操作用戶,還可以對配置內容進行靈活對比。下面給大家展示一下配置中心的用戶界面。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Falcon平臺主頁:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/47\/47371a276dd40b18012653707206b013.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"配置中心主頁:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/f2\/f2c95b0ee6d4fdd64a8a10778ec7a244.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"配置中心歷史記錄頁面:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":" "}]},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/4f\/4fd00f3373eae7713c2a135c70329f92.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"落地常見問題"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在搭建配置中心的實戰過程中,我們踩了不少的坑,也總結了一些經驗。在這裏分享給大家,希望能對大家有所幫助。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"如何合理使用AppConfig服務使其收費最低?"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"GetConfiguration API是AWS AppConfig服務中最重要的API,通過輪詢這個API可以獲得配置版本變化信息和最新的配置項內容。該API請求所帶的參數ClientConfigurationVersion與服務端最新的配置版本一致時收費較低,否則收費較高。爲避免額外收費,客戶端一定要在本地存儲之前查詢的服務端最新的配置版本,在調用API時使用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"客戶端還需要注意一個邏輯,就是客戶端真實生效的配置版本不一定等同於服務端最新的配置版本,因爲客戶端從發現配置版本變化到啓動配置更新這一過程是可能出錯的。即使客戶端在配置更新過程出錯,也要保存出錯版本供下次調用使用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"如何獲取有效的配置文件版本?"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"AppConfig的配置文件版本等同於S3文件版本。但S3上傳配置文件和AppConfig部署配置不是一個事務操作,所以最新的S3文件版本不等同於AppConfig的有效配置文件版本。所以要獲取AppConfig最新生效的配置文件版本,不能調用S3 API,而是調用AppConfig ListDeploymentsCommand API,讀取返回列表中最新的配置版本。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"如何在本地開發環境調試AppConfig?"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在本地開發環境調試AppConfig時不能使用生產環境的IAM角色,可以使用一個AWS賬號的臨時憑證來發送AppConfig API請求:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"import (\n\n        \"github.com\/aws\/aws-sdk-go\/aws\"\n\n        \"github.com\/aws\/aws-sdk-go\/aws\/credentials\"\n\n        \"github.com\/aws\/aws-sdk-go\/aws\/session\"\n\n)\n\n \n\nfunc getAWSSession(region, env, accessKey, secretKey, awsSessionToken string) *session.Session {\n\n        awsConfig := &aws.Config{ Region: region }\n\n        if env == DEV_ENV {\n\n               creds := credentials.NewStaticCredentials(accessKey, secretKey, awsSessionToken)\n\n               awsConfig.Credentials = creds\n\n        }\n\n        return session.Must(session.NewSession(awsConfig))\n\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其中accessKey、secretKey、awsSessionToken來源於AWS CLI爲這個AWS賬號提供的臨時憑證信息,可在AWS控制平臺上用賬號登陸後實時查詢。不添加這個臨時憑證信息就會自動使用EC2默認或者配置的IAM角色憑證。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"如何合理配置AppConfig服務的讀寫權限?"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以Freewheel配置中心的客戶端和用戶界面爲例,客戶端需要發送大量AppConfig讀請求,用戶界面需要發送少量AppConfig讀寫請求。所以我們爲客戶端EC2的默認IAM配置了AppConfig讀權限,爲用戶界面EC2申請了特殊IAM角色併爲它配置了AppConfig讀寫權限。要使特殊IAM角色生效,需要修改K8S部署文件:"}]},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"\/\/ deployment.yaml\n\nspec:\n\n  template:\n\n    sepc:\n\n      serviceAccountName: {{ $.Your.Arn.Account.Name }}\n\n \n\n\/\/ serviceaccount.yaml\n\nannotations:\n\n  eks.amazonaws.com\/role-arn: {{ $.Your.Arn.Role.Name }}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"特殊IAM角色配置成功後,可以在POD裏查詢環境變量確認:AWS_ROLE_ARN,AWS_WEB_IDENTITY_TOKEN_FILE。使用特殊IAM角色,需要通過AWS STS獲取臨時憑證後再發送AWS服務請求。注意如使用JS SDK V3發送請求,則需使用v3.10或以上版本(否則不支持獲取憑證的功能),如下所示:"}]},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"\/\/ AWS JS SDK V3獲取憑證\n\nconst { AppConfigClient } = require(\"@aws-sdk\/client-appconfig\");\n\nconst { getDefaultRoleAssumerWithWebIdentity } = require(\"@aws-sdk\/client-sts\");\n\nconst { fromTokenFile } = require(\"@aws-sdk\/credential-provider-web-identity\");\n\nconst appconfig = new AppConfigClient({ credentials: fromTokenFile({\n\n        roleAssumerWithWebIdentity: getDefaultRoleAssumerWithWebIdentity() })\n\n});"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"使用特殊IAM角色遇到ExpiredTokenException怎麼解決?"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"EC2默認IAM的權限長期有效,特殊IAM角色的憑證是有期限的。如果在服務運行時遇到了ExpiredTokenException,需要審視一下AWS API Client的生命週期。如配置中心用戶界面,爲每次請求重新生成一個AppConfigClient來避免憑證過期。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"配置中心未來展望"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"配置驅動資源正在成爲雲計算的一個重要技術趨勢,即認爲不光是應用進程,與雲計算相關的所有資源都可以通過配置去驅動。這將令配置中心的雲端之路充滿變化和挑戰。未來我們也會繼續思考配置中心的其他應用模式,比如如何在雲服務平臺上與其他服務整合,如何去獨立支撐某些業務場景等等。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"作者簡介:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"孫自然 Lead Software Engineer,FreeWheel"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"畢業於北京大學計算機系,目前就職於Comcast FreeWheel核心業務團隊。研究方向爲微服務架構、雲計算、產品設計等領域,熱衷於新技術的探索與分享。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"系列文章推薦閱讀:"},{"type":"link","attrs":{"href":"https:\/\/www.infoq.cn\/theme\/87","title":null,"type":null},"content":[{"type":"text","text":"雲原生環境下的微服務實踐全解"}],"marks":[{"type":"underline"}]}]}]}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章