大型專案程式配置管理演化之路

thoughtworkers發表於2016-07-26

前言

CI/CD的精髓在於持續,持續意味著自動化。我們希望自己的程式可以無縫的自動部署在各種環境各個機器上,然而環境的不一致、配置的不同需求,需要我們引入不同的策略來實現不同環境的持續交付。不管是傳統企業,還是網際網路公司,現在已經或多或少的實現了自動化部署,但是也或多或少的存在著需要手動干預的步驟,減少手動干預可以有效縮短部署時間,減少出錯。自動化部署中不一致的指令碼和策略,也會增加出錯的概率,或者根本就是錯誤的設計。今天我們就來聊聊程式的配置管理,看我們如何在軟體發展的各個階段實現更好的程式配置管理,來支撐持續交付。

引入

配置管理(注意:沒有”程式”二字)定義了程式執行所依賴的一切,及它們之間的唯一關係。包括程式配置、依賴管理(第三方類庫等)、環境配置(包括作業系統、網路等等,像Docker、SDN),涵蓋除程式原始碼生成物之外的一切程式執行的依賴(甚至可以包括主機型號、所需要的電壓電流),然後我們可以這樣定義它們之間的關係:一個特定的程式,在特定的版本和環境下的配置是什麼樣的。 詳細可以參考《持續交付》這本書,它的範疇很大,所以這裡我們只討論其中的一部分,就是程式配置管理,像連線資料庫、上下游依賴,等等。程式配置管理在不同的時期、不同的專案複雜度中可以使用不同的實踐。接下來,就是演進式的介紹不同的配置管理方法。有的方法優雅,但是可能需要更多的維護成本。有的簡單粗暴,但是很適合小專案。希望這些可以對你,在做方案選擇時,有一些幫助。

正文

第一階段:基於檔案的程式配置管理

在專案發展的初期,我們通常會把程式配置放到各種配置檔案裡,app.exe.config, *.ini,xml檔案等等。這種方式特別簡單,程式可以直接讀取配置檔案,各種語言都有類庫支援這種配置檔案的讀寫,這也是現階段大部分程式的實現方式。 一起來看一下在基於檔案的配置管理下有哪些方法: 專案初始化後,就有了本地的配置檔案。很快,我們有了第一次上線。生產環境的配置會和本地開發一樣嗎?顯然不會。怎麼辦?於是你可能會想到以下幾種方案:

I: 不同環境不同的配置檔案,不同的生成包 需要準備不同環境的配置檔案,使用自動化指令碼打包的時候,可以把不同的檔案打到不同的包裡,如圖:

1-different-packages

這樣,通過維護不同環境的配置檔案來實現不同環境的不同配置,打包和部署指令碼也非常簡單,不需要任何的複雜邏輯。但是,這個方案看看就罷了。每個環境有自己的包,這個是強烈不推薦的,因為每個環境都有自己獨立的二進位制包,這意味著:你在測試環境所測試的包,可能會跟生產環境的二進位制包不同,不管是什麼原因導致的。我們更加期望,在整個過程中只要一份二進位制檔案,而不同環境的差異僅僅是配置檔案。

II: 不同環境不同的配置檔案,相同的包 還是有多個配置檔案。但是,我們是在部署的時候,用不同的環境配置檔案替換預設的檔案。如圖所示:

2-one-package

這樣,我們實現了只有一個生成包,保證了不同環境程式程式碼的一致性,不同環境的差異體現在配置檔案上。因為部署指令碼會改變程式的執行環境,所以,這個時候一定要做部署後的Smoke測試。我們專案裡有很好的實踐就是HealthCheck,所有的程式都有一個不需要登入就可以訪問的HealthCheck介面,用於顯示程式所依賴的上下游服務、資料庫是否可用。每次部署完畢通過HealthCheck做Smoke測試來保證部署的正確性。 這個方案已經可以滿足一些小的專案需要了,滿足了不同環境下的自動化部署需求,部署指令碼非常簡單,所以專案開始時可以採用這種方式。

隨著配置複雜性的慢慢提高,這個方案也逐漸暴露出一些缺點:配置檔案中有很多重複項,不同環境的配置檔案很多是重複的、公用的,並且要部署一個新的環境就需要增加一套配置檔案。當然,具體有一些技術可以部分解決重複配置的問題,比如配置檔案繼承,或者分塊。有些則很難解決,例如有時候我們需要部署兩套環境,但是區別只在於服務的埠號,也就是重複的粒度更小了,用配置檔案本身的機制已經不足以滿足需求了。所以,我們需要提供一個更靈活的修改管理配置檔案的機制。這裡,我們引入了另一個方案。

III. 使用同一個配置檔案,為不同的環境提供不同的變數 在程式和配置檔案的基礎上,增加了一個變數層,安裝指令碼負責把相應的變數寫到配置檔案中。例如你可以在指令碼中定義{“Dev”:{“DB”:”localhost”},“Prod”:{“DB”:”10.18.0.10”}}, 或者為不同的環境定義單獨的變數檔案(eg,dev.ps1, prod.ps1), 在不同的環境部署中會載入不同的變數檔案,安裝指令碼把變數的值寫到配置檔案中。這樣使得重複達到了最小限度。如圖:

3-different-variables

與第二個方案相比,這個方案的部署指令碼變得複雜了,但是整個結構也更加合理。它很好的解決了配置檔案重複以及新增環境複雜的問題。基本這個方案可以解決我們的大部分問題,這也是目前我們正在開發的一個已經進行了七年之久的主要部署方案。因為引入了變數檔案,所以部署更靈活。變數可以重用,繼承,組合。

到現在為止,我們的軟體配置管理還是基於檔案的。當需要維護的機器數量到一定量級,分散在各個機器,各個程式上的配置檔案的維護成本就變高了。當然,自動化部署,指令碼化等技術,可以減緩這種不足。但是問題依然存在,當因為配置不當引起產品問題後,排查就變得非常困難,需要在不同的機器,不同的地方檢視這些檔案的配置是不是正確。就跟所有軟體設計的驅動力和方法一樣。當軟體體量越來越大,我們需要通過分層或者模組化來簡化軟體的複雜性,使其更容易理解和維護。基於服務的配置管理就是在這樣一個背景下產生的。

第二階段:基於服務的配置管理

配置管理作為單獨的服務存在,負責提供一切程式需要的配置資訊。有了服務配置管理,程式只需要說,”我需要DB服務”,配置管理服務就返回給程式DB IP;“我需要某個service”,配置管理服務就返回這個service的地址。並且,配置管理服務可以做的更多,比如負載均衡、更容易的伺服器增減等等。在不同環境的程式中,只需要知道配置管理服務的地址,其他的服務地址都由配置管理來集中管理。 是不是很簡單?是,也不是。程式在架構上更清晰。但是,程式還需要適配新的配置管理方式,以前它就是簡單的從config中讀取。現在需要跟配置服務互動,可能需要自己實現一些介面。 結構如圖:

4-different-services

這樣分散在各個系統中的配置資訊就可以集中管理了,同時可以增加配置檢查功能,對配置的值做初步的校驗,防止簡單的拼寫錯誤。配置備份也會容易,方便快速搭建環境和恢復業務。這種方式也是現在一些新專案中使用的方式。

當然,兩種方式有不同的優缺點。最後,我們來比較下:

檔案配置 服務配置
適用場景 服務之間依賴簡單,機器數量少。 依賴複雜,機器數量多
複雜性 簡單,不需要額外的軟體支援 需要維護相應的配置服務管理,需要程式適配新的配置讀取
可維護性 較差,配置檔案散落在各處 配置集中管理

最後通過上面的表格可以看到,兩者都有自己的缺點和優點。所以,沒有最好的,只有最合適的。在不同的階段,使用不同的策略。建議開始階段先使用檔案的方式,後期可以切換到服務上。所以,通常我們在做軟體架構時,配置讀寫都會基於介面,這樣方便以後切換。

配置和程式是緊緊配合在一起的。我們曾遇到過許多次這樣的情況,程式在本地執行完美,可是到線上就啟動不了。我們不得不花費大量的時間去做排查,這都使我們抓耳撓腮,最後才恍然大悟,一個簡單的配置錯誤,可能僅僅是一個字元而已。程式設計師常常有“配置沒有程式重要”的想法,改改配置是很多程式設計師不屑去做的事情,但是,從終端使用者的角度來看,配置和程式碼哪個不工作都是問題,只有工作的軟體才是最終交付的價值。

相關文章