Skip to content

antony@notes:~/kubernetes$ cat "Kubernetes-kube-controller-manager-設定參數深度解析.md"

Kubernetes kube-controller-manager 設定參數深度解析

2026-04-23· kubernetes

Kubernetes kube-controller-manager 設定參數深度解析

node-cidr-mask-sizeterminated-pod-gc-threshold 出發,了解 PodGC Controller 的運作原理,並透過 Kind 本機環境實作驗證。


文件資訊

項目內容
文件主題kube-controller-manager extraArgs 參數解析與 PodGC 實作驗證
建立日期2026-04-23
適用版本Kubernetes(以 master 分支原始碼為準)
驗證環境Kind 本機叢集
主要參考kubernetes/pkg/controller/podgc

目錄


一、背景:設定檔的用途

本文件所討論的設定檔片段:

controllerManager:
  extraArgs:
    - name: node-cidr-mask-size              # set pod cidr per node
      value: "25"
    # Garbage Collection
    # Max number of terminated pods to keep before cleaning up
    - name: terminated-pod-gc-threshold
      value: "500"

這是 Kubernetes kubeadm ClusterConfiguration 中,用來傳遞額外參數給 kube-controller-manager 的設定片段。extraArgs 裡面的每一項,最終會被轉換成 kube-controller-manager 啟動時的命令列參數(例如 --node-cidr-mask-size=25)。

kube-controller-manager 是 Kubernetes 控制平面的核心元件之一,內部包含了許多 controller(如 Node Controller、Replication Controller、Garbage Collector 等),負責維持叢集的「期望狀態」。


二、參數解析

2.1 node-cidr-mask-size: "25"

字面意義

設定「每個 Node 分配到的 Pod CIDR 子網路遮罩大小」為 /25

運作原理

當您在叢集建立時設定了整個叢集的 Pod 網段(--cluster-cidr,例如 10.244.0.0/16),kube-controller-manager 會從這個大網段中,切出一小塊子網段分配給每個加入叢集的 Node,這塊子網段就是該 Node 上所有 Pod 的 IP 來源。

node-cidr-mask-size=25 代表每個 Node 會拿到一個 /25 的子網段。

實際影響(以 /25 為例)

  • 每個 Node 可分配的 IP 數量:2^(32-25) = 128 個 IP
  • 扣除網路位址、廣播位址等保留 IP,每個 Node 實際可跑約 126 個 Pod
  • 若叢集的 cluster-cidr 是 /16(共 65,536 個 IP),理論上可切出 2^(25-16) = 512 個 Node 的配額

與預設值的差異

Kubernetes 預設值是 /24(每個 Node 可跑約 254 個 Pod)。設成 /25 代表:

  • 節省 IP 位址:在有限的 cluster-cidr 下可容納更多 Node
  • ⚠️ 限制單一 Node 的 Pod 密度:每個 Node 最多只能跑約 126 個 Pod(仍高於 kubelet 預設的 max-pods=110,所以實務上通常夠用)

什麼情境會這樣設定?

  • Pod IP 網段資源有限(例如 VPC 內可用的 CIDR 不大)
  • 叢集 Node 數量多、但每個 Node 的 Pod 密度不高
  • 希望在有限網段內擴充 Node 規模

2.2 terminated-pod-gc-threshold: "500"

字面意義

當叢集中「已終止(terminated)狀態的 Pod」數量超過 500 個時,Garbage Collector 會開始自動清理這些 Pod。

運作原理

當 Pod 執行完畢(Succeeded 或 Failed),它們不會立刻從 etcd 中刪除,而是會保留下來供使用者查詢記錄(例如 kubectl get pods 時看到 Completed 狀態的 Pod)。這些 Pod 雖然不再消耗運算資源,但仍會佔用:

  • etcd 儲存空間
  • API Server 的查詢效能

這個參數控制「保留多少個已終止 Pod 才開始清理」。

實際影響(設為 500)

  • 當已終止 Pod 數量 ≤ 500:不會觸發清理,全部保留
  • 當已終止 Pod 數量 > 500:從最舊的開始刪除,直到數量回到 500 以下

與預設值的差異

Kubernetes 預設值是 12500,設成 500 代表:

  • 更積極地清理,減輕 etcd 負擔、加快 API Server 回應速度
  • ✅ 適合有大量 Job / CronJob 產生短命 Pod 的叢集
  • ⚠️ 可保留的歷史記錄變少,若需要排查較早的失敗 Pod 可能已被刪除(建議搭配 Log 收集系統,如 Loki、ELK)

什麼情境會這樣設定?

  • 叢集會跑大量 Batch Job / CronJob
  • etcd 效能是瓶頸、或想降低 etcd 儲存壓力
  • 已有完善的 log / event 外部保存機制,不需依賴 API Server 保留 Pod 記錄

2.3 兩個參數的整體設計意圖

關注點對應參數設計思路
IP 資源效率node-cidr-mask-size=25用比較小的 Pod CIDR 讓叢集能容納更多 Node
控制平面效能terminated-pod-gc-threshold=500積極清理終止 Pod,減輕 etcd / API Server 壓力

兩者都指向同一個方向:這是一個偏向中大型、但希望控制資源使用效率的叢集


2.4 設定時的注意事項

  1. node-cidr-mask-size 必須與 cluster-cidrkubelet --max-pods 三者一起通盤考量,避免出現 Node 配額超過 IP 可用量,或 Pod 配額超過網段容量的狀況。
  2. terminated-pod-gc-threshold 不能設太低(例如幾十個),否則剛跑完的 Job 馬上被清掉,會讓 debug 非常困難。500 是一個相對平衡的數字。
  3. 修改 controllerManager.extraArgs 後,需要重啟 kube-controller-manager Pod 才會生效(若是 kubeadm 叢集,修改 /etc/kubernetes/manifests/kube-controller-manager.yaml 會自動重啟)。

三、原理:PodGC Controller 如何認定「terminated Pod」

3.1 原始碼位置

Kubernetes 的 PodGC Controller 位於:

kubernetes/pkg/controller/podgc/gc_controller.go

3.2 原始碼層面的 terminated 定義

根據 Kubernetes 官方 pkg/controller/podgc 套件的 doc.go 描述:

PodGCController… If the number of pods in terminated phases (right now either Failed or Succeeded) surpasses a configurable threshold, the controller will delete pods in terminated state until the system reaches the allowed threshold again. The PodGCController prioritizes pods to delete by sorting by creation timestamp and deleting the oldest objects first.

這段話給了我們三個關鍵定義:

判定依據說明
1. Pod 的 status.phase必須是 FailedSucceeded
2. 判斷時機當 terminated Pod 總數 > threshold 才觸發清理
3. 刪除順序依照 creationTimestamp 排序,最舊的先刪

gc_controller.go 中也能看到這個判定邏輯的核心程式碼片段:

if phase := pod.Status.Phase; phase != v1.PodPending &&
    phase != v1.PodRunning &&
    phase != v1.PodUnknown {
    // 這個 Pod 屬於 terminated,納入 GC 候選名單
}

這段程式碼使用反向列舉的方式定義 terminated:只要不是 Pending / Running / Unknown,就是 terminated。在 Kubernetes 的 Pod phase 定義中,剩下的就是 SucceededFailed 兩種。


3.3 用 kubectl 判斷 Pod 是否為 terminated

對應到 kubectl,可以用 status.phase 做 field selector 過濾:

# 查詢所有 Succeeded 的 Pod
kubectl get pods --all-namespaces --field-selector=status.phase=Succeeded

# 查詢所有 Failed 的 Pod
kubectl get pods --all-namespaces --field-selector=status.phase=Failed

# 同時查詢兩者(field-selector 不支援 OR,需要用 jq 輔助)
kubectl get pods --all-namespaces -o json | \
  jq '.items[] | select(.status.phase=="Succeeded" or .status.phase=="Failed") | .metadata.name'

# 快速統計 terminated Pod 數量
kubectl get pods --all-namespaces -o json | \
  jq '[.items[] | select(.status.phase=="Succeeded" or .status.phase=="Failed")] | length'

⚠️ 常見誤解澄清 kubectl get pods 看到的 Completed 不是 phase 值,而是 STATUS 欄位的顯示字串。真正的 phase 是 Succeeded。同理 Error 對應的是 Failed。 驗證時一定要用 status.phase 當條件,不要用 STATUS 欄位的字串。


3.4 值得注意的原始碼細節

細節 1:GC 只會把超出的部分刪掉,不會歸零

根據 GitHub Issue #71421 的討論,PodGC 的設計行為是:

The current behavior seems to allow the terminated Pod amount fluctuate above and under the threshold, instead of clean up completely when threshold is met.

實例:假設 threshold = 500,當 terminated Pod 達到 600 時,GC 會刪掉 100 個(600 - 500),讓數量回到 500,而不是全部清光。這個設計是刻意的。

細節 2:threshold ≤ 0 代表停用 GC

when set terminated-pod-gc-threshold <= 0, the terminated pod garbage collector is disabled.

細節 3:GC 檢查週期

原始碼中有個常數 gcCheckPeriod,預設是 20 秒。也就是 PodGC Controller 每 20 秒會檢查一次是否需要清理。


四、驗證實驗:用 Kind 驗證 terminated-pod-gc-threshold

4.1 實驗設計

項目設定值設計目的
叢集Kind 本機叢集快速建置、可改 controller-manager 參數
threshold500(接近真實情境)驗證實際設定檔的值
Job 數量600 個(超過 threshold 100 個)驗證超量時的清理行為
觀察點 1Job 全部完成瞬間的 terminated Pod 數量應該是 600
觀察點 2GC 週期後(等 30~60 秒)應該被砍到 500 左右
觀察點 3被保留的 Pod creationTimestamp應該是較新的 500 個(舊的被刪)

4.2 完整實驗步驟

Step 1: 建立 Kind 叢集並掛入自訂的 controller-manager 參數

建立 kind-config.yaml:

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: podgc-test
nodes:
  - role: control-plane
    kubeadmConfigPatches:
      - |
        kind: ClusterConfiguration
        controllerManager:
          extraArgs:
            terminated-pod-gc-threshold: "500"
  - role: worker
  - role: worker

建立叢集:

kind create cluster --config kind-config.yaml

Step 2: 驗證參數有正確套用到 kube-controller-manager

# 進入 control-plane 容器查看 manifest
docker exec podgc-test-control-plane \
  cat /etc/kubernetes/manifests/kube-controller-manager.yaml \
  | grep terminated-pod-gc-threshold

# 應該看到: --terminated-pod-gc-threshold=500

另一個驗證方式是看 controller-manager Pod 的 command:

kubectl -n kube-system get pod -l component=kube-controller-manager \
  -o jsonpath='{.items[0].spec.containers[0].command}' | tr ',' '\n' | grep terminated

Step 3: 建立 600 個會立即完成的 Job

準備腳本 create-jobs.sh:

#!/bin/bash
set -e

NAMESPACE="podgc-test"
kubectl create namespace $NAMESPACE --dry-run=client -o yaml | kubectl apply -f -

for i in $(seq 1 600); do
  cat <<EOF | kubectl apply -f - >/dev/null
apiVersion: batch/v1
kind: Job
metadata:
  name: quickjob-$(printf "%03d" $i)
  namespace: $NAMESPACE
spec:
  ttlSecondsAfterFinished: 99999   # 避免 Job controller 自己先清掉 Pod,干擾實驗
  backoffLimit: 0
  template:
    spec:
      restartPolicy: Never
      containers:
        - name: c
          image: busybox:1.36
          command: ["sh", "-c", "echo done"]
EOF
  # 每 50 個稍微喘一下,避免 API Server 過載
  if (( i % 50 == 0 )); then
    echo "Created $i jobs..."
    sleep 2
  fi
done

echo "All 600 jobs submitted."

⚠️ 關鍵設計:ttlSecondsAfterFinished 設得很大,是為了排除 Job Controller 的 TTL 機制(那是另一個清理機制),確保清理行為只由 PodGC 觸發,這樣實驗才乾淨。

執行:

chmod +x create-jobs.sh
./create-jobs.sh

Step 4: 持續觀察 terminated Pod 數量變化

開啟另一個終端機,跑一個監控腳本 watch-gc.sh:

#!/bin/bash
NAMESPACE="podgc-test"

while true; do
  TS=$(date '+%H:%M:%S')
  TOTAL=$(kubectl get pods -n $NAMESPACE -o json 2>/dev/null | \
    jq '[.items[] | select(.status.phase=="Succeeded" or .status.phase=="Failed")] | length')
  RUNNING=$(kubectl get pods -n $NAMESPACE -o json 2>/dev/null | \
    jq '[.items[] | select(.status.phase=="Running" or .status.phase=="Pending")] | length')
  echo "[$TS] terminated=$TOTAL  active=$RUNNING"
  sleep 5
done

執行:

chmod +x watch-gc.sh
./watch-gc.sh

Step 5: 預期觀察結果

您會看到類似以下的輸出序列:

[14:30:05] terminated=0    active=120     # Job 開始跑
[14:30:10] terminated=45   active=200
[14:30:20] terminated=180  active=300
[14:30:30] terminated=420  active=180
[14:30:40] terminated=590  active=10      # 接近高峰
[14:30:45] terminated=600  active=0       # 全部完成
[14:30:50] terminated=600  active=0
[14:30:55] terminated=600  active=0
[14:31:05] terminated=500  active=0       # ← GC 觸發!砍掉 100 個
[14:31:10] terminated=500  active=0       # 穩定在 threshold

驗證點:

  • ✅ 高峰值達到 600 → 證明 Pod 確實累積
  • ✅ GC 觸發後剩 500(而非 0) → 驗證「只砍超出部分」的設計
  • ✅ GC 觸發延遲 15~20 秒 → 對應原始碼中的 gcCheckPeriod = 20s

Step 6: 驗證「刪除順序是從最舊開始」

# 看留下來的 Pod 中,最早的 creationTimestamp 是什麼時候
kubectl get pods -n podgc-test \
  --field-selector=status.phase=Succeeded \
  --sort-by=.metadata.creationTimestamp \
  -o custom-columns=NAME:.metadata.name,CREATED:.metadata.creationTimestamp \
  | head -5

# 對照實驗:最早的 100 個 Pod(quickjob-001 ~ quickjob-100)應該都不見了
kubectl get pod quickjob-001 -n podgc-test 2>&1 | grep -i "not found"
kubectl get pod quickjob-600 -n podgc-test   # 應該還在

如果 quickjob-001 找不到、quickjob-600 還在,就印證了原始碼中的排序刪除邏輯:最舊的優先刪除。

Step 7: 查看 controller-manager 的 log 做最後驗證

kubectl -n kube-system logs -l component=kube-controller-manager \
  --tail=200 | grep -i "podgc\|garbage"

會看到類似:

I0423 14:31:05.123456  podgc_controller.go:xxx] "PodGC is force deleting Pod" pod="podgc-test/quickjob-001"
I0423 14:31:05.234567  podgc_controller.go:xxx] "PodGC is force deleting Pod" pod="podgc-test/quickjob-002"
...

這直接證明 PodGC Controller 確實在依照您設定的 threshold 工作


4.3 實驗清理

kind delete cluster --name podgc-test

五、整體驗證流程總結

原始碼層面定義 terminated
  └─ status.phase ∈ {Succeeded, Failed}
       │
       ▼
kubectl 指令層面驗證
  └─ --field-selector=status.phase=Succeeded/Failed
       │
       ▼
實驗層面驗證(Kind + 600 Jobs + threshold=500)
  ├─ 驗證點 1: 達到 600 → 證明 Pod 有累積
  ├─ 驗證點 2: 砍到剩 500 → 證明 threshold 精準生效
  ├─ 驗證點 3: 留下的是新的 → 證明 oldest-first 排序
  └─ 驗證點 4: controller-manager log 有 GC 紀錄 → 直接證據

六、參考資料

類型連結
PodGC 原始碼https://github.com/kubernetes/kubernetes/blob/master/pkg/controller/podgc/gc_controller.go
PodGC 套件說明https://github.com/kubernetes/kubernetes/blob/master/pkg/controller/podgc/doc.go
PodGC 行為討論 Issuehttps://github.com/kubernetes/kubernetes/issues/71421
Enhancement Proposalhttps://github.com/kubernetes/enhancements/issues/2890
Pod Lifecycle 官方文件https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/
kubeadm ClusterConfigurationhttps://kubernetes.io/docs/reference/config-api/kubeadm-config.v1beta4/
Kind 官方文件https://kind.sigs.k8s.io/

本文件由 Claude 整理自對話紀錄,供學習與參考使用。