一個 Pod 拖垮一整台 Node
凌晨四點,你被告警吵醒:
「整個 cluster 不正常,Pod 一直被驅逐(Evicted)。」
你登入查看:
\\\bash\
kubectl top nodes
NAME CPU% MEMORY%
worker-1 95% 98% ← 這台快爆了
\\
\\\bash\
kubectl top pods -n production
NAME CPU MEMORY
data-job-xxx 100m 14Gi ← 兇手在這
\\
一個 \data-job\ Pod 沒設記憶體限制,吃光了 Node 的 16GB。其他 Pod 全部跟著掛——這就是「鄰居效應」。
解法:Resource requests + limits。
requests vs limits — 兩個概念,一次搞清楚
| requests(請求) | limits(限制) | |
|---|---|---|
| 比喻 | 預約座位 | 最多坐幾個 |
| 影響 | Scheduler 排程依據 | 容器執行時的天花板 |
| 超過怎樣 | 不會超過(保證給你) | CPU 被節流、記憶體 OOMKilled |
\\yaml
spec:
containers:
- name: api
image: my-api:1.0
resources:
requests:
cpu: "100m" # 0.1 顆 CPU
memory: "64Mi" # 保底
limits:
cpu: "500m" # 最多 0.5 顆
memory: "128Mi" # 超過就 OOMKilled
\\\
CPU 單位
- \
1\= 1 顆 CPU - \
500m\= 0.5 顆(500 millicpu) - \
100m\= 0.1 顆
記憶體單位
- \
Mi\= MiB(1024 進位) - \
Gi\= GiB - \
128M\跟 \128Mi\不一樣(前者是 1000 進位)——永遠用 \Mi\/ \Gi\
CPU 超過 vs 記憶體超過
關鍵差別:
| CPU 超過 limits | 記憶體超過 limits | |
|---|---|---|
| 後果 | 節流(Throttle)——程式變慢 | OOMKilled——直接殺掉 |
| 可恢復 | 可以,等流量降 | 不可,要重啟 |
三種 QoS(Quality of Service)
K8s 根據你怎麼設 requests/limits,給 Pod 一個 QoS 等級。Node 資源不夠時,這個等級決定誰先被殺。
| QoS | 條件 | 被殺優先順序 |
|---|---|---|
| Guaranteed | requests = limits(每個容器都設且相等) | 最後被殺 |
| Burstable | 有設 requests,但 requests ≠ limits | 中間 |
| BestEffort | 完全沒設 requests 和 limits | 最先被殺 |
BestEffort 為什麼危險
\\\yaml\
# 沒寫 resources,就是 BestEffort
spec:
containers:
- name: api
image: my-api:1.0
# 沒有 resources!
\\
當 Node 資源緊張時,K8s 第一個犧牲的就是這種 Pod。生產環境絕對不能 BestEffort。
該選 Guaranteed 還是 Burstable?
- Guaranteed(requests = limits):資源保證、最穩定,但浪費——預留的資源用不完也鎖住
- Burstable(requests < limits):彈性好,平常用 requests 的量,需要時可 burst 到 limits
實作:手動觸發 OOMKilled
\\\yaml\
# oom-demo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: oom-demo
spec:
replicas: 1
selector:
matchLabels:
app: oom-demo
template:
metadata:
labels:
app: oom-demo
spec:
containers:
- name: stress
image: progrium/stress
args: ["--vm", "1", "--vm-bytes", "256M", "--timeout", "60s"]
resources:
limits:
memory: "128Mi" # 限 128,但程式要吃 256
\\
\\\bash\
kubectl apply -f oom-demo.yaml
kubectl get pods -w
\\
你會看到:
\\\\
NAME READY STATUS RESTARTS
oom-demo 0/1 OOMKilled 1
oom-demo 0/1 CrashLoopBackOff 2
oom-demo 0/1 CrashLoopBackOff 3 ← 一直重啟
\\
確認是不是 OOMKilled
\\\bash\
kubectl describe pod oom-demo-xxx
# Last State: Terminated
# Reason: OOMKilled ← 在這
# Exit Code: 137
\\
Exit Code 137 = SIGKILL(128 + 9),記憶體超標被強制殺掉的標誌。
看到 OOMKilled 怎麼辦?
排查順序:
kubectl describe pod\ 確認是 OOMKilledkubectl top pod\ 看實際用量- 如果尖峰流量正常但被殺 → 加大 limits
- 如果用量持續成長不下降 → 程式有 leak,先修 bug
慘案案例:把 limits 設太小(像 64Mi)跑 Java 應用,JVM 啟動就炸。Java 應用 limits 至少 512Mi 起跳。
重點整理
- requests = 保底(排程依據),limits = 天花板(執行限制)
- CPU 超過 → 節流,記憶體超過 → OOMKilled
- 三種 QoS:Guaranteed > Burstable > BestEffort
- 生產環境至少要 Burstable,BestEffort 第一個被殺
- Exit Code 137 = OOMKilled 的標誌
- 看到 OOMKilled,先分清楚是「limits 設太小」還是「程式有 leak」
下一步
設好 Resource limits,單一 Pod 不會拖垮 Node。但流量爆漲怎麼辦?
雙 11 流量 10 倍,固定 3 個 Pod 撐不住——該自動擴容了。
下一篇:HPA 自動擴縮 — K8s 怎麼根據 CPU 自動加 Pod
📅 下一篇:HPA 自動擴縮:K8s 怎麼根據 CPU 自動加 Pod
流量爆漲手動擴容來不及——HPA 看著 CPU/Memory 自動加 Pod,還配壓測實測。
📚 完整系列總覽:K8s 系列教學首頁(共 40 課,按學習路徑順序排)