容器化技術 ch14 從單機到叢集:Kubernetes 在解什麼問題
下一章→
CH 14 編排與 SRE

從單機到叢集:Kubernetes 在解什麼問題

控制迴圈才是靈魂Pod/Deployment/Service滾動更新與自我修復三種 Proberequests vs limitsSRE 除錯入門

前面十三章,你已經完整掌握「單機上的容器」。這一章跨出最後一步:當一台機器不夠用、或不能掛掉的時候。

Kubernetes 常被形容成「很複雜」,但複雜的是它的廣度(元件多、名詞多),核心思想其實只有一個——而且非常優雅:

你描述「期望的狀態」,系統不斷地比對現況並自動修正差異。

這叫做控制迴圈(control loop)。理解了它,K8s 的所有行為都變成同一件事的不同展現:Pod 掛了會自己重生、更新版本會逐步替換、節點壞掉工作負載會搬家—— 全都是「現況不等於期望,所以我來修正」。

這章會用你已經熟悉的 Compose 當對照,並且用 SRE 的角度切入:不只是「怎麼部署」,而是「它壞掉的時候會怎麼壞、你要怎麼查」。

先講清楚問題,才知道 K8s 那一大堆概念是在回應什麼。

場景一:機器掛了

  • Compose:所有容器都在那一台機器上。機器掛 → 服務全掛,你半夜爬起來手動處理。
  • 編排系統:偵測到節點失聯,自動把上面的工作負載重新排到其他健康的節點上。你早上起來看到的是一則通知,不是一場事故。

場景二:更新不能停機

  • Compose:up -d 更新服務時,舊容器停掉、新容器啟動,中間有幾十秒的空窗(Java 冷啟動更久)。
  • 編排系統:滾動更新——先起一個新版本、確認它真的能服務、把流量切過去、才關掉一個舊的,如此逐一替換。使用者完全無感。出問題還能一鍵回滾。

場景三:流量暴增

  • Compose:手動改 --scale,而且只能在這一台機器上長,長到機器極限就沒了。
  • 編排系統:依 CPU/記憶體/自訂指標自動增減副本數,不夠時甚至自動增加節點(AWS ch03 的 Auto Scaling 概念,但作用在容器層級)。

場景四:服務多到管不動

  • 5 個服務用 Compose 很舒服。50 個服務、分屬 8 個團隊、跑在 20 台機器上——你需要的是一套統一的「宣告部署意圖」的方式,加上權限隔離、資源配額、統一的監控與日誌。
一句話總結:Compose 讓你「把一組容器跑起來」,Kubernetes 讓你「宣告一個服務應該長什麼樣,然後由系統負責讓它一直是那樣」。差別不在功能多寡,在誰負責維持狀態——前者是你,後者是系統。
🚨 但請務必記住:如果你的服務就是單機規模、能容忍幾分鐘維護停機,Compose + 一台機器是完全合理的正式方案。K8s 的複雜度是真實的成本——很多團隊上了 K8s 之後,維運負擔反而超過它解決的問題。選型看需求,不看流行。

如果這章你只記得一件事,請記這個。

K8s 的每個功能,本質上都是同一個迴圈在跑:
while (true) {
    期望狀態 = 讀取你提交的設定(etcd 裡的 YAML)
    實際狀態 = 觀察叢集現在的樣子
    if (實際 != 期望) {
        採取行動縮小差距
    }
}
這叫做調和迴圈(reconciliation loop),執行它的元件叫 controller。

對照 Compose 就懂了

  • Compose = 命令式的一次性動作:你打 up,它照做,然後就沒事了。之後容器掛掉,除非你設了 restart 策略(那也只是本機 daemon 的簡單重試),沒有人在「持續確保」狀態正確。
  • K8s = 持續運作的宣告式系統:你說「我要 3 個副本」,controller 就一直盯著。有一個掛了 → 現況 2 ≠ 期望 3 → 建一個新的。節點失聯 → 上面的 Pod 全部重新調度。你不需要做任何事,因為系統的職責就是消除差異。

由這一個機制,直接推導出 K8s 的所有招牌能力

  • 自我修復:Pod 掛了 → 副本數不足 → 重建。
  • 滾動更新:你改了映像版本 → 期望狀態變了 → controller 逐步把舊的換成新的。
  • 自動擴縮:HPA 依指標改寫「期望副本數」→ 迴圈自然去達成它。
  • 節點故障轉移:節點沒回報心跳 → 上面的 Pod 視為不存在 → 在別處重建。
💡 這也解釋了 K8s 一個常讓新手困惑的行為:你手動 kubectl delete pod xxx,結果它馬上又出現了(名字不同)。因為你刪的是「現況」,而「期望」還寫著要 3 個——controller 只是盡忠職守。要真的移除,得去改期望狀態(改副本數或刪掉 Deployment)。
⭐ 這個思維叫做宣告式(declarative),跟 ch01 講的「不可變基礎設施」是同一個哲學的延伸:不要描述步驟,描述結果;不要手動修復,讓系統自己收斂。
┌──────────── 控制平面(Control Plane,叢集的大腦)────────────┐
│  kube-apiserver      所有操作的唯一入口(kubectl 打的就是它)    │
│  etcd                儲存所有狀態的資料庫(期望狀態存這裡)      │
│  kube-scheduler      決定新 Pod 該放到哪個節點                  │
│  controller-manager  跑各種控制迴圈的地方 ← 上一張卡的主角       │
└──────────────────────────┬───────────────────────────────────┘
                           │
     ┌─────────────────────┼─────────────────────┐
┌────┴──── Node 1 ────┐ ┌──┴─── Node 2 ────┐ ┌───┴── Node 3 ───┐
│ kubelet             │ │ kubelet          │ │ kubelet         │
│  └ 負責在本機把 Pod  │ │                  │ │                 │
│    跑起來、回報狀態  │ │                  │ │                 │
│ kube-proxy          │ │ kube-proxy       │ │ kube-proxy      │
│  └ 處理 Service 的   │ │                  │ │                 │
│    網路轉發         │ │                  │ │                 │
│ 容器執行環境         │ │ containerd       │ │ containerd      │
│  (containerd)       │ │                  │ │                 │
│ [Pod] [Pod] [Pod]   │ │ [Pod] [Pod]      │ │ [Pod]           │
└─────────────────────┘ └──────────────────┘ └─────────────────┘

各角色的白話職責

  • API Server = 總機:所有人(你、controller、kubelet)都只跟它講話,它負責認證、驗證、寫入 etcd。K8s 沒有元件之間直接互相呼叫——全部透過 API Server,這讓系統極度模組化。
  • etcd = 唯一的事實來源:整個叢集的狀態都在這裡。etcd 掛了叢集就失憶,所以正式環境一定要備份它。
  • Scheduler = 排位人員:看到「有個 Pod 還沒地方去」,依資源需求、親和性規則、節點狀況挑一台。
  • Controller Manager = 一群盡責的管家:Deployment controller、ReplicaSet controller、Node controller… 各自跑自己的調和迴圈。
  • kubelet = 節點上的工頭:接到「你這台要跑這些 Pod」,就呼叫容器執行環境把它們跑起來,並持續回報健康狀況。
  • kube-proxy = 節點上的交通警察:維護轉發規則,讓「連到 Service」的流量正確送到後端的 Pod。
💡 注意「容器執行環境」那一層:K8s 現在用的是 containerd(透過 CRI 介面),不是 Docker。但這完全不影響你——因為你用 Docker 建的映像遵循 OCI 標準(ch02),containerd 一樣跑得動。「K8s 移除 Docker 支援」那則新聞當年嚇壞很多人,實際上對映像使用者毫無影響。

Pod = 最小的部署單位(不是容器!)

  • 一個 Pod 可以裝一個或多個容器,它們共用同一個 network namespace 和儲存 volume。
  • 共用 network namespace 的意思(回扣 ch02):同一個 Pod 裡的容器可以用 localhost 互相連線、共用同一組 port 空間(所以不能撞埠)。
  • 典型用法是「主容器 + 輔助容器(sidecar)」:主容器跑你的 Spring Boot,旁邊掛一個日誌收集器或服務網格代理。
  • 90% 的情況你的 Pod 只有一個容器——那就把 Pod 想成「容器的包裝紙」即可。
  • Pod 是可拋棄的:它會死、會被重建,而且重建後 IP 會變。這正是需要 Service 的原因。

Deployment = 你實際上會寫的東西

你幾乎不會直接建立 Pod,而是宣告一個 Deployment:「我要 3 個這樣的 Pod,用這個映像,更新時請逐步替換。」

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 3                     # ← 期望狀態:我要 3 個
  selector:
    matchLabels: { app: myapp }
  template:                       # ← 每個 Pod 長什麼樣
    metadata:
      labels: { app: myapp }
    spec:
      containers:
        - name: app
          image: myregistry.io/myapp:1.4.2    # 明確版本,不用 latest(ch04)
          ports:
            - containerPort: 8080
          env:
            - name: SPRING_PROFILES_ACTIVE
              value: prod
            - name: SPRING_DATASOURCE_PASSWORD
              valueFrom:
                secretKeyRef: { name: db-secret, key: password }   # 秘密注入(ch10)
          resources:
            requests: { memory: 512Mi, cpu: 250m }
            limits:   { memory: 1Gi,   cpu: 1000m }
          securityContext:                     # ch10 的安全實務
            runAsNonRoot: true
            readOnlyRootFilesystem: true
            capabilities: { drop: [ALL] }

Service = 穩定的入口(解決 Pod IP 會變的問題)

apiVersion: v1
kind: Service
metadata:
  name: myapp
spec:
  selector: { app: myapp }      # 找到所有帶這個標籤的 Pod
  ports:
    - port: 80
      targetPort: 8080
  • Service 有一個固定不變的名稱與虛擬 IP,會把流量負載平衡到符合標籤的所有 Pod 上。
  • 其他服務用 http://myapp 就連得到——跟 Compose 用服務名稱互連是同一個心智模型(ch07),只是背後從單機的 DNS 換成了叢集 DNS + kube-proxy 的轉發規則。
  • 三種類型:ClusterIP(只在叢集內,預設)、NodePort(每個節點開一個埠)、LoadBalancer(雲端商配一顆負載平衡器)。對外的 HTTP 流量通常再上一層用 Ingress 統一處理路由與 TLS。

ch05 講過 Docker 的 HEALTHCHECK。K8s 把它拆成三個用途不同的探測,設錯會導致服務詭異地反覆重啟或流量被丟到還沒準備好的實例上。

① Liveness Probe ——「你還活著嗎?」

  • 失敗的後果:殺掉容器並重建。
  • 用途:偵測死鎖、無限迴圈這類「process 還在但已經廢了」的狀態。
  • ⚠️ 最常見的錯誤:把它指向會依賴資料庫的端點。資料庫短暫抖動 → liveness 失敗 → 你的應用被殺掉重啟 → 重啟後資料庫還是有問題 → 無限重啟迴圈,而且問題根本不在你的應用。Liveness 應該只檢查「這個 process 本身還能運作嗎」。

② Readiness Probe ——「你現在能接工作嗎?」

  • 失敗的後果:從 Service 的後端清單移除,不再送流量給它(但不會殺掉它)。
  • 用途:暫時性的不可用——正在啟動、連線池耗盡、正在載入大量快取。
  • 這才是可以檢查相依服務的地方:資料庫連不上時把自己標記成 not ready,流量會被導到其他健康的實例,等恢復後自動回到輪替。

③ Startup Probe ——「你啟動完了嗎?」

  • 對 Java 應用至關重要。在它成功之前,liveness 和 readiness 都暫停不執行。
  • 解決的問題:Spring Boot 冷啟動要 30~90 秒,如果只設 liveness,應用會在啟動途中被判定為死亡而遭殺害,然後永遠重啟(這是 Java 上 K8s 最經典的翻車現場)。
startupProbe:                      # 給它最多 5 分鐘啟動(30 次 × 10 秒)
  httpGet: { path: /actuator/health/liveness, port: 8080 }
  failureThreshold: 30
  periodSeconds: 10

livenessProbe:                     # 只看 process 本身,不看相依服務
  httpGet: { path: /actuator/health/liveness, port: 8080 }
  periodSeconds: 10
  failureThreshold: 3

readinessProbe:                    # 可以看相依服務(DB、Redis)
  httpGet: { path: /actuator/health/readiness, port: 8080 }
  periodSeconds: 5
  failureThreshold: 3
💡 Spring Boot 有內建支援:開啟 management.endpoint.health.probes.enabled=true,就會提供 /actuator/health/liveness 與 /actuator/health/readiness 兩個端點,語意剛好對應。而且它會在應用開始優雅關機時自動把 readiness 標記為 DOWN,流量提早停止導入——配合 ch05 的 SIGTERM 處理,才能做到真正的零停機更新。

這組設定直接決定你的服務穩不穩、以及叢集的錢有沒有花在刀口上。

resources:
  requests:              # 「我至少需要這麼多」→ 用於「排程決策」
    memory: 512Mi
    cpu: 250m            # 250 milli-core = 0.25 顆 CPU
  limits:                # 「我最多只能用這麼多」→ 用於「執行時限制」(就是 ch02 的 cgroup)
    memory: 1Gi
    cpu: 1000m

兩者的職責完全不同

  • requests = 給 scheduler 看的:決定這個 Pod 能被放到哪台節點。節點的剩餘可分配量是用 requests 加總計算的,跟實際用量無關。
  • limits = 給 kernel 看的:就是 ch02 那個 cgroup 上限,超過就限流(CPU)或砍掉(記憶體)。

設定不當的三種災難

  1. 沒設 requests → scheduler 以為這個 Pod 不吃資源,把一堆 Pod 塞進同一台節點 → 實際跑起來記憶體不夠 → 大規模 OOM。
  2. CPU limit 設太低 → CPU 限流(throttling):process 被強制暫停。對 Java 特別致命——GC 執行緒被限流會造成延遲尖峰,而且從應用日誌上完全看不出原因。
  3. 記憶體 limit 設太低 → OOMKilled(ch02、ch09 的 137)。記憶體和 CPU 不同:CPU 超過是「變慢」,記憶體超過是「立刻死亡」,沒有中間地帶。

Java 應用的建議設定

  • 記憶體:requests 和 limits 設成一樣(獲得 Guaranteed QoS 等級,最不容易在節點資源緊張時被驅逐)。搭配 -XX:MaxRAMPercentage=75(ch09),讓 JVM 自動適應這個上限。
  • CPU:requests 一定要設(保證排程時拿得到);limits 則常見的建議是「設寬鬆一點,或延遲服務乾脆不設」——因為 CPU 限流造成的延遲尖峰,通常比讓它偶爾多用一點 CPU 更糟。
⭐ QoS 等級(節點資源不足時的驅逐順序):
• Guaranteed(requests = limits)→ 最後才被驅逐,正式環境的關鍵服務用這個。
• Burstable(requests < limits)→ 中間。
• BestEffort(兩者都沒設)→ 第一個被踢掉。正式環境絕對不要留 BestEffort。

ch13 教了 Docker 的除錯,這裡是 K8s 版本。好消息是:問題的成因幾乎完全一樣,只是名字不同。

① CrashLoopBackOff(最常見)

  • 意思:容器不斷啟動又不斷崩潰,K8s 用指數退避拉長重試間隔。
  • 成因:跟 ch13 的「一直 Restarting」完全一樣——設定錯誤、相依服務連不上、或 liveness probe 設錯把健康的應用殺掉。
  • 查法:
    kubectl logs <pod> --previous     # ← 關鍵:看「上一次崩潰前」的日誌
    kubectl describe pod <pod>         # 看 Events 區段
    --previous 這個旗標非常重要,因為現在這個容器可能才剛啟動、日誌還是空的。

② ImagePullBackOff / ErrImagePull

  • 成因:映像名稱打錯、標籤不存在、私有 registry 沒設認證(imagePullSecrets)、或架構不符(ch04 的 M1 問題)。
  • 查法:kubectl describe pod 的 Events 會寫出確切的失敗原因。

③ Pending(卡住不動)

  • 成因:scheduler 找不到地方放它——所有節點的可分配資源都不足以滿足你的 requests、或有 taint/親和性規則擋住、或 PVC 還沒綁定。
  • 查法:kubectl describe pod 會直接寫「0/5 nodes are available: insufficient memory」。

④ OOMKilled

  • 跟 ch09 一模一樣:記憶體超過 limits。kubectl describe pod 的 Last State 會顯示 OOMKilled、exit code 137。
  • Java 的檢查順序:有沒有設 MaxRAMPercentage?limits 是不是太小(Spring Boot 建議至少 512Mi)?是不是真的洩漏?

⑤ Running 但沒有流量進來

  • 成因:readiness probe 沒通過(Pod 活著但沒被加進 Service 的後端),或 Service 的 selector 標籤跟 Pod 的 labels 對不上。
  • 查法:
    kubectl get pods            # READY 欄是 0/1 就是 readiness 沒過
    kubectl get endpoints myapp # 後端清單是空的?那就是選不到 Pod

指令對照(你已經會 Docker 了,這些是同義詞)

docker ps                 → kubectl get pods
docker logs -f X          → kubectl logs -f X
docker exec -it X bash    → kubectl exec -it X -- bash
docker inspect X          → kubectl describe pod X
docker stats              → kubectl top pods
docker events             → kubectl get events --sort-by=.lastTimestamp
(無對應)                 → kubectl port-forward pod/X 8080:8080  ← 本機直連 Pod,除錯神器

你現在的位置

你已經有整個容器基礎(前 13 章)+ K8s 的核心心智模型。這比「只會照著 YAML 抄」的人扎實得多——因為 K8s 的所有行為都建立在容器的原理上(Pod 共用 namespace、limits 就是 cgroup、映像就是 OCI image)。

建議的下一步(照順序)

  1. 本機跑起一個叢集:kind(Docker 裡跑 K8s,最輕)、minikube、或 Docker Desktop 內建的 K8s。把 ch12 那套 Compose 環境翻成 Deployment + Service + ConfigMap + Secret 手動做一遍——這個練習的學習密度最高。
  2. ConfigMap 與 Secret:設定與秘密的注入方式(對應 ch09、ch10 的環境變數與 secret)。
  3. 持久化儲存:PersistentVolume/PVC/StorageClass(對應 ch06 的 volume,但多了「跨節點」的複雜度)。
  4. Ingress 與流量管理:對外路由、TLS 憑證(cert-manager)。
  5. 套件化:Helm(樣板化與版本管理)或 Kustomize(疊加式覆蓋,概念很像 ch11 的 compose override)。
  6. GitOps:ArgoCD/Flux——把 Git 當成期望狀態的唯一來源,控制迴圈負責讓叢集與 Git 保持一致。這是本章「控制迴圈」思想的最高展現,也是現在 SRE 的主流做法。
  7. 可觀測性:Prometheus(指標)+ Grafana(視覺化)+ Loki/ELK(日誌)+ OpenTelemetry(追蹤)。SRE 的核心技能其實在這裡——不是部署,是「知道系統現在好不好、壞了為什麼」。

託管 K8s:不要自己架控制平面

  • AWS EKS、Google GKE、Azure AKS:控制平面由雲端商維護(etcd 備份、版本升級、高可用),你只管工作節點與應用。
  • 自架控制平面是一項專職工作,對絕大多數團隊都不划算。
  • 你在 AWS ch08 學過的 ECS/EKS/Fargate 選型,現在應該完全看得懂了:ECS 是 AWS 自家的較簡單編排、EKS 是託管的標準 K8s、Fargate 是「連工作節點都不用管」的執行模式。
⭐ 給未來 SRE 的一句話:K8s 只是工具,SRE 的核心是「用工程手段維持系統可靠性」——SLI/SLO 的訂定、錯誤預算、事故響應與檢討、容量規劃、把重複勞動自動化掉。容器與 K8s 是你的基礎設施語言,但真正的價值在於你能不能回答「這個系統現在健康嗎?為什麼?下次怎麼避免?」
Compose vs Kubernetes:概念對照
Compose 的東西K8s 的對應差別在哪
serviceDeployment(+它管的 Pod)K8s 多了副本數、滾動更新、自我修復
容器Pod(可含多個容器)Pod 內共用 network namespace,可用 localhost 互連
服務名稱互連Service(ClusterIP)同樣用名稱,但多了負載平衡與跨節點
ports 對外Service(NodePort/LoadBalancer) 或 IngressIngress 可統一處理路由與 TLS
environmentenv / ConfigMapConfigMap 可跨多個服務共用
.env 秘密Secret可掛成檔案、有 RBAC 權限控管
volumesPersistentVolumeClaim多了「跨節點」的儲存問題要處理
healthcheckliveness / readiness / startup 三種 probe用途分離:殺掉重建 vs 停止導流 vs 等啟動
deploy.resourcesresources.requests / limitsrequests 影響排程,limits 才是 cgroup 限制
restart: unless-stopped(內建)控制迴圈自動維持不只重啟,還能換節點重建
三種 Probe 的分工(設錯會出事)
Probe問題失敗的後果可以檢查相依服務嗎
Startup啟動完了嗎?超過次數才殺;成功前暫停其他兩種不需要(Java 一定要設)
Liveness你還活著嗎?殺掉容器並重建❌ 絕對不要!DB 抖動會害你無限重啟
Readiness現在能接工作嗎?從 Service 後端移除,不再導流(不殺)✅ 這才是檢查 DB/Redis 的地方
Pod 狀態異常速查
狀態意思常見原因第一個指令
CrashLoopBackOff反覆啟動又崩潰設定錯誤、相依連不上、liveness 設錯kubectl logs <pod> --previous
ImagePullBackOff拉不到映像名稱/標籤錯、缺 imagePullSecrets、架構不符kubectl describe pod(看 Events)
Pending排不進任何節點資源不足、taint/親和性擋住、PVC 未綁定kubectl describe pod
OOMKilled記憶體超過 limitslimits 太小、JVM 沒設 MaxRAMPercentagedescribe 看 Last State(exit 137)
Running 但 READY 0/1活著但不接流量readiness 沒過,或 Service selector 對不上kubectl get endpoints <service>

練習題 點選選項查看解析

0 / 10
01 / 10
Kubernetes 最核心的設計思想是什麼?
A 把容器分散到多台機器上執行
B 控制迴圈:你宣告期望狀態,controller 持續比對現況並自動修正差異——自我修復、滾動更新、自動擴縮全都是這一個機制的展現
C 用 YAML 取代命令列操作
D 提供比 Docker 更快的容器執行環境
解析
這是理解 K8s 的鑰匙。對比 Compose:Compose 是命令式的一次性動作(up 完就沒事了),K8s 是持續運作的宣告式系統(controller 一直盯著)。這也解釋了為什麼你 kubectl delete pod 之後它馬上又出現——你刪的是現況,期望狀態還寫著要 3 個。
02 / 10
關於 Pod,正確的敘述是?
A Pod 就是容器的另一個名字
B Pod 是最小部署單位,可含一個或多個容器,它們共用同一個 network namespace 與 volume——所以同 Pod 內的容器可以用 localhost 互連
C 一個 Pod 只能包含一個容器
D Pod 有固定不變的 IP 位址
解析
共用 network namespace 正是 ch02 的概念在 K8s 的應用:同 Pod 的容器共用網路堆疊與 port 空間(所以不能撞埠)。典型用法是主容器+sidecar(日誌收集、服務網格代理)。另外 Pod 是可拋棄的,重建後 IP 會變——這正是需要 Service 提供穩定入口的原因。
03 / 10
為什麼 Java 應用在 K8s 上「一定要」設定 startupProbe?
A 為了加快啟動速度
B 因為 Spring Boot 冷啟動要 30~90 秒,只設 liveness 的話應用會在啟動途中被判定死亡而遭殺害,然後陷入永遠重啟;startupProbe 成功前會暫停 liveness 與 readiness
C 因為 K8s 規定必須設定
D 為了讓 Service 更快發現 Pod
解析
這是 Java 上 K8s 最經典的翻車現場。startupProbe 給足啟動寬限期(例如 failureThreshold: 30 × periodSeconds: 10 = 5 分鐘),成功後才交棒給另外兩個 probe。這和 ch05 的 Docker HEALTHCHECK --start-period 是同一個問題的不同解法。
04 / 10
livenessProbe 為什麼「絕對不應該」檢查資料庫連線?
A 因為會增加資料庫負載
B 因為 liveness 失敗會殺掉容器:資料庫短暫抖動就導致應用被殺掉重啟,重啟後資料庫仍有問題,形成無限重啟迴圈——而問題根本不在你的應用
C 因為檢查太慢會逾時
D 因為資料庫連線需要密碼
解析
職責要分清楚:liveness 只檢查「這個 process 本身還能運作嗎」(偵測死鎖、無限迴圈);readiness 才是檢查相依服務的地方——失敗時只是從 Service 後端移除、停止導流,不會殺掉容器,等恢復後自動回到輪替。
05 / 10
resources 的 requests 與 limits 差別是?
A requests 是初始值,limits 是最大值,兩者作用相同
B requests 給 scheduler 決定 Pod 能放到哪台節點(節點可分配量是用 requests 加總算的);limits 是執行時的 cgroup 上限,超過就限流或砍掉
C requests 用於 CPU,limits 用於記憶體
D requests 是建議值,K8s 可以忽略
解析
兩者的服務對象完全不同:requests 給排程器看、limits 給核心看(就是 ch02 的 cgroup)。沒設 requests 的後果很嚴重——scheduler 以為這個 Pod 不吃資源,把一堆 Pod 塞進同一台節點,實際跑起來就大規模 OOM。
06 / 10
Java 應用在 K8s 上的資源設定,較佳的做法是?
A CPU 和記憶體的 limits 都設得越低越省錢
B 記憶體 requests 與 limits 設成一樣(取得 Guaranteed QoS,最不易被驅逐)並搭配 -XX:MaxRAMPercentage;CPU requests 一定要設,limits 則宜寬鬆或不設以避免 GC 被限流
C 完全不設,讓 K8s 自動判斷
D 只設 limits 不設 requests
解析
記憶體與 CPU 的失敗模式不同:CPU 超過是「變慢」(throttling,會造成 GC 延遲尖峰且從應用日誌看不出原因),記憶體超過是「立刻死亡」(OOMKilled),沒有中間地帶。另外 BestEffort(兩者都沒設)在節點資源緊張時第一個被驅逐,正式環境絕對不要用。
07 / 10
Pod 狀態是 CrashLoopBackOff,第一個該執行的指令是?
A kubectl delete pod 讓它重建
B kubectl logs <pod> --previous——看「上一次崩潰前」的日誌,因為現在這個容器可能才剛啟動、日誌還是空的
C kubectl scale 增加副本數
D kubectl rollout restart
解析
--previous 這個旗標是關鍵。接著用 kubectl describe pod 看 Events 區段。成因和 ch13 的「一直 Restarting」完全一樣:設定錯誤、相依服務連不上、或 liveness probe 設錯把健康的應用殺掉。刪掉 Pod 只會讓它重建後再次崩潰。
08 / 10
Pod 顯示 Running 但 READY 是 0/1,而且沒有流量進來。最可能的原因是?
A 節點資源不足
B readiness probe 沒通過,Pod 沒被加進 Service 的後端清單;也可能是 Service 的 selector 標籤與 Pod labels 對不上
C 映像拉取失敗
D 容器被 OOMKilled
解析
Running 代表容器在跑,READY 代表「可以接流量」——兩者是不同的事。用 kubectl get endpoints <service> 檢查:如果後端清單是空的,就是選不到 Pod(標籤不符)或所有 Pod 都 not ready。
09 / 10
K8s 現在使用 containerd 而非 Docker 作為容器執行環境,這對你的映像有什麼影響?
A 必須用 containerd 重新建置所有映像
B 沒有影響——Docker 建的映像遵循 OCI 標準,containerd 一樣能執行
C 映像必須轉換格式才能使用
D 只有 Alpine 基底的映像可以使用
解析
「K8s 移除 Docker 支援」那則新聞當年嚇壞很多人,但對映像使用者毫無影響。原因回到 ch02:容器是一組核心功能的組合使用,業界把它標準化成 OCI 規範,所以 Docker、containerd、Podman、CRI-O 產出與執行的映像彼此相容。
10 / 10
關於是否該導入 Kubernetes,較合理的判斷是?
A 所有專案都應該用 K8s,這是業界標準
B 看實際需求:如果服務是單機規模、能容忍幾分鐘維護停機,Compose 加一台機器就是合理的正式方案;K8s 的複雜度是真實成本,很多團隊導入後維運負擔反而超過它解決的問題
C 只要有兩個以上的容器就該用 K8s
D 只有大公司才能使用 K8s
解析
K8s 解決的是「機器掛了要自己搬家」「更新不能停機」「流量大了自動擴容」「幾十個服務要統一管理」這些具體問題。沒有這些問題卻導入,就是用複雜度換不到價值。選型看需求,不看流行——這也是工程判斷力的一部分。

點擊卡片翻面查看答案,共 12 張。

QUESTION
K8s 的核心思想(只記一件事的話)?
點擊翻面
ANSWER
控制迴圈(reconciliation loop)
while(true) { 比對期望狀態 vs 實際狀態,有差就修正 }
→ 自我修復、滾動更新、自動擴縮全是這一個機制的展現
點擊翻回
QUESTION
Compose 與 K8s 的本質差異?
點擊翻面
ANSWER
Compose:命令式的一次性動作(up 完就沒事了)
K8s:持續運作的宣告式系統(controller 一直盯著)
差別在「誰負責維持狀態」——前者是你,後者是系統
點擊翻回
QUESTION
為什麼 kubectl delete pod 之後它又出現?
點擊翻面
ANSWER
你刪的是「現況」,但「期望狀態」還寫著要 N 個
controller 盡忠職守地補回來
要真的移除 → 改期望狀態(改副本數或刪 Deployment)
點擊翻回
QUESTION
控制平面的四個元件?
點擊翻面
ANSWER
API Server:唯一入口(所有元件都只跟它講話)
etcd:唯一事實來源(狀態資料庫,要備份)
Scheduler:決定 Pod 放哪台
Controller Manager:跑各種控制迴圈
點擊翻回
QUESTION
Pod 是什麼?跟容器差在哪?
點擊翻面
ANSWER
最小部署單位,可含多個容器
共用同一個 network namespace 與 volume → 可用 localhost 互連
典型:主容器 + sidecar
Pod 可拋棄,重建後 IP 會變 → 所以需要 Service
點擊翻回
QUESTION
三種 Probe 的分工?
點擊翻面
ANSWER
Startup:啟動完了嗎(成功前暫停另外兩個,Java 必設)
Liveness:還活著嗎(失敗 → 殺掉重建)❌ 不要查 DB
Readiness:能接工作嗎(失敗 → 停止導流,不殺)✅ 這裡查 DB
點擊翻回
QUESTION
liveness 檢查資料庫會怎樣?
點擊翻面
ANSWER
DB 短暫抖動 → liveness 失敗 → 應用被殺掉重啟
重啟後 DB 還是有問題 → 無限重啟迴圈
而問題根本不在你的應用
點擊翻回
QUESTION
requests vs limits?
點擊翻面
ANSWER
requests:給 scheduler 決定放哪台(節點可分配量用它加總)
limits:執行時的 cgroup 上限(超過 → CPU 限流/記憶體砍掉)
沒設 requests → 一堆 Pod 擠同一節點 → 大規模 OOM
點擊翻回
QUESTION
Java 在 K8s 的資源設定建議?
點擊翻面
ANSWER
記憶體:requests = limits(Guaranteed QoS,最不易被驅逐)+ MaxRAMPercentage=75
CPU:requests 必設;limits 宜寬鬆或不設(限流會造成 GC 延遲尖峰)
BestEffort(都沒設)第一個被驅逐
點擊翻回
QUESTION
五個必認的 Pod 異常狀態?
點擊翻面
ANSWER
CrashLoopBackOff → logs --previous
ImagePullBackOff → describe 看 Events
Pending → 排不進去(資源/taint/PVC)
OOMKilled → exit 137
Running 但 0/1 → readiness 沒過或 selector 對不上
點擊翻回
QUESTION
kubectl 與 docker 的指令對照?
點擊翻面
ANSWER
ps→get pods、logs→logs、exec→exec -- 、inspect→describe pod
stats→top pods、events→get events
額外神器:kubectl port-forward pod/X 8080:8080
點擊翻回
QUESTION
SRE 方向的下一步?
點擊翻面
ANSWER
kind/minikube 本機叢集 → 把 ch12 的 compose 翻成 Deployment+Service
→ ConfigMap/Secret → PVC → Ingress
→ Helm/Kustomize → GitOps(ArgoCD) → 可觀測性(Prometheus/Grafana/OTel)
託管用 EKS/GKE,不要自己架控制平面
點擊翻回