K8s · 維運與部署 · 第 31 課 · · 10min read

Resource limits 與 QoS:OOMKilled 是什麼?怎麼避免一個 Pod 吃光記憶體?

一個 Pod 沒設限制,吃光 Node 記憶體,整台機器跟著倒。requests 是「保證有」、limits 是「最多吃」,配出 Guaranteed / Burstable / BestEffort 三種 QoS。

#Kubernetes #Resource #limits #QoS #OOMKilled
章節目錄 · 8

一個 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——直接殺掉
可恢復可以,等流量降不可,要重啟
這是因為:CPU 是可壓縮資源(compressible),記憶體不是。記憶體不夠就只能殺。

三種 QoS(Quality of Service)

K8s 根據你怎麼設 requests/limits,給 Pod 一個 QoS 等級。Node 資源不夠時,這個等級決定誰先被殺

QoS條件被殺優先順序
Guaranteedrequests = 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
實務上絕大多數選 Burstable——平常省資源,尖峰能爆量。

實作:手動觸發 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\ 確認是 OOMKilled

  • \kubectl top pod\ 看實際用量

  • 判斷:是 limits 太小,還是程式 memory leak?

  • - 如果尖峰流量正常但被殺 → 加大 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 課,按學習路徑順序排)