GitOps 入門指南:寫給 Kubernetes 使用者
談到 GitOps,許多團隊常把它簡化為「把 YAML 丟進 Git」或「在 CI Pipeline 裡面跑 kubectl apply」;更有團隊過早引入複雜的內部自研平台,結果交付流程沒變快,維運負擔反而翻倍。
要真正發揮 GitOps 的效益,關鍵在於貫徹 KISS(Keep It Simple, Stupid)、YAGNI(You Aren’t Gonna Need It) 與 Boring Technology(無聊技術) 原則,回歸交付流程的本質。
什麼是 GitOps
GitOps 一詞最早由 Weaveworks 於 2017 年提出,隨後在 CNCF OpenGitOps 工作小組中確立為四項核心原則。
簡單說,GitOps 要求團隊以宣告式描述系統的期望狀態,把 Git 當作唯一的事實來源。叢集內部常駐的代理人(如 Argo CD)會自動拉取最新設定,並透過控制迴圈持續比對、修正,確保線上狀態永遠與 Git 一致。
這套持續校正的迴圈機制,能為日常開發體驗帶來實質改善:
- 免除生產叢集存取憑證:開發者不必向維運團隊申請高權限憑證,日常作業無須依賴或記憶繁瑣的
kubectl指令。 - 以 PR 作為唯一發布介面:發布新版本只需向組態儲存庫發起 Pull Request,將映像檔標籤由
v1.0.0調整為v1.0.1。 - 程式碼審查兼具安全把關:團隊審查與主管核准成為線上發布的授權標準,免除繁瑣的跨系統流程。
- 透過 Git Revert 快速復原:當線上發生異常時,直接在版本控制平台上點選「Revert」按鈕合併,叢集代理人會在數十秒內將服務復原至穩定狀態。
- 變更紀錄兼具審計日誌:
git log提供了透明且難以竄改的歷史紀錄,清楚記錄變更人員、時間與具體修改內容。
常見誤區:你以為的 GitOps 可能不是 GitOps
最常見的混淆,是將「基礎設施即程式碼(IaC)版本化」誤認為 GitOps。把 Dockerfile 與 Kubernetes YAML 存進版本庫只是第一步。
若線上出事時依然允許工程師直接使用 kubectl edit 手動修改,或在雲端控制台調整環境變數,系統的實際運作狀態就已經與 Git 脫鉤。只要 Git 不是狀態變更的唯一合法途徑,它就只是一份靜態的歷史備份。
另一種常見做法,是在 CI 腳本中執行 kubectl apply -f k8s/。這本質上仍是 Push 模式的外部自動化部署,CI 腳本執行完畢即離線,無法持續感知線上發生的手動變更,也未形成自動修正狀態漂移的迴圈。
架構機制:Push 模式痛點與 Pull 模式的防禦力
要理解 GitOps 的工程價值,必須深入對比傳統 CI 推播模式(Push-based)與代理人拉取模式(Pull-based)的底層架構差異。
Push 模式在安全與維運上的缺陷
在 Push 模式中,CI 工具扮演主動發起者。當程式碼合併後,CI 伺服器打包映像檔,最後讀取儲存在 CI 變數中的 kubeconfig,直接呼叫遠端 Kubernetes API Server。
這種架構存在難以規避的安全與維運缺陷:
- 憑證外洩風險極高:CI 系統必須握有生產叢集的高權限憑證。一旦第三方 Action 或 Runner 出現漏洞,攻擊者便能竊取憑證並掌控生產叢集。
- 網路邊界穿透成本:企業 Kubernetes API Server 通常部署於封閉的 VPC 私有子網內。若要讓雲端 SaaS CI 進行 Push,就必須在防火牆打洞開通對外存取,或維護昂貴的私有 Runner。
- 無法感知狀態漂移:CI 只是觸發型任務,執行完指令後任務隨即結束。假若維運人員隨後手動修改了線上環境,CI 毫無察覺,直到下一次發布引發意外覆蓋或衝突。
Pull 模式的架構優勢與安全防線
Pull 模式在 Kubernetes 叢集內部常駐一個具備控制迴圈的 Operator(如 Argo CD 或 Flux),定期比對 Git 與現場狀態。
這種模式帶來了顯著的架構改進:
- 關閉外部入站連線:Kubernetes API Server 可對外網隱形,叢集內部 Operator 僅需對 Git 儲存庫建立單向出站連線(HTTPS 或 SSH),收斂暴露於外網的攻擊面。
- 安全邊界轉移至 Git:關閉 API 入站連線後,安全防線轉移到了版本控制平台。儲存庫的分支保護規則(Branch Protection)與審查門檻成為叢集的實質防火牆。
- 最小特權原則:CI 系統僅具備推送映像檔與發起 Git 變更的權限,完全不持有叢集管理憑證,落實安全隔離。
- 持續對齊與自我修復:Operator 預設每隔 1 至 3 分鐘主動比對狀態。一旦偵測到手動修改造成的狀態漂移,會立即強制重置回 Git 宣告的正確設定。
主流工具評析:Argo CD 與 Flux v2 直球對決
在 CNCF 的 GitOps 生態系中,Argo CD 與 Flux(Flux v2) 是最受矚目的兩大 Graduated 畢業級專案。兩者在架構哲學上有鮮明的取向差異。
架構哲學的對比
Argo CD 走的是視覺化控制台與協作中心路線。它具備直觀的 Web 介面與即時資源關係樹,原生整合各類 SSO 單點登入,並能直接在介面比對 Diff 與檢視 Log,大幅降低跨職能團隊的溝通門檻。
Flux v2 則全面擁抱 Kubernetes 原生 API 與 Unix 哲學。它沒有單一龐大的控制台,而是拆分為一組專注的微控制器(Source、Kustomize、Helm 等),記憶體佔用極低,適合邊緣運算或偏好純 CLI 工作流的資深團隊。
特性對比與務實選型
| 評比面向 | Argo CD | Flux v2 | 務實評析 |
|---|---|---|---|
| 視覺化 Web 控制台 | 完整強大,支援即時 Diff 與 Log | 無原生 UI,依賴社群第三方工具 | Argo CD 顯著降低值班與跨職能溝通成本 |
| 架構與 CRD 設計 | 自訂 Application 與 AppProject 模型 | 原生貼合 K8s API,高度模組化 | Flux v2 輕量彈性:微控制器架構便於客製裁減 |
| 上手門檻 | 平緩直觀,所見即所得 | 中等,需熟悉多個 Controller 與 CLI | Argo CD 較易推廣,業務工程師可自主檢視同步狀態 |
| 記憶體資源開銷 | 中等(約 500MB 至 1.5GB) | 極低(約 100MB 至 300MB) | 在一般雲端叢集中,此開銷差距通常可忽略 |
| 多租戶與 RBAC | 原生整合 SSO,支援細緻 UI/API 權限 | 依賴 K8s 原生 RBAC 與 ServiceAccount | Argo CD 更適合中大型團隊與複雜權限劃分 |
依據 Boring Technology 原則,推薦 90% 的常規研發團隊優先選擇 Argo CD。視覺化介面能讓開發、測試與維運在同一個畫面上溝通,省下的協作成本遠高於微小的資源開銷。僅在資源極端受限的單節點邊緣環境,或全體成員皆具備深度 Kubernetes 經驗的情境下,才優先採用 Flux v2。
NOTE
組態架構與環境劃分:Kustomize 的單一主幹實務
許多團隊在導入 GitOps 時,最嚴重的挫敗往往不是出在工具安裝,而是出在儲存庫結構劃分與分支策略錯誤。
核心原則:App Repo 與 Config Repo 徹底分離
將應用程式原始碼與 Kubernetes 部署組態拆分至獨立儲存庫,是避免流程踩雷的第一道防線:
- 避免 CI 死循環:若放在同一儲存庫,CI 建構新映像檔後將 Tag 寫回 YAML 並 commit,該次提交又會觸發下一次建構,造成無窮迴圈。
- 落實權限隔離:業務工程師可自由推送程式碼至應用庫;但組態庫涉及網路策略與資源配額,應透過審核機制設定嚴格權限。
- 保持語意歷史乾淨:應用程式庫記錄商業邏輯變更;組態庫記錄基礎設施狀態演進,職責涇渭分明。
別用 Git 分支區分環境
早年許多團隊習慣維護 dev、staging、prod 三個長期分支,並讓 GitOps 工具分別監聽不同分支。這是一個極度危險的反模式。
各環境必定存在設定差異(例如副本數量或特定網域)。當團隊嘗試跨分支合併時,這些特異設定會引發頻繁的衝突。
若依賴分支隔離環境,一旦線上緊急熱修復遺漏了跨分支 cherry-pick,各分支的設定基準便會逐漸分歧,最終失去一致性。
最佳做法:單一主幹配合 Kustomize 目錄隔離
務實的解法是:組態庫僅保留單一主幹 main 分支,利用 Kustomize 的 base/ 與 overlays/ 結構,在實體目錄上劃分環境。
Kustomize 的優勢在於遵循 KISS 原則,沒有複雜的模板語法,純粹透過補丁覆蓋特定欄位,且 kubectl 原生支援,無須架設額外的套件儲存庫。
標準目錄結構如下所示:
k8s-config-repo/
+-- apps/
+-- payment-service/
+-- base/
| +-- deployment.yaml # 通用基礎定義
| +-- service.yaml # 通用 Service 定義
| +-- kustomization.yaml # 宣告基礎資源
+-- overlays/
+-- dev/
| +-- kustomization.yaml # 覆蓋副本數與標籤
+-- staging/
| +-- kustomization.yaml # 指定測試設定
+-- prod/
+-- kustomization.yaml # 鎖定正式標籤與資源限額
+-- patches/
+-- replicas.yaml # 覆蓋副本數為生產規格
當 CI 完成映像檔建構後,可在工作流中透過 kustomize edit set image 更新對應環境目錄下的標籤。
為兼顧自動化與安全審查,實務上通常為 CI 配發受限的 GitHub App 或 Deploy Key,自動開立變更 PR 或在通過預檢後合併至 main 分支,叢集代理人隨後便會偵測並同步新版本。
機密資訊(密碼、金鑰、憑證)則絕不能以明文進入 Git——Kubernetes 原生 Secret 只是 Base64 編碼,並非加密。
實務上建議將機密存放在雲端託管服務(如 AWS Secrets Manager、HashiCorp Vault),再透過 External Secrets Operator(ESO) 同步至叢集,Git 裡只留路徑參照,不留敏感數值。
避開陷阱:五大常見反模式
在實踐 GitOps 的過程中,許多團隊容易陷入看似美好但實則致命的陷阱。
- 緊急破窗後未及時回補:線上故障時工程師手動修改資源,卻因 Auto-sync 覆蓋而引發循環故障;或關閉 Auto-sync 後遺忘重啟。正解:建立緊急破窗 SOP,手動介入後必須在 30 分鐘內於 Git 補上對應 PR 並合併。
- 頻繁將動態資料寫入 Git:將 HPA 計算的副本數寫回 Git,引發無窮迴圈。正解:Git 僅記期望狀態。若用 HPA 應自清單移除
replicas,或在 Argo CD 設定ignoreDifferences配合RespectIgnoreDifferences=true。 - 建構過於複雜的相依鏈條:在單一應用中同時部署 CRD、基礎設施元件與商業應用,導致載入順序混亂。正解:基礎設施與商業負載分層治理,保持相依關係簡單線性。
- 過早引入全自動金絲雀與回退:在業務規模尚小、流量不足時強推複雜的漸進式發布工具,常因微小波動導致發布卡死。正解:貫徹 YAGNI 原則,善用 Kubernetes 原生
RollingUpdate搭配完善的就緒探針(readinessProbe),足以解決絕大多數業務需求。 - 跟風平台工程:強行引入複雜的自製介面或內部開發者平台。正解:Git 本身就是最佳介面,善用成熟的 Pull Request 審查機制即可獲得極高可靠度。
結語:讓生產發布變得無聊且可靠
持續交付的核心追求,是讓生產發布變得足夠平凡且可預期。變更不再依賴工程師在半夜手敲指令,而是化為常規上班時間內的一張 PR,經程式碼審查後由叢集代理人平穩完成狀態同步。
NOTE
實踐 GitOps 不需要一開始就鋪設全套平台工程。先從一個無狀態服務著手,把組態儲存庫獨立出來、理清 Kustomize 環境目錄,讓交付流程在透明且可逆的軌道上運轉,便已經達成了八成的核心價值。回歸常識,堅守 KISS、YAGNI 與 Boring Technology,系統將比任何過度設計的架構都更加穩固長青。