AI-Chain

Kubernetes 不只是部署工具:用控制迴路把容器系統變成可自癒平台

分享:
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 將它定位為管理多主機容器化應用程式的開源系統,提供部署、維護與擴展的基本機制。官方概念文件則進一步說明,它提供服務探索與負載平衡、自我修復、水平擴展、滾動更新、密鑰與設定管理等能力。 這個專案適合用來寫實作型文章,原因有三個: 1. **它是一個真正可執行的控制平台。** 不只是規範或資源清單,而是包含 API Server、Controller Manager、Scheduler、kubelet 與大量控制器的完整系統。 2. **抽象層彼此可驗證。** 你可以用 `kubectl get` 觀察資源,用 `describe` 追蹤事件,再把 YAML 的宣告和實際狀態對照起來。 3. **同一套模型可延伸到 AI 服務。** API、工作負載、服務、資源限制與健康檢查,都是模型推理服務、資料處理工作與 Agent 後端會遇到的基礎問題。 ## 核心不是 Pod,而是控制迴路 初學者常把 Kubernetes 等同於「啟動 Pod 的工具」,但 Pod 只是最小的可部署單位。實務上,應用程式通常由多個物件共同描述:Deployment 管理無狀態工作負載,ReplicaSet 維持副本數,Service 提供穩定的網路入口,ConfigMap 與 Secret 分離設定和敏感資料,Probe 則把應用程式健康狀態交給平台判斷。 可以把一次部署想成以下流程: 1. 使用者將期望狀態送進 API Server,例如「要有三個 `web` Pod,映像版本是 `v2`」。 2. Deployment Controller 讀取這個期望狀態,建立或更新 ReplicaSet。 3. ReplicaSet Controller 觀察目前 Pod 數量,若少於三個,就建立新的 Pod 物件。 4. Scheduler 為尚未綁定節點的 Pod 選擇合適 Node,考量資源請求、限制、親和性與其他排程條件。 5. Node 上的 kubelet 透過容器執行環境啟動容器,並持續回報狀態。 6. 若容器不符合 liveness 或 readiness 條件,平台依探針結果採取重啟或暫停流量分派等動作。 這裡最重要的觀念是「控制器不是腳本」。腳本執行完就結束;控制器會持續 watch 資源與事件,因此即使某個 Pod 被刪除,系統仍會發現差異並嘗試修復。這也是 Kubernetes 能處理節點故障、部署中斷與副本漂移的原因。 ## 建立一個最小可觀察範例 以下範例採用 Minikube 作為本機學習環境。官方 Hello Minikube 教學涵蓋建立叢集、建立 Deployment、使用 Service 暴露應用程式、查看 Pod 與 Node,以及執行擴展和滾動更新。若你已經有其他 Kubernetes 叢集,也可以把 `minikube` 指令換成自己的叢集操作。 ### 先確認環境 準備 Docker 或其他相容的容器執行環境,以及 `kubectl` 與 Minikube。先建立叢集並確認節點狀態: ```bash minikube start kubectl cluster-info kubectl get nodes -o wide ``` 如果 `minikube start` 失敗,先看容器執行環境是否啟動、目前使用的 driver 是否可用,以及本機是否有足夠 CPU、記憶體和磁碟。不要一開始就把問題歸咎於 YAML;叢集尚未 Ready 時,後續每一層都會連鎖失敗。 ### 用 Deployment 描述工作負載 建立 `web.yaml`: ```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 ``` 套用並觀察: ```bash 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`: ```yaml apiVersion: v1 kind: Service metadata: name: web spec: selector: app: web ports: - name: http port: 80 targetPort: http type: ClusterIP ``` 執行: ```bash kubectl apply -f web-service.yaml kubectl get service web kubectl get endpointslices -l kubernetes.io/service-name=web minikube service web --url ``` Service 以 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、輪替與稽核。 一個不含真實憑證的示例: ```yaml 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 的映像版本: ```bash kubectl set image deployment/web nginx=nginx:1.27-alpine kubectl rollout status deployment/web kubectl rollout history deployment/web ``` 如果新版本行為不符合預期,可以回滾: ```bash kubectl rollout undo deployment/web kubectl rollout status deployment/web ``` 這些指令背後是 Deployment Controller 建立新 ReplicaSet、逐步調整新舊 ReplicaSet 副本數的過程。實際的可用性還取決於 `maxUnavailable`、`maxSurge`、PodDisruptionBudget、探針與應用程式是否能優雅關閉。看到 rollout 完成,不代表功能驗證完成;仍應搭配 smoke test、指標、日誌和錯誤率檢查。 水平擴展則是調整期望副本數: ```bash 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: - Kubernetes README: - Kubernetes Overview: - Deployments: - Service: - Secrets: - Liveness、Readiness、Startup Probes: - kubectl Quick Reference: - Hello Minikube: