iT邦幫忙

2026 iThome 鐵人賽

DAY 18
0
Kubernetes

從零到一:使用 K8S + GitOps 打造異構技術棧的資料分析平台系列 第 18

[Day 18] ArgoCD 實戰:安裝與設定 GitOps 中心 —— 實戰安裝 ArgoCD 並與 GitHub 建立安全連結。

  • 分享至 

  • xImage
  •  

Day 18: ArgoCD 實戰:安裝與設定 GitOps 中心

實戰安裝 ArgoCD 並與 GitHub 建立安全連結。

1. 安裝 ArgoCD

安裝本身出乎意料地簡單:開一個專屬 Namespace,套用官方安裝檔。

kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml --server-side

提醒:ArgoCD 的 CRD 大到會超過 annotation 的長度限制,--server-side 這個參數不加的話,apply 會直接失敗,錯誤訊息是 metadata.annotations: Too long。原因是 client-side apply 會把整份原始 YAML 塞進 last-applied-configuration 這個 annotation 裡,而 annotation 有 256KB 上限。

還有一件事上線前要先想清楚:stable 這個標籤是浮動的。上面那行安裝指令抓的是「當下最新的穩定版」,代表你今天裝的跟三個月後同事裝的可能是不同版本。真的要進 production,把它換成明確的版本號:

kubectl apply -n argocd --server-side \
  -f https://raw.githubusercontent.com/argoproj/argo-cd/v3.3.9/manifests/install.yaml

畢竟我們整個系列都在講「Git 是唯一真理」,結果連 GitOps 工具自己都用浮動版本安裝,那就有點好笑了。

2. 裝完先驗,三個檢查

kubectl apply 沒報錯不等於裝好了——這句話等一下會用一個真實案例打我自己的臉。先看三個檢查:

https://ithelp.ithome.com.tw/upload/images/20260820/20182549NiZ55ADt0t.png

▲ 安裝驗證:七個元件、三張 CRD、以及 UI 走的那個 Service

檢查一:七個元件都要 Running

ArgoCD 不是一個服務,是一組各司其職的 controller:

元件 負責什麼
application-controller 核心。持續比對期望狀態與實際狀態、執行同步、評估健康度。selfHeal 就是它在動手
repo-server 把 Git clone 下來、把 Helm Chart 或 Kustomize 渲染成純 YAML。Day 20 會提到的「ArgoCD 底下沒有 Helm release」就是因為它只做 helm template
server API Server + 那個章魚 UI,也負責認證授權。它不參與同步決策
redis 快取渲染結果與資源樹。掛掉不會弄壞叢集,只是每次都要重算,UI 會變慢
dex-server SSO 的轉接頭,要接 GitHub / Google / LDAP 登入才需要它
notifications-controller 依 App 狀態變化送通知(Slack、Webhook、Email)
applicationset-controller 用產生器批次生出 Application,管很多環境時才會用到

搞懂這張表最實際的好處是:出事時知道要看哪個 Pod 的日誌。同步卡住看 application-controller,Git 拉不下來或 Helm 渲染失敗看 repo-server,UI 進不去看 server。

檢查二:CRD 要齊

ArgoCD 靠三張 CRD 擴充 K8S 的 API:applications(部署單位)、appprojects(權限邊界)、applicationsets(批次產生器)。少一張,對應的 controller 就活不了。

檢查三:確認 UI 走哪個 Service

argocd-server 同時開了 80 和 443。443 是自簽憑證的 HTTPS——第 5 節那個坑就是從這裡長出來的。

我自己就沒通過檢查一

上面那張圖是修好之後拍的。實際上我第一次認真跑這套驗證時,看到的是這樣:

argocd-applicationset-controller-7878b5cc9f-pkqzl   0/1   CrashLoopBackOff   4181 (68s ago)   96d

重啟四千一百八十一次,持續九十六天。 我完全不知道,因為 ArgoCD 該做的事它都在做——同步正常、UI 正常、部署正常。

照 Day 29 的排查順序,直接看日誌:

kubectl logs -n argocd deploy/argocd-applicationset-controller --tail=5
"error":"failed to get restmapping: no matches for kind \"ApplicationSet\" in version \"argoproj.io/v1alpha1\"",
"msg":"if kind is a CRD, it should be installed before calling Start"

原因如下:applicationsets.argoproj.io 這張 CRD 根本不在叢集裡。controller 啟動、找不到自己要管的資源類型、超時、死掉、重啟,循環了九十六天。回頭對照檢查二,kubectl get crd 的清單裡確實只有 applicationsappprojects 兩張。

補上那張 CRD 就好了:

kubectl apply --server-side \
  -f https://raw.githubusercontent.com/argoproj/argo-cd/v3.3.9/manifests/crds/applicationset-crd.yaml
kubectl -n argocd rollout restart deploy/argocd-applicationset-controller

這件事有兩個教訓。第一個是技術面的:CRD 和使用它的 controller 是兩份東西,--server-side 的分批 apply 有可能只成功一半,而 K8S 不會為此報錯。第二個更重要——壞掉的元件如果剛好沒人用,它可以壞很久很久。這跟 Day 29 會講的那種「沒有任何 Pod 在哭」的故障是同一個家族:不是沒有訊號,是沒有人在看。

所以裝完真的要驗,而且要照著清單一項一項對,不能只看「apply 沒報錯」。

3. 順手裝一下 argocd CLI

UI 很好看,但很多操作用指令快得多,而且能寫進腳本。CLI 不是必要的,但我會建議裝:

# macOS
brew install argocd
# Windows
winget install argoproj.argocd
# Linux
curl -sSL -o /usr/local/bin/argocd \
  https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64

之後常用的大概就這幾個:

argocd login localhost:8443 --insecure   # 自簽憑證,本機測試加 --insecure
argocd app list                          # 所有 App 的狀態一覽
argocd app get wafer-bi-platform         # 單一 App 的細節與資源清單
argocd app diff wafer-bi-platform        # Git 與叢集的差異(Day 17 §5-2)
argocd app sync wafer-bi-platform        # 手動觸發同步
argocd app history wafer-bi-platform     # 同步歷史

順帶一提:這些事情用 kubectl 也做得到,因為 Application 本身就是 CRD。像我後來要在腳本裡觸發同步,就直接 patch 資源,連 CLI 都不用裝:

kubectl patch app wafer-bi-platform -n argocd --type merge \
  -p '{"operation":{"sync":{"revision":"main"}}}'

這也是 Day 17 說的「ArgoCD 用 CRD 擴充 K8S API」的好處:原本會的工具就能操作它。

4. 登入 ArgoCD UI

先用 port-forward 存取(正式對外的做法在下一節):

kubectl -n argocd port-forward svc/argocd-server 8443:443

初始密碼藏在一個 Secret 裡:

kubectl -n argocd get secret argocd-initial-admin-secret \
  -o jsonpath="{.data.password}" | base64 -d

打開 https://localhost:8443,迎接你的是那隻著名的太空章魚:

https://ithelp.ithome.com.tw/upload/images/20260820/201825491MO7aZZlg0.png

▲ ArgoCD 登入頁:Let's get stuff deployed!

「Let's get stuff deployed!」——嗯,氣勢先給滿分 哈哈。

登入後第一件事是改密碼,然後把那個 Secret 刪掉

argocd account update-password
kubectl -n argocd delete secret argocd-initial-admin-secret

為什麼要刪?因為 argocd-initial-admin-secret明文可解的初始密碼,任何拿得到那個 namespace 讀取權限的人都能拿它登入 ArgoCD——而 ArgoCD 的 admin 等於整座叢集的部署權限。改完密碼之後這個 Secret 就只是個沒必要留著的風險,ArgoCD 官方也建議刪掉。

5. 讓 UI 對外:那個一定會踩到的憑證坑

本機用 port-forward 就夠了,但要給團隊用就得走 Ingress。這裡有一個幾乎每個人都會撞一次的問題:argocd-server 預設自己就是 HTTPS,而且用的是自簽憑證。

我自己在做這系列的截圖時就活生生撞到:拿瀏覽器自動化工具連上去,直接被 ERR_CERT_AUTHORITY_INVALID 擋掉;改連 HTTP 的 port,回我一個 307 重導向到 HTTPS。也就是說 HTTP 進不去、HTTPS 憑證不被信任,兩條路都不通。

這個問題有兩種解法,方向完全相反:

解法 A:讓 Ingress 不要拆封,直接轉給 ArgoCD(我們專案用的)

helm/wafer-bi/templates/argocd-ingress.yaml 的關鍵三行:

annotations:
  nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
  nginx.ingress.kubernetes.io/ssl-passthrough: "true"      # TLS 不在 nginx 拆
  nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"    # 後端講 HTTPS

TLS 連線一路穿過 nginx,由 argocd-server 自己終結。但有個前提很多人不知道:Nginx Ingress Controller 的 --enable-ssl-passthrough 預設是關的,沒開的話這個 annotation 完全不會生效,你會得到一個看起來設定正確、實際上沒作用的 Ingress。

解法 B:讓 ArgoCD 不要自己做 TLS

kubectl -n argocd patch cm argocd-cmd-params-cm --type merge \
  -p '{"data":{"server.insecure":"true"}}'
kubectl -n argocd rollout restart deploy/argocd-server

argocd-server 改成純 HTTP,TLS 交給 Ingress 終結(正式環境搭 cert-manager 簽的憑證,就是 Day 11 做的事)。這樣就不會有自簽憑證的問題,設定也單純很多。

要注意 insecure 這個字容易誤導:它指的是「argocd-server 這一段不加密」,不是「整條連線不加密」。只要 Ingress 有正確終結 TLS,使用者到叢集之間依然是 HTTPS,裸露的只有叢集內部那一段。但如果你沒有 Ingress 就開這個參數,那就真的是明文了。

6. 建立 Application

Application 是 ArgoCD 的核心資源:「哪個 Git repo 的哪個路徑,部署到哪個叢集的哪個 namespace」。可以在 UI 點 New App,但既然都 GitOps 了,我們用 YAML 宣告(k8s/argocd-app.yaml):

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: wafer-bi-platform
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/DarkSchneider1024/wafer-bi.git
    targetRevision: main
    path: helm/wafer-bi          # ← ArgoCD 直接吃 Helm Chart
    helm:
      valueFiles:
        - values.yaml
        - values-production.yaml # production 覆寫(replicas、HPA…)
  destination:
    server: https://kubernetes.default.svc
    namespace: k8sdemo
  syncPolicy:
    automated:
      prune: true      # Git 裡刪掉的資源,叢集也自動刪
      selfHeal: true   # 有人手動改叢集?自動改回來
    syncOptions:
      - CreateNamespace=true

這份 YAML 有以下幾個地方要說明:

  • path: helm/wafer-bi:ArgoCD 原生支援 Helm,會自動 helm template 渲染再比對,Day 10 的 Chart 直接無縫接軌
  • automated + prune + selfHeal:production 開全自動;本機示範時我刻意拿掉 automated 改手動 sync,才能親眼看到 Day 17 講的那個 OutOfSync → Synced 的對比過程
  • 這份 Application YAML 本身只需要在叢集初始化時 apply 一次,之後連它都不用再碰

apply 下去之後回到 UI,點進 App 看資源樹——Day 17 看的是清單頁的狀態,這裡才是真正發生了什麼

https://ithelp.ithome.com.tw/upload/images/20260820/20182549Zo7NPo4rE6.png

▲ 剛建立:Git 說要有 jaeger(deploy)和 jaeger-service(svc),叢集裡兩個都還是幽靈狀態

(這裡示範用的是一個輕量的 Application,指向 repo 的 k8s/base/observability,資源少、看得清楚。換成 helm/wafer-bi 的話樹會長成三十幾個節點,截圖上什麼都看不到。)

這裡有以下兩個細節要注意:

  • from main (6bdc539)——ArgoCD 明確告訴你比對的基準是哪一個 commit,底下還帶出作者和 commit 訊息。這裡的作者是 github-actions[bot],正是明天要講的 CI 自動回填留下的痕跡
  • Auto sync is not enabled——這個 demo 我刻意關掉自動同步,才拍得到「Git 有、叢集還沒有」的中間狀態

按下 SYNC:

https://ithelp.ithome.com.tw/upload/images/20260820/20182549EuIXMZhYOZ.png

▲ 同步完成:Sync OK,資源樹從 App 一路長到 Pod,全部亮綠

樹的形狀是這張圖最值得看的地方:Git 裡只寫了 2 個資源,樹上卻長出 4 個節點——多出來的 ReplicaSet 和 Pod 不在 Git 裡,是 K8S 自己派生的。ArgoCD 分得清楚這件事:它只對「Git 裡有的」做同步判定,但把「因此產生的」全部顯示出來讓你追蹤健康狀態。

這也解釋了 Day 17 說的那兩個獨立維度為什麼會分開:Pod 掛掉時 Health 變 Degraded,但 Sync Status 依然是 Synced——因為 Git 跟叢集確實一致,只是那個一致的版本跑不起來

而且這棵樹是可以互動的:點任何一個節點都能直接看它的 Events、Logs、YAML,甚至直接刪掉它(然後看 ArgoCD 把它長回來)。對不熟 kubectl 的團隊成員來說滿方便的。

7. AppProject:別讓每個 App 都是神

上面那份 YAML 有一行很容易被跳過:project: default

AppProject 是 ArgoCD 的權限邊界。預設的 default project 幾乎沒有任何限制——它允許來源是任何 repo、目的地是任何叢集的任何 namespace、可以部署任何種類的資源。一個人只要能建 Application,就能把任意 Git repo 的內容部署到你叢集的任何角落。

自己一個人玩沒差,但只要開始有第二個人碰它,就該建自己的 project:

apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: wafer-bi
  namespace: argocd
spec:
  sourceRepos:                              # 只准從我們自己的 repo 部署
    - https://github.com/DarkSchneider1024/wafer-bi.git
  destinations:                             # 只准部署到這個 namespace
    - server: https://kubernetes.default.svc
      namespace: k8sdemo
  clusterResourceWhitelist: []              # 不准碰叢集層級資源(如 ClusterRole)
  namespaceResourceBlacklist:
    - group: ''
      kind: ResourceQuota

搭配 argocd-rbac-cm 這張 ConfigMap,還能做到「A 團隊只能 sync 自己的 App、看不到別人的」。這是那種「出事之後才會後悔沒設」的東西——GitOps 把部署權限從人手上收走交給 controller,但如果 controller 本身無所不能,你只是換了一個單點風險。

8. 私有 Repo 怎麼辦?

我們的 repo 是 public,ArgoCD 匿名就能拉。如果是私有 repo,在 Settings → Repositories 加一組 Deploy Key(唯讀 SSH key)或 GitHub App 憑證即可。

這一頁也是安裝驗證的最後一塊:它會告訴你 ArgoCD 到底連不連得到你的 Git

https://ithelp.ithome.com.tw/upload/images/20260820/201825494StwjYMrmP.png

▲ Settings → Repositories:CONNECTION STATUS 顯示 Successful,代表 repo-server 真的拉得到這個 repo

那個 Successful 是實際去連過的結果,不是設定填完就給你的樂觀顯示。如果憑證錯了、網路不通、或 repo 路徑打錯,這裡會直接顯示 Failed 並附上錯誤訊息——比等到建完 Application 才發現 ComparisonError 快多了。私有 repo 接不上時,第一個該看的就是這一頁。

實務上有以下三件事情要注意:

  • 一定要唯讀。ArgoCD 只需要讀 Git,永遠不需要寫——真正會寫 Git 的是 CI(Day 19 的回填),那是另一組完全不同的憑證。權限最小化貫徹到底
  • 憑證存在哪:ArgoCD 會把它存成 argocd namespace 裡的 Secret。所以「誰能讀 argocd namespace」等於「誰能拿到你的 repo 憑證」,跟前面刪初始密碼是同一個道理
  • 用 GitHub App 比 Deploy Key 好管:Deploy Key 綁單一 repo,repo 一多就變成一堆金鑰要輪替;GitHub App 可以集中管理權限與到期

9. 小結

GitOps 中心開張了。今天最想強調的其實不是安裝——安裝真的只有兩行——而是安裝之後的那些事

  • 裝完要驗:七個元件、三張 CRD、一個 Service。我自己就是靠這套清單才發現有個 controller 已經默默壞了九十六天
  • 三件容易跳過的安全瑣事:刪掉初始密碼 Secret、搞懂 insecure 到底不安全在哪一段、知道 project: default 是一張沒有上限的信用卡
  • 資源樹會告訴你真相:Git 寫了什麼、K8S 又自己派生了什麼、哪一層出問題,一眼看得出來

但目前為止 CI 跟 ArgoCD 還是兩條平行線——CI 產 image,ArgoCD 看 Git,中間差一個「誰去把新版本號寫進 Git」。明天就來把這最後一哩路接起來,完成全自動閉環。


上一篇
[Day 17] GitOps 降臨:為什麼我們需要 ArgoCD? —— 從「推動式」轉向「聲明式」,順便看清它跟 CI 的分工。
下一篇
[Day 19] 流水線串接:實現代碼推送即部署的自動化閉環 —— 打通從 Commit 到 K8S 生產環境的最後一哩路。
系列文
從零到一:使用 K8S + GitOps 打造異構技術棧的資料分析平台19
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言