Kubernetes 不只是部署工具:用控制迴路把容器系統變成可自癒平台
從控制迴路、Deployment 與 Service,到健康檢查、滾動更新和 AI 推理服務,拆解 Kubernetes 如何把容器維運變成可觀察、可回復的工程系統。
Kubernetes 不只是部署工具:用控制迴路把容器系統變成可自癒平台
很多團隊第一次接觸 Kubernetes,看到的是一串指令:建立 Deployment、暴露 Service、設定副本數,然後期待應用程式「自己運作」。但 Kubernetes 真正值得理解的地方,不是命令列有多少子命令,而是它把分散式系統的維運工作,整理成一組可持續執行的控制迴路。
你描述想要的狀態,Kubernetes 負責觀察目前狀態、計算差異,再透過控制器、Scheduler、kubelet 與網路元件逐步把系統拉回目標。容器意外停止時,它可以重新啟動;副本數不足時,它可以補齊;版本更新時,它可以依 Deployment 的策略逐步替換。這套模型讓「部署一次」轉成「持續維持」。
本文以 kubernetes/kubernetes 為主題,從原始碼專案的定位開始,拆解控制平面與工作負載的關係,接著用一個可在本機重現的範例,走過部署、服務發現、健康檢查、設定管理、滾動更新與回滾。重點不是背 YAML,而是理解每一個資源在控制迴路中扮演的角色。
先看專案:為什麼 Kubernetes 仍值得研究
截至 2026 年 8 月 10 日,GitHub API 顯示 kubernetes/kubernetes 約有 124,378 顆星,最近一次推送時間為 2026 年 8 月 9 日,授權為 Apache-2.0;同一個 API 回應也列出 v1.37.0-rc.0 於 2026 年 8 月 6 日發布。這些資料只用來確認專案活躍度與版本脈絡,不代表候選版本已經適合所有生產環境。
Kubernetes 官方 README 將它定位為管理多主機容器化應用程式的開源系統,提供部署、維護與擴展的基本機制。官方概念文件則進一步說明,它提供服務探索與負載平衡、自我修復、水平擴展、滾動更新、密鑰與設定管理等能力。
這個專案適合用來寫實作型文章,原因有三個:
- 它是一個真正可執行的控制平台。 不只是規範或資源清單,而是包含 API Server、Controller Manager、Scheduler、kubelet 與大量控制器的完整系統。
- 抽象層彼此可驗證。 你可以用
kubectl get觀察資源,用describe追蹤事件,再把 YAML 的宣告和實際狀態對照起來。 - 同一套模型可延伸到 AI 服務。 API、工作負載、服務、資源限制與健康檢查,都是模型推理服務、資料處理工作與 Agent 後端會遇到的基礎問題。
核心不是 Pod,而是控制迴路
初學者常把 Kubernetes 等同於「啟動 Pod 的工具」,但 Pod 只是最小的可部署單位。實務上,應用程式通常由多個物件共同描述:Deployment 管理無狀態工作負載,ReplicaSet 維持副本數,Service 提供穩定的網路入口,ConfigMap 與 Secret 分離設定和敏感資料,Probe 則把應用程式健康狀態交給平台判斷。
可以把一次部署想成以下流程:
- 使用者將期望狀態送進 API Server,例如「要有三個
webPod,映像版本是v2」。 - Deployment Controller 讀取這個期望狀態,建立或更新 ReplicaSet。
- ReplicaSet Controller 觀察目前 Pod 數量,若少於三個,就建立新的 Pod 物件。
- Scheduler 為尚未綁定節點的 Pod 選擇合適 Node,考量資源請求、限制、親和性與其他排程條件。
- Node 上的 kubelet 透過容器執行環境啟動容器,並持續回報狀態。
- 若容器不符合 liveness 或 readiness 條件,平台依探針結果採取重啟或暫停流量分派等動作。
這裡最重要的觀念是「控制器不是腳本」。腳本執行完就結束;控制器會持續 watch 資源與事件,因此即使某個 Pod 被刪除,系統仍會發現差異並嘗試修復。這也是 Kubernetes 能處理節點故障、部署中斷與副本漂移的原因。
建立一個最小可觀察範例
以下範例採用 Minikube 作為本機學習環境。官方 Hello Minikube 教學涵蓋建立叢集、建立 Deployment、使用 Service 暴露應用程式、查看 Pod 與 Node,以及執行擴展和滾動更新。若你已經有其他 Kubernetes 叢集,也可以把 minikube 指令換成自己的叢集操作。
先確認環境
準備 Docker 或其他相容的容器執行環境,以及 kubectl 與 Minikube。先建立叢集並確認節點狀態:
minikube start
kubectl cluster-info
kubectl get nodes -o wide如果 minikube start 失敗,先看容器執行環境是否啟動、目前使用的 driver 是否可用,以及本機是否有足夠 CPU、記憶體和磁碟。不要一開始就把問題歸咎於 YAML;叢集尚未 Ready 時,後續每一層都會連鎖失敗。
用 Deployment 描述工作負載
建立 web.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
labels:
app: web
spec:
replicas: 2
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:stable-alpine
ports:
- name: http
containerPort: 80
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
readinessProbe:
httpGet:
path: /
port: http
periodSeconds: 5
livenessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 10
periodSeconds: 10套用並觀察:
kubectl apply -f web.yaml
kubectl get deployment,replicaset,pod -l app=web
kubectl describe deployment web
kubectl get events --sort-by=.lastTimestamp這幾個指令提供不同層次的觀察:get 看摘要,describe 看控制器計算出的狀態與事件,events 則協助辨識排程、拉取映像或探針失敗。若 Pod 卡在 Pending,優先檢查資源與排程;若是 ImagePullBackOff,檢查映像名稱、標籤與 Registry;若是 CrashLoopBackOff,再查看容器日誌和啟動參數。
用 Service 固定網路入口
Pod 會被重新建立,IP 也可能改變,因此不應讓其他服務直接依賴 Pod IP。建立 web-service.yaml:
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector:
app: web
ports:
- name: http
port: 80
targetPort: http
type: ClusterIP執行:
kubectl apply -f web-service.yaml
kubectl get service web
kubectl get endpointslices -l kubernetes.io/service-name=web
minikube service web --urlService 以 selector 找到符合條件的 Pod,提供穩定的服務抽象。這個細節很關鍵:Deployment 負責「有哪些副本」,Service 負責「流量送到哪些副本」。把兩者混在一起,排查問題時就很容易誤判。
三種健康檢查,不要只設定一種
官方文件將探針分成 liveness、readiness 與 startup 三類。它們解決的是不同問題:
- livenessProbe:應用程式是否已經進入無法自行恢復的狀態。如果失敗,kubelet 可能重啟容器。
- readinessProbe:應用程式目前能否接收流量。如果失敗,Pod 會暫時從 Service 的可用端點中移除,但不一定重啟容器。
- startupProbe:應用程式是否完成啟動。啟動探針成功前,liveness 與 readiness 不會開始判斷,適合需要較長 warm-up 的服務。
這三者的分工能避免一個常見錯誤:把「尚未載入模型」誤判成「程式壞掉」。以 AI 推理服務為例,模型載入、GPU 初始化或快取建立可能需要幾十秒甚至更久。此時應用程式可能仍然活著,但還不適合接收流量;使用 startup 和 readiness 的組合,通常比單純把 liveness 的延遲調到很大更清楚。
探針也不是越多越好。HTTP endpoint 應該是低成本、可預測的檢查;不要讓健康檢查每次都觸發資料庫全表掃描、模型推理或昂貴的外部 API 呼叫。否則平台為了判斷健康,反而會製造新的負載。
設定、密鑰與映像版本要分離
把所有設定硬編碼在映像裡,會讓同一個映像難以跨環境重用。ConfigMap 適合非敏感設定,例如服務埠、功能開關與環境名稱;Secret 則用來傳遞密鑰、憑證或其他敏感資料。Secret 並不等於完整的密鑰管理系統,生產環境仍應評估加密儲存、RBAC、外部 Secret manager、輪替與稽核。
一個不含真實憑證的示例:
apiVersion: v1
kind: ConfigMap
metadata:
name: web-config
data:
LOG_LEVEL: info
FEATURE_MODE: production
---
apiVersion: v1
kind: Secret
metadata:
name: web-secret
type: Opaque
stringData:
API_TOKEN: "[REDACTED]"套用後,可以用 envFrom 或個別 env 將資料注入容器。檢查設定時要留意:kubectl describe、事件、Shell history 和 CI log 都可能造成敏感資料意外曝光。不要把 kubectl get secret -o yaml 的輸出貼到 issue 或聊天頻道。
滾動更新、擴展與回滾
Kubernetes 的價值不只在第一次部署,更在於變更之後仍能維持服務。修改 Deployment 的映像版本:
kubectl set image deployment/web nginx=nginx:1.27-alpine
kubectl rollout status deployment/web
kubectl rollout history deployment/web如果新版本行為不符合預期,可以回滾:
kubectl rollout undo deployment/web
kubectl rollout status deployment/web這些指令背後是 Deployment Controller 建立新 ReplicaSet、逐步調整新舊 ReplicaSet 副本數的過程。實際的可用性還取決於 maxUnavailable、maxSurge、PodDisruptionBudget、探針與應用程式是否能優雅關閉。看到 rollout 完成,不代表功能驗證完成;仍應搭配 smoke test、指標、日誌和錯誤率檢查。
水平擴展則是調整期望副本數:
kubectl scale deployment/web --replicas=4
kubectl get pods -l app=web -w如果要依 CPU 或自訂指標自動擴展,還需要安裝並正確設定相對應的 metrics 元件,不能只建立一個 HPA 物件就假設它會工作。對模型推理服務而言,GPU、模型記憶體、併發請求和批次大小通常比 CPU 使用率更能反映容量;擴展策略要依服務特性設計。
從故障現象反推控制迴路
實作時,先建立一個固定的排查順序,比記住更多指令更有用。第一步看資源是否真的存在:kubectl get deployment web、kubectl get pods -l app=web 和 kubectl get svc web 可以確認物件是否被 API Server 接受。第二步看狀態是否收斂:Deployment 的 AVAILABLE、Pod 的 READY 與 Service 的 EndpointSlice 應該互相一致。第三步才深入事件和日誌:kubectl describe pod 能顯示排程、掛載、探針與映像拉取事件,kubectl logs 則用來辨識應用程式本身的啟動錯誤。
常見狀況可以這樣分層:Pod 是 Pending,通常先查 Node 資源、taint、親和性或 PVC;Pod 是 ContainerCreating,檢查映像、Volume、CNI 與 Secret;Pod 反覆重啟,查看上一個容器的日誌和退出碼;Deployment 有新 ReplicaSet 卻沒有可用副本,檢查 readinessProbe、資源限制和映像版本;Service 沒有端點,檢查 selector 是否與 Pod labels 完全相符。這套流程的目的,是把「服務不通」拆成 API、排程、啟動、健康與網路幾個可驗證的假設。
完成修改後,至少做一次正向和一次反向驗證:先確認正常版本能被 Service 存取,再故意部署一個不存在的映像或暫時調高副本數,觀察事件和 rollout 狀態,最後恢復設定並確認系統回到穩定狀態。這比只看到一行 deployment successfully configured 更能證明控制迴路真的按預期工作。
生產環境最容易被忽略的邊界
1. 資源請求不是裝飾
requests 影響排程,limits 影響容器可使用的資源上限。沒有合理的 requests,Scheduler 難以做出可靠決策;沒有觀測實際使用量,limits 可能造成 OOMKill 或 CPU throttling。先用指標建立基線,再調整,而不是複製別人的數字。
2. 命名空間與 RBAC 要一起設計
Namespace 可以協助分隔團隊、環境和配額,但它不是完整的安全邊界。ServiceAccount、Role、RoleBinding 與最小權限原則才是 API 存取控制的核心。工作負載不應預設拿到整個叢集的讀寫權限。
3. 觀測要對準控制迴路
除了應用程式 log,還要看 Pod 狀態、Deployment 可用副本、排程事件、探針失敗、節點壓力和 API Server 錯誤。遇到問題時,依序問「期望狀態是什麼、目前狀態是什麼、哪個控制器沒有把差異收斂」通常比盲目重啟更快找到根因。
4. YAML 可維護性會決定變更速度
小型測試可以用單一檔案;團隊協作則要建立版本控制、環境覆寫、驗證與審查流程。無論選用 Kustomize、Helm 或其他工具,都應讓最後送進 API Server 的資源可追蹤、可重現、可回滾。模板不是目的,能清楚回答「這次變更會影響哪些工作負載」才是目的。
AI Chain 實作視角:把模型服務當成平台的一部分
Kubernetes 不會替你解決模型品質、提示詞設計或資料治理,但它能承接 AI 應用進入生產後的工程問題:
- 模型 API 可以由 Deployment 管理副本,由 Service 提供穩定入口。
- 模型載入時間 可以用 startupProbe 描述,避免容器尚未準備好就被重啟或接收流量。
- 版本切換 可以透過多個 Deployment、標籤與流量策略逐步執行,而不是一次替換所有實例。
- GPU 工作負載 可以使用 Kubernetes 的資源與排程能力,但仍須搭配正確的裝置外掛、節點標籤、資源請求與監控。
- 資料處理工作 可用 Job 或 CronJob 表達一次性與週期性任務,並將失敗重試、保留策略和輸出位置明確化。
這種拆法能讓 AI 系統從「一台機器上跑一個服務」逐步成為可觀測、可更新、可回復的工程系統。不過,叢集複雜度也是真實成本。若服務規模很小,Docker Compose 或平台代管服務可能更合適;選 Kubernetes 的理由應該是你確實需要它的控制迴路、擴展能力和生態系,而不是因為它很流行。
結語:先學會觀察差異,再學 YAML
Kubernetes 最值得帶走的不是某一個 kubectl 參數,而是「宣告期望狀態,讓控制迴路持續收斂」的工程思想。Deployment 管副本與版本,Service 管穩定入口,Probe 管健康判斷,ConfigMap 和 Secret 管設定邊界;每個物件都應該有清楚責任,並能用命令、事件和指標驗證。
建議用本文的最小範例開始:先建立兩個副本,確認 Service 能找到端點,再故意改錯映像觀察 rollout,最後回滾並檢查事件。當你能回答「哪個控制器看到了什麼差異、它做了什麼、下一步如何驗證」,就不再只是會套用 YAML,而是真的開始理解 Kubernetes。
查證來源
- Kubernetes GitHub repository:<https://github.com/kubernetes/kubernetes>
- Kubernetes README:<https://github.com/kubernetes/kubernetes/blob/master/README.md>
- Kubernetes Overview:<https://kubernetes.io/docs/concepts/overview/>
- Deployments:<https://kubernetes.io/docs/concepts/workloads/controllers/deployment/>
- Service:<https://kubernetes.io/docs/concepts/services-networking/service/>
- Secrets:<https://kubernetes.io/docs/concepts/configuration/secret/>
- Liveness、Readiness、Startup Probes:<https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/>
- kubectl Quick Reference:<https://kubernetes.io/docs/reference/kubectl/quick-reference/>
- Hello Minikube:<https://kubernetes.io/docs/tutorials/hello-minikube/>