先從一個常見狀況開始。假設 todo-api 需要兩個副本,其中一個 container 因為記憶體不足而停止。剩下一個副本雖然還能處理部分流量,但服務已經不符合原本的需求。若每次都要由人登入主機、找出故障的 container 再重啟,服務和副本一多,這件事很快就會變成負擔。
Kubernetes 的做法是讓我們先描述目標,再由叢集持續比對現況。這種設計稱為宣告式 API(Declarative API);後面的 GitOps 與 Operator 也都建立在同一個概念上。
直接對主機下指令,例如執行 restart,是指令式管理(Imperative Management):我們要明確告訴系統「現在做哪個動作」。它很適合一次性的工作,也能立刻看到結果。
不過,指令不會記錄服務後續應有的狀態。container 再次停止、節點故障,或有人手動調整副本數時,系統不知道原本要恢復成什麼狀態,仍要由人處理。
Kubernetes 使用宣告式管理(Declarative Management)。使用者、CI 或之後的 GitOps controller 提交資源定義,說明服務要維持什麼狀態。以下以 todo-api 說明:replicas: 2 的意思是這個服務要維持兩個副本,不是只在提交 YAML 的當下建立兩個 Pod。
apiVersion: apps/v1
kind: Deployment
metadata:
name: todo-api
spec:
replicas: 2
selector:
matchLabels:
app: todo-api
template:
metadata:
labels:
app: todo-api
spec:
containers:
- name: todo-api
image: todo-api:0.1.0
API Server 接收這份 YAML,並將資源的 spec 與 status 儲存在 etcd。若實際只剩一個健康 Pod,Deployment controller 會發現副本數不足,並開始建立替代 Pod。
Kubernetes 不靠一支「部署完就結束」的腳本維護服務。資源的 spec 被修改、Pod 被刪除,或 kubelet 回報新的 Pod 狀態時,相關 controller 會收到事件。它讀取資源定義與目前狀態;若發現差異,就執行 reconcile,建立、更新或刪除資源,讓現況逐步接近 spec。
一次 reconcile 是 controller 的一次處理,不是一條永不結束的迴圈。controller 完成這次處理後,會繼續等待下一個事件;反覆觀察與處理差異的整體模式,才是控制迴路(Control Loop)。
controller 只會依資源定義執行,不會判斷應用程式的商業邏輯。image 寫錯、readiness probe 設錯,或 migration 會破壞資料時,controller 仍可能持續重試。因此,YAML 除了要寫出目標,也要正確設定應用程式的健康條件。
spec 是目標,status 是目前觀察結果前一段的 Deployment YAML 是我們提交給 Kubernetes 的內容,其中的 spec 是輸入:副本數、image 與標籤等希望叢集維持的條件。
資源建立後,controller 會把觀察結果寫回同一個資源的 status。開發者通常不會手動填寫這個區塊,而是透過 kubectl get deployment todo-api -n todo -o yaml 之類的指令讀取它。若目標是兩個副本、其中一個已可用,輸出會包含類似下列內容:
# Deployment controller 寫入的觀察結果範例
status:
replicas: 2
readyReplicas: 1
availableReplicas: 1
conditions:
- type: Available
status: "False"
reason: MinimumReplicasUnavailable
這兩個區塊回答的是不同問題:
spec.replicas: 2:我們要維持幾個副本?status.availableReplicas: 1:目前真正可提供服務的副本有幾個?當兩者不同時,代表叢集尚未達成 spec 的要求,controller 會繼續處理這個差異。因此,不能只看資源是否建立成功,還要看它是否真的進入可用狀態。
todo-api Deployment 為例
當其中一個 Pod 被刪除時,ReplicaSet controller 會再次發現副本數少於兩個,於是建立替代 Pod。其他如 Node 失效、Deployment 的 spec 被修改,或新 Pod 無法啟動時,controller 也會再次執行 reconcile。
直接建立 Pod 可以啟動 container,卻沒有定義副本數、更新策略與後續維護方式。Pod 停止後,Kubernetes 不會知道是否需要建立新的 Pod。
Deployment 定義了這些規則。Deployment controller 管理版本更新,並建立 ReplicaSet;ReplicaSet 再負責維持指定數量的 Pod。rollout、rollback、水平擴展與自我修復都能透過這組資源完成。
閱讀 Kubernetes YAML 時,除了確認 kind,還要確認三件事:這份資源要維持什麼狀態?哪個 controller 會處理它?發生問題時,應該查看哪個 status 欄位?
部署完成後,可以觀察 spec.replicas 和 status.availableReplicas 是否收斂;刪除其中一個 todo-api Pod 後,ReplicaSet controller 應建立替代副本,讓可用副本數回到兩個。
若 image 拉取失敗,Pod 可能已被建立,但 availableReplicas 不會增加;這時要看 status,不能只看資源是否存在。
理解 Deployment、ReplicaSet 與 controller 如何維持狀態後,下一篇才能進一步拆解服務上線時各資源的責任。