創建一個成熟的GitOps流水線,需要做哪些決定?
在軟件交付領域,GitOps是近期的熱門趨勢,它沿襲并擴展了DevOps、基礎架構即代碼和CI/CD等趨勢。
GitOps的優勢可以簡單地歸納如下:
- 自由地審計更改
- 持續集成和交付
- 更好地控制更改管理
然而,現實情況卻是構建GitOps流水線并非易事,它涉及到許多大大小小的決定,而這些決定會給實施工作帶來許多麻煩。我們將這些決定稱為“GitOps架構”,它可能會導致實施過程中面臨許多挑戰。
好的方面是只要有一定的規劃和經驗,就可以大大減少過渡到GitOps交付模式的痛苦。
在本文中,我將通過一家公司的故事來解釋其中的一些挑戰。這家公司從一個零散的小創業公司采用GitOps,成長為一家規范的跨國企業。雖然這種加速成長的情況很少見,但它確實反映了大型組織中的許多團隊從概念驗證,到最小可行性產品(minimum viable product),再到成熟系統的經驗。
簡單的開始
如果你剛剛開始,最簡單的做法是創建一個單一的Git repo,將所有需要的代碼都放在里面。其中可能包括:
- 應用程序代碼
- Dockerfile,用于構建應用程序鏡像
- 一些CI/CD流水線代碼(例如GitLab CI/CD或GitHub
- Actions)
- Terraform,以配置運行應用程序所需資源
此外,所有的更改都是直接對master進行改動,因此更改可以直接生效。
這一方法的主要優勢在于你有單一的參考點,以及所有代碼都會緊密集成在一起。如果您的所有開發人員都受到完全信任,并且速度就是一切,那么這一方法會持續生效。
不幸的是,隨著你的業務量不斷增長,這種方法的弊端很快就會顯現出來。
首先,隨著越來越多的代碼被添加到代碼庫中,代碼庫規模的膨脹會使得工程師們陷入困惑,因為他們會遇到更多必須解決的更改之間的沖突。如果團隊成員大幅增長,那么隨之而來的重新分配工作或合并會導致進一步的混亂。
其次,如果您需要分開控制流水線運行,則會遇到困難。有時,您只想快速測試對代碼的更改,而不是進行部署以實現完整的端到端交付。這種方法在整體性方面產生了越來越多需要解決的問題,隨著這些更改的進行可能會影響其他人的工作。
第三,隨著您的成長,您可能會希望工程師和團隊之間的責任邊界更加細化。這可以通過一個單一的repo來實現,并且一個repo通常是一個更清晰和更干凈的邊界。
分離Repository
隨著業務的增長,流水線會越來越擁擠,merge開始變得痛苦。此外,您的團隊也需要專業化,將不同的責任領域劃分給不同的成員。
所以你需要將Repo分離出來。這時,你首先要面對大量的決定,譬如repo應該分離到什么程度?是否需要為應用程序代碼建立一個單獨的repo?看起來是不是很合理?然后把Docker構建的東西也一起放進去?那這樣的分離其實沒有什么意義。
那所有團隊的Terraform代碼呢?應該放在一個新的repo里嗎?這聽起來很合理,但是:新創建的中央“平臺”團隊想要控制對AWS中核心IAM(身份和訪問管理)規則定義的訪問,而團隊RDS配置代碼也在其中,開發團隊需要定期對其進行調整。
所以你決定將Terraform分離成兩個repo:一個是“平臺”repo,一個是“特定應用程序”repo。這就帶來了另一個挑戰,因為你現在還需要分離Terraform的狀態文件。這并不是一個無法解決的問題,但這也并不是您所習慣的快速功能交付,所以產品經理將不得不解釋為什么功能請求所需的時間比之前更長。
不幸的是,這些GitOps決策還沒有既定的最佳實踐或模式。
分離的問題還不止于此。以前,流水線內構建的組件之間的協調是微不足道的(因為所有需要的組件都是共處的),而現在你必須協調repo之間的信息流。例如:當構建一個新的Docker鏡像時,這可能需要觸發集中式平臺repo中的部署,同時將新的鏡像名稱作為觸發的一部分傳遞過來。
同樣,這些也不是無法解決的問題,但在構建GitOps流水線的早期,這些挑戰更容易實現。后來,當步驟、政策和流程更加成熟時,再做出改變就需要付出更多的時間代價。
分布式vs集中式
你的業務正在增長,你正在構建越來越多的應用程序和服務。越來越明顯的是,在如何構建和部署應用程序方面,你需要某種結構上的一致性。中央平臺團隊需要嘗試執行這些標準。但是你可能會遭到開發團隊的反對,他們認為與在DevOps和GitOps出現之前,在集中式IT中他們被賦予了更多的自治和控制權。
如果上述情況您覺得似曾相識,那可能是因為GitOps和應用架構領域的單體與微服務爭論之間有些類似。就像你在這些爭論中看到的那樣,隨著系統的成熟,規模和范圍的擴大,分布式和集中式IT之間的緊張關系會越來越頻繁地出現。
從某種層面上來說,你的GitOps流程就像其他分布式系統一樣,如果你設計得不好,其中一個部分出現問題可能會產生難以預料的問題。
環 境
在你決定分離repo的同時,你意識到你需要一種一致的方式來管理不同的部署環境。而直接上線已經行不通了,因為此時需要QA團隊,在上線之前測試更改。
現在你需要為你的應用鏡像在測試和QA環境中指定不同的Docker標簽,你可能還希望在不同的環境中啟用不同大小的實例大小或副本功能。你如何在源碼中管理這些不同環境的配置?一個比較直接的方法是為每個環境建立一個單獨的Git倉庫(如:super-app-dev,super-app-qa,super-app-live)。
分離repo有 “涇渭分明” 的好處,就像我們在上面劃分Terraform代碼時看到的那樣。然而,很少有人最終會喜歡這種解決方案,因為大多數團隊不具備Git知識和相關專業水平,進而在不同的repo之間移植更改。更為復雜的是,repo之間必然會存在很多重復的代碼,而且隨著時間的推移,也可能會出現很多漂移(drift)。
如果你想把事情保持在一個單一的repo中,你至少有三種選擇:
- 每個環境都有一個目錄
- 每個環境都有一個分支
- 每個環境有一個標簽
同步步驟選擇
如果你嚴重依賴YAML生成工具或模板,您可以考慮另一種方式。例如,Kustomize強烈鼓勵基于目錄的環境分離。如果您使用的是原始YAML,那么分支或標記的方法會更適合您。
運行時環境顆粒度
然而,在您的運行時環境中,可以選擇您想要什么級別的分離。在集群層面,如果您使用的是Kubernetes,你可以在以下幾種情況下選擇:
- 一個集群管理所有
- 每個環境一個集群
- 每個團隊一個集群
在極端情況下,你可以把所有的環境放到一個集群中。不過通常情況下,在大多數組織中至少有一個單獨的集群用于生產。
一旦你想好了你的集群策略,在命名空間層面,你仍然可以選擇:
- 每個環境都有一個命名空間
- 每個應用程序/服務擁有一個命名空間
- 每個工程師擁有一個命名空間
- 每個構建都有一個命名空間
平臺團隊通常從 “dev”、“test”、“prod” 的命名空間設置開始,然后才意識到他們想要更精細地分化團隊的工作。
你也可以混合和匹配這些選項——例如,為每個工程師提供"desk testing "命名空間,以及每個團隊的命名空間。
總 結
我們在這里只是對成熟的GitOps流程所需的決策領域做了一些簡單的介紹。如果您的企業真的成長為那家跨國企業,你也可以考慮RBAC/IAM等要求。
通常情況下,推出GitOps會讓人覺得只是一種投資,可能最終沒有太多令人滿意的產出。但是在GitOps之前,團隊常常會經歷混亂和延遲,因為沒有人能夠確定任何東西應該是什么狀態。這些都會導致二次成本,因為審計人員會進行抽查,而因意外和未記錄的更改而導致的中斷則占據了員工大量的注意力,這是一個很高的成本。
然而,隨著你的GitOps流程的愈發成熟,好處倍增,該流程會解決許多之前存在的問題。但更多的時候,你面臨的壓力是要更快地展現出GitOps流程的優勢。
目前GitOps最大的挑戰是,沒有既定的模式或是最佳實踐來指導你的選擇。GitOps顧問,通常也只是引導團隊找到最適合他們的解決方案,并根據經驗將團隊往某些方向引導。
但我觀察到的情況是,早期因為看起來 “太復雜”而被放棄的選擇,往往后來都會為此后悔。這并不意味著你應該直接跳到為每個構建創建一個命名空間,每個團隊擁有一個Kubernetes集群的程度,原因有二:
- 每當你給GitOps架構增加復雜性時,你最終都會增加交付一個可行的GitOps解決方案的成本和時間
- 你可能真的永遠都不需要這種設置
在我們接受這個領域的可行標準之前,正確的 GitOps 架構永遠是一門藝術,而不是科學。