一句話結論:把 ArgoCD 裝好之後,只手動 apply 一個 root Application,讓它掃描 Git 裡的某個目錄;之後要新增、修改、移除任何 App,都只要改 Git,不用再碰
kubectl apply。
遇到的問題
GitOps 的第一步,通常是為每個服務建立一個 ArgoCD Application。
服務少的時候沒問題。服務一多,就變成每新增一個,都要手動 kubectl apply 一份 Application 的 YAML。
這等於在 GitOps 裡留了一個沒被 GitOps 管的缺口:Application 本身不在 Git 的管理範圍內。
解法是 App of Apps:用一個 Application,去管理其他所有 Application。
這篇用最小的例子,從安裝 ArgoCD 開始,走完整個流程。
手把手步驟
步驟一:用 Helm 裝 ArgoCD
前提是手邊已經有一個 Kubernetes 叢集,minikube、kind 或雲端的都可以。
helm repo add argo https://argoproj.github.io/argo-helm
helm repo update
helm upgrade --install argocd argo/argo-cd \
--namespace argocd --create-namespace \
--wait --timeout 15m
確認 ArgoCD 起來了:
kubectl -n argocd get pods
kubectl -n argocd rollout status deploy/argocd-server
官方的 Getting Started 是用 kubectl apply 安裝 manifest。這裡改用 Helm,之後想調整參數(資源限制、Ingress 等)比較方便,也方便升級。
步驟二:登入 UI 看一下
最簡單的方式是 port-forward:
kubectl port-forward svc/argocd-server -n argocd 8080:443
然後用瀏覽器打開 https://localhost:8080(憑證是自簽的,瀏覽器會警告)。
帳號是 admin,初始密碼這樣取:
argocd admin initial-password -n argocd
沒有裝 argocd CLI 的話,也可以直接讀 Secret:
kubectl -n argocd get secret argocd-initial-admin-secret \
-o jsonpath='{.data.password}' | base64 -d; echo
官方文件提醒:登入並改完密碼之後,要把
argocd-initial-admin-secret刪掉。這個 Secret 只是初始用,留著沒有意義。
步驟三:準備 Git repo 的結構
建一個 Git repo,結構長這樣:
gitops/
├── bootstrap/
│ └── root-app.yaml # 手動 apply 一次
└── apps/ # root 會掃描這個目錄
└── guestbook.yaml # 一個子 Application
兩個目錄要分開:bootstrap/ 放只需要手動套用一次的東西,apps/ 放 root 要持續掃描的東西。
不要把 root 自己放進 apps/,否則會變成 root 在管理自己,行為難以預期,也很難理解。
步驟四:寫一個子 Application
apps/guestbook.yaml,使用 ArgoCD 官方的範例 repo,大家都能拿到:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: guestbook
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
repoURL: https://github.com/argoproj/argocd-example-apps.git
targetRevision: HEAD
path: guestbook
destination:
server: https://kubernetes.default.svc
namespace: guestbook
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
幾個欄位值得說明一下。
namespace
Application 這個物件本身預設要放在 argocd namespace。
spec.destination.namespace 才是這個 App 的資源要部署到哪裡。兩個很容易搞混。
finalizer
resources-finalizer.argocd.argoproj.io 的作用是:刪除這個 Application 時,連它部署出來的資源也一起刪掉(預設是前景的 cascading deletion)。
沒有加的話,刪掉 Application 只會移除 ArgoCD 的紀錄,資源還留在叢集裡。
syncPolicy
automated 打開自動同步。prune: true 代表 Git 裡移除的資源,叢集裡也會被刪掉。selfHeal: true 代表有人手動改了叢集內容,ArgoCD 會把它改回 Git 的樣子。
CreateNamespace=true 讓 ArgoCD 在目標 namespace 不存在時自動建立。
步驟五:寫 root Application
bootstrap/root-app.yaml:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: root
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
repoURL: https://github.com/<你的帳號>/gitops.git
targetRevision: main
path: apps
directory:
recurse: true
destination:
server: https://kubernetes.default.svc
namespace: argocd
syncPolicy:
automated:
prune: true
selfHeal: true
這份 YAML 跟子 Application 長得幾乎一樣,差別有兩個。
path: apps 加 directory.recurse: true
path 指向要掃描的目錄。ArgoCD 預設只讀該目錄第一層的檔案,要往下一層一層讀,要設 recurse: true。
所以之後可以在 apps/ 底下用子資料夾整理,root 一樣讀得到。
destination 的 namespace 是 argocd
因為 root 部署出來的東西就是「Application 這種物件」,而它們預設要在 argocd namespace。
把這個 repo 推到 Git,repoURL 換成自己的。如果是私有 repo,要先在 ArgoCD 設定 repo 的存取憑證。
步驟六:套用 root,看它長出子 App
kubectl apply -f bootstrap/root-app.yaml
這是唯一一次要手動 apply 的東西。
看一下結果:
kubectl get applications -n argocd
預期會看到 root 與 guestbook 兩個,狀態慢慢變成 Synced 與 Healthy:
NAME SYNC STATUS HEALTH STATUS
guestbook Synced Healthy
root Synced Healthy
ArgoCD 預設大約每 3 分鐘輪詢一次 Git,所以 push 之後可能要等一下。急的話,在 UI 上按 Refresh 就會立刻檢查。
步驟七:新增、移除 App,只改 Git
要新增一個 App,在 apps/ 加一個 Application 的 YAML,commit、push。
等下一輪輪詢(或按 Refresh),root 就會偵測到新檔案,建立對應的 Application,新的 App 再各自同步。
要移除,把檔案刪掉再 push。因為 root 有 prune: true,那個 Application 會被刪除,加上子 Application 的 finalizer,它部署的資源也會一起清掉。
全程不需要再對叢集下 kubectl apply。
踩到的坑
1. 刪掉 root,會連帶刪光所有子 App 和它們的資源
root 有加 finalizer,所以刪除它會級聯往下刪:先刪子 Application,子 Application 因為也有 finalizer,再刪它們部署的資源。
換句話說,一個 kubectl delete application root,就能把整個叢集的服務清空。
官方的 Cluster Bootstrapping 文件有專門一節在講這件事。
如果不想要這種風險,可以考慮不給 root 加 finalizer,或是關掉 root 的 prune。取捨是:以後刪除 root 或移除子 Application 時,要自己處理殘留的資源。
2. 子 Application 本身的設定,是由 root 負責同步的
這個坑很隱蔽。假設改了某個子 Application 的設定(例如 plugin 的環境變數、syncOptions),push 之後,如果 root 還沒同步這個改動,子 Application 會繼續用舊的設定渲染,狀態還顯示 Synced,但新開的資源就是沒有出現。
我就是這樣被騙過:看起來一切正常,卻怎麼等都沒有東西。
處理方式是:先確認 root 已經是 Synced,再對子 Application 做一次 hard refresh:
kubectl -n argocd annotate application <名稱> \
argocd.argoproj.io/refresh=hard --overwrite
hard 的意思是連 manifest 快取都清掉再重新評估。
3. recurse: true 會讀到目錄裡所有的 YAML
root 掃描的目錄裡,只應該放 Application 的 manifest。
如果裡面混了其他 YAML(文件、範例、值檔),ArgoCD 會把它們都當成要部署的資源,可能直接報錯,或是意外建立不該存在的東西。
把「手動套用一次的東西」和「持續掃描的東西」分在不同目錄,就是為了避免這個問題。
為什麼這樣選
官方的 Cluster Bootstrapping 文件,推薦的其實是 ApplicationSet,App of Apps 被列為替代方案。
ApplicationSet 適合「很多個長得很像的 App」或「要部署到很多個叢集」,用 generator 一次產生一堆 Application,不用寫一堆 YAML。
我選 App of Apps 的原因很單純:
- 一個 App 一個檔案,看檔案就知道它的設定,容易讀。
- 每個 App 的設定可以各自不同(有的自動同步,有的手動)。
- 概念簡單,不需要先學 generator 和模板語法。
單一叢集、App 數量不多、每個差異不小的情況,這樣就夠了。要管很多叢集或很多相似的 App,可以直接看 ApplicationSet。
另外有一個官方的安全警告值得記住:App of Apps 是管理員專用的工具。
能在任意 Project 底下建立 Application,等於是管理員等級的權限。所以只有管理員能對 root 所指向的 repo 推送,改動也應該經過審核。
延伸閱讀
- Argo CD:Cluster Bootstrapping:App of Apps 的官方說明、Helm 範例、級聯刪除,以及 ApplicationSet 的推薦做法。
-
Argo CD:Automated Sync Policy:
prune、selfHeal,以及輪詢間隔(預設 120 秒加最多 60 秒的抖動)。 -
Argo CD:App Deletion:
resources-finalizer.argocd.argoproj.io與級聯刪除的行為。 -
Argo CD:Directory Application Source:
directory.recurse的用法。 - Argo CD:Getting Started:初始管理員密碼、port-forward 與登入。
-
Argo CD:Annotations and Labels:
argocd.argoproj.io/refresh的normal與hard。 -
argocd-example-apps:官方範例 repo,裡面的
guestbook就是本篇用的,apps目錄是官方的 App of Apps 範例。 - argo-helm:argo-cd chart:用 Helm 安裝與調整參數。
這篇用的是 ArgoCD 的 stable 文件與 argo-helm 的 argo-cd chart。版本不同的話,記得對一下欄位名稱。
Top comments (0)