2025年2月17日
GitOps 與 Argo CD:為什麼部署要從「推」改成「拉」
Push 模式的問題
最常見的 Kubernetes 部署方式,是在 CI Pipeline 的最後一步直接部署:
helm upgrade --install order-api ./charts/order-api -f envs/prod/values.yaml
這叫 Push 模式:由 Pipeline 把變更「推」進叢集。它可以運作,但隨著叢集與服務變多,問題會一一浮現:
- CI 系統要握有叢集的管理權限:每一個 Pipeline 都要有能改正式環境的憑證,CI 系統一旦被入侵,所有叢集都暴露
- 叢集的實際狀態沒人知道:有人半夜手動
kubectl edit調整了設定解決問題,事後沒有改回設定檔,下次部署時又被覆蓋回去,問題重現 - 「現在線上是什麼版本」要去叢集裡查:沒有一個地方可以一眼看出每個環境部署了什麼
- 回滾要重跑 Pipeline:而且要記得當時是用哪一個 commit、哪一份 values 部署的
GitOps:Git 是唯一的真相
GitOps 的核心想法是:叢集應該是什麼樣子,完全由 Git 上的檔案描述。OpenGitOps 定義了四個原則:
- 宣告式(Declarative):系統的期望狀態以宣告式的設定描述(Kubernetes Manifest、Helm values)
- 版本化且不可變(Versioned and Immutable):期望狀態存在 Git 中,每一次變更都有紀錄,可以追溯、可以回到任何一個版本
- 自動拉取(Pulled Automatically):由叢集內的代理程式主動從 Git 拉取期望狀態,而不是由外部推入
- 持續調和(Continuously Reconciled):代理程式持續比對「期望狀態」與「實際狀態」,發現不一致就修正
在 GitOps 的流程裡,CI 和 CD 被拆開了:
- CI 只負責建置、測試、推送映像,最後更新 Git 上的映像版本號(發一個 commit 或 PR),不碰叢集
- CD 由叢集內的 Argo CD 負責:它看到 Git 變了,就把叢集同步成 Git 描述的狀態
部署變成「改 Git」,回滾變成「revert 一個 commit」,稽核變成「看 Git log」。
Argo CD 的核心概念:Application
Argo CD 用一個 CRD Application 描述「把哪個 Git 路徑的內容,部署到哪個叢集的哪個 Namespace」:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: order-api-prod
namespace: argocd
spec:
project: trading
source:
repoURL: https://git.example.com/platform/deploy.git
targetRevision: main
path: charts/order-api
helm:
valueFiles:
- ../../envs/prod/order-api/values.yaml
destination:
server: https://prod-cluster.example.com
namespace: order
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
每個 Application 都有兩個狀態:
- Sync Status:叢集的實際狀態和 Git 是否一致——
Synced或OutOfSync - Health Status:部署出去的資源是否健康——Deployment 的 Pod 都 Ready 了嗎?
Healthy、Progressing、Degraded
同步策略:automated、prune、selfHeal
syncPolicy 決定 Argo CD 發現不一致時要做什麼:
| 設定 | 作用 | 沒開的話 |
|---|---|---|
automated | Git 有變更時自動同步 | 只標示 OutOfSync,要人按下 Sync |
prune: true | Git 上刪掉的資源,叢集裡也刪掉 | 資源會留在叢集裡變成孤兒 |
selfHeal: true | 叢集被手動修改時,自動改回 Git 的狀態 | 只標示 OutOfSync,不主動修正 |
selfHeal 是 GitOps 最有感的一點:有人手動 kubectl scale --replicas=8,Argo CD 幾秒內就會把它改回 Git 上的 replicas: 4。這逼著所有變更都必須走 Git——想改副本數,就去改 values 檔發 PR。
例外:和 HPA 一起使用時,副本數是由 HPA 動態決定的,Argo CD 不應該把它改回去。可以在 Application 中用
ignoreDifferences忽略 Deployment 的/spec/replicas,或是乾脆不在 Manifest 中設定replicas。
正式環境要不要開 automated,各團隊的選擇不同:有些團隊在正式環境只開 selfHeal 防止漂移,同步仍然由人在確認後按下,把「合併 PR」和「真正上線」拆成兩個決策點。
Sync Wave:控制資源的部署順序
一個應用程式可能包含資料庫 Migration Job、ConfigMap、Deployment,它們之間有先後順序:Migration 要先跑完,新版的程式才能啟動。Argo CD 用 Sync Wave 控制順序,數字小的先部署,等它健康了才進下一波:
metadata:
annotations:
argocd.argoproj.io/sync-wave: "-1" # Migration Job:先跑
---
metadata:
annotations:
argocd.argoproj.io/sync-wave: "0" # Deployment:後跑(預設 0)
搭配 argocd.argoproj.io/hook: PreSync 這類 Hook,還可以定義只在同步前或同步後執行的 Job,例如部署前的資料庫備份、部署後的冒煙測試。
多叢集、多環境怎麼規劃
當環境與服務一多,Application 的數量會快速增加。常見的規劃方式:
Helm Chart 共用,環境差異只放在 values
deploy/
├── charts/
│ └── order-api/ ← 共用的 Chart
└── envs/
├── dev/order-api/values.yaml
├── uat/order-api/values.yaml
└── prod/order-api/values.yaml
環境之間的差異(副本數、資源、網域、外部服務位址)集中在 values 檔,比對兩個環境的差異就是比對兩個檔案。
用 ApplicationSet 產生 Application
不需要為「每個服務 × 每個環境」手寫一份 Application。ApplicationSet 可以依 Git 目錄結構或叢集清單自動產生:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: order-api
spec:
generators:
- list:
elements:
- env: dev
cluster: https://dev-cluster.example.com
- env: prod
cluster: https://prod-cluster.example.com
template:
metadata:
name: "order-api-{{env}}"
spec:
project: trading
source:
repoURL: https://git.example.com/platform/deploy.git
targetRevision: main
path: charts/order-api
helm:
valueFiles: ["../../envs/{{env}}/order-api/values.yaml"]
destination:
server: "{{cluster}}"
namespace: order
新增一個環境,只要在清單中加一筆。
應用程式原始碼和部署設定分開兩個 Repo
CI 更新映像版本時會 commit 到部署設定的 Repo。如果兩者放在同一個 Repo,CI 的 commit 又會觸發一次 CI,很容易形成迴圈;權限上,能改程式碼的人也不一定該能改正式環境的設定。
使用情境:證券系統導入 GitOps
在證券系統導入 Argo CD 時,主要想解決的是「正式環境的實際狀態不透明」和「部署權限散落在各個 Pipeline」:
- 各服務的 Helm Chart 與各環境的 values 集中在部署 Repo,正式環境的每一次變更都是一個經過審核的 PR
- CI 只負責建置映像並更新 values 中的
image.tag,不再持有叢集的管理權限 - 正式環境開啟
selfHeal,手動調整會被自動修正,避免設定漂移 - 搭配 Centralized Pipeline Library,開發人員不需要自己撰寫部署腳本
一句話總結
GitOps 讓 Git 成為叢集期望狀態的唯一真相,CI 只負責產出映像並更新 Git,叢集內的 Argo CD 持續拉取、比對、同步;用 automated 自動部署、prune 清掉被刪除的資源、selfHeal 修正手動漂移,再用 Sync Wave 控制部署順序、ApplicationSet 管理多環境,部署、回滾、稽核都回到 Git 上完成。