返回首页

K8s Pod Evicted 驱逐 — 根因排查与运维应对三板斧

📅 创建于 2026-07-31 🔄 更新于 2026-09-02 📝 870 字

K8s Pod Evicted 驱逐

来源:老郭 | 发布日期:2026-06-11

Pod 被 Evicted,是节点资源快耗尽时,kubelet 作为节点"管家"为保住节点自身和其他更重要的 Pod,强行终止该 Pod。这是 K8s 的自我保护机制——Node-pressure Eviction。

Evicted vs Failed

状态 触发主体 主要原因 典型场景
Evicted kubelet 主动驱逐 节点资源不足(内存、磁盘、inode 等) 节点内存飙到 90%、磁盘写满
Failed Pod 内容器自身 应用启动失败、崩溃、镜像拉不下来 代码 bug、配置错误、依赖挂了

一句话:Evicted 是"系统让你死",Failed 是"你自己作死"。

驱逐信号与默认硬阈值

kubelet 盯着节点上的关键指标,一旦跌破阈值就触发驱逐。以下基于 K8s v1.24+:

驱逐信号 含义 默认硬驱逐阈值 最坑场景
memory.available 可用内存 < 100Mi 设了 limits 没设 requests,内存超卖严重
nodefs.available 根磁盘可用空间 < 10% /var 目录写满,kubelet 自己都快挂了
nodefs.inodesFree 根磁盘可用 inode < 5% 大量小文件耗光 inode——df -h 看不出问题
imagefs.available 容器镜像存储可用空间 < 15% 镜像没及时清理,/var/lib/containerd 写满
pid.available 可用 PID 数 < 10% 进程 fork 炸弹或容器泄漏

硬驱逐 vs 软驱逐

驱逐类型 配置参数 Grace Period 适用场景
硬驱逐 --eviction-hard 0s(立即终止) 资源严重告急
软驱逐 --eviction-soft 可配置 资源紧张但不致命,给缓冲期

驱逐优先级:QoS 决定生死

kubelet 按 QoS 等级从低到高驱逐,BestEffort 最先挨刀:

BestEffort(没设 requests/limits)→ 第一个被驱逐
Burstable(requests < limits)→ 中等
Guaranteed(requests == limits)→ 最后

注意:QoS 决定 kubelet 节点压力驱逐的优先击杀顺序,PriorityClass 决定调度器在资源紧张时的驱逐顺序。两者独立,可能冲突。生产建议:QoS 设 Guaranteed,PriorityClass 留给关键系统组件(如 kube-dns)。

被低估的坑:ephemeral-storage

很多团队只给 CPU/Memory 设配额,忘了本地临时存储。容器日志、/tmp、emptyDir、镜像可写层都会消耗本地盘,超出阈值即被驱逐。K8s v1.8+ 支持 ephemeral-storage 资源配额。

emptyDir 必须显式限容: emptyDir 本质是借节点磁盘做局部缓存,sidecar/业务进程持续写入、无大小限制无轮转时,单个 Pod 就能把节点磁盘慢慢打满并触发驱逐、殃及同节点其他业务。至少显式限制:volumes: [{name: log, emptyDir: {sizeLimit: 500Mi}}]。边界:emptyDir 只适合临时数据,不可丢失数据请用 PVC;节点磁盘监控要能追到 Pod 维度;日志/缓存都要有轮转与清理策略。

事故现场排查(四步法)

假设监控报警:kube_pod_status_phase{phase="Evicted"} > 0

第一步:统计量级

# 查看所有 Evicted Pod
kubectl get pods -A | grep Evicted

# 统计总数
kubectl get pods -A --field-selector=status.phase=Failed -o json \
  | jq -r '.items[] | select(.status.reason=="Evicted") | .metadata.name' | wc -l

# 按 namespace 统计
kubectl get pods -A --field-selector=status.phase=Failed -o json \
  | jq -r '.items[] | select(.status.reason=="Evicted") | .metadata.namespace' \
  | sort | uniq -c | sort -rn

第二步:揪出肇事节点

# 找出 Evicted Pod 最多的节点
kubectl get pods -A --field-selector=status.phase=Failed -o json \
  | jq -r '.items[] | select(.status.reason=="Evicted") | .spec.nodeName' \
  | sort | uniq -c | sort -rn

# 检查节点状态
kubectl describe node <node-name>

重点看 Conditions 部分是否挂上 MemoryPressure 或 DiskPressure 标签。

第三步:挖出驱逐原因

kubectl describe pod <pod-name> -n <namespace>

Events 里会明确告诉你原因:

  • The node was low on resource: ephemeral-storage → 磁盘临时存储吃紧
  • The node had condition: DiskPressure → 磁盘整体压力大
  • Pod was evicted due to memory pressure → 内存不够

第四步:节点层面验证

# 磁盘使用率和 inode(df -h 看不出的 inode 问题这里一目了然)
df -h && df -i

# kubelet 日志
journalctl -u kubelet -f --since "10 minutes ago"

# 容器运行时日志
journalctl -u containerd -f --since "10 minutes ago"

彩蛋:df -h 看不出 inode 耗尽。df -i 是 inode 专用,别漏了。云厂商默认系统盘 inode 有限,日志密集型应用很容易撑爆。

应急处理

清理 Evicted Pod 僵尸

# 清理单个 namespace
kubectl delete pods --namespace prod-blue --field-selector=status.phase=Failed

# 建议先 --dry-run=server 模拟再真删

排查资源占用

# 磁盘占用大户
du -sh /var/lib/kubelet/* 2>/dev/null | sort -hr | head -20
du -sh /var/lib/containerd/* 2>/dev/null | sort -hr | head -20

# 内存/CPU 占用 Top Pod
kubectl top pods -A --sort-by=memory | head -20
kubectl top pods -A --sort-by=cpu | head -20

封锁问题节点

kubectl cordon <node-name>
kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data

云上环境(EKS/GKE/AKS)最快方式是滚动替换节点。注意 PDB 限制——drain 可能因 PDB 卡住,需临时放宽或手动删除特定 Pod。

根因修复

1. 补齐资源配额(90% 情况的根因)

resources:
  requests:
    memory: "256Mi"
    cpu: "250m"
    ephemeral-storage: "1Gi"    # 必须加
  limits:
    memory: "512Mi"
    cpu: "500m"
    ephemeral-storage: "2Gi"

2. 镜像垃圾回收

# /var/lib/kubelet/config.yaml
imageGCHighThresholdPercent: 85
imageGCLowThresholdPercent: 80

3. 容器日志大小控制

# /etc/containerd/config.toml
[plugins."io.containerd.grpc.v1.cri".containerd]
  max_container_log_line_size = 16384

日志轮转交给 sidecar 或节点级 logrotate。

kubelet 自带容器日志轮转(推荐先确认): 不需要额外装 logrotate,kubelet 就能按大小/份数轮转容器日志。确认当前发行版/托管平台支持这些字段,配置修改影响节点级容器日志保留,需要灰度节点验证:

# kubelet 配置片段
containerLogMaxSize: 50Mi
containerLogMaxFiles: 5

宿主机视角查容器日志(很多「删了没释放」发生在这一层): 容器日志常由 kubelet 或运行时在宿主机写入,应用容器里删自己看到的路径,不一定影响宿主机 JSON 日志。应分别检查 /var/log/pods、/var/log/containers 与运行时存储;不要手工删除 overlay2/containerd 元数据目录:

sudo du -xhd2 /var/log/pods 2>/dev/null | sort -h | tail -n 30
sudo find /var/log/pods -xdev -type f -name '*.log' -size +500M \
  -printf '%s %p\n' 2>/dev/null | sort -nr | numfmt --field=1 --to=iec
sudo lsof -nP +L1 /var/lib/containerd 2>/dev/null | head -n 50

定位某个容器实际落在宿主机哪个挂载上,用 crictl 拿宿主 PID 再 nsenter 进它的 mount namespace,不要直接进入未知 namespace 删文件:

container_id=$(sudo crictl ps --name myapp -q | head -n1)
host_pid=$(sudo crictl inspect "$container_id" | jq -r '.info.pid')
sudo nsenter -t "$host_pid" -m -- findmnt -T /var/log/myapp/app.log

宿主机视角的 deleted FD 证据链见 linux-deleted-file-disk-space-reclaim。

4. PriorityClass 保护核心服务

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority
value: 1000000
globalDefault: false
description: "关键业务,驱逐时最后考虑"

在 Pod spec 中引用:priorityClassName: high-priority

5. PodDisruptionBudget(PDB)

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: critical-app-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: critical-app

注意:kubelet 节点压力驱逐不遵守 PDB!PDB 只能拦住 kubectl drain 等 API 发起的驱逐,无法阻止节点资源紧张时的硬驱逐。该配资源配额还是要配。

Prometheus 告警

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: pod-evicted-rules
  namespace: monitoring
spec:
  groups:
  - name: pod-evicted.rules
    rules:
    - alert: PodEvictedCountIncreased
      expr: sum(kube_pod_status_phase{phase="Evicted"}) by (namespace) > 5
      for: 2m
      labels:
        severity: critical
      annotations:
        summary: "命名空间 {{ $value }} 个 Evicted Pod"

关联页面

页面关联点
pod-troubleshootingPod 排障全场景(CrashLoopBackOff / ImagePullBackOff / Pending / Terminating)
k8s-troubleshooting-principlesK8s 生产排障基本原则与快速定位流程
k8s-resource-limits-configuration资源限制配置(Request / Limit / QoS / CPU Throttling)
k8s-probes-guide三探针完整配置指南(startupProbe / livenessProbe / readinessProbe)
k8s-rolling-update-pitfalls滚动更新无损发布误区(优雅终止 / 连接排空)
linux-disk-space-troubleshootingLinux 磁盘空间排查(df / du / lsof / journalctl)
linux-deleted-file-disk-space-reclaim幽灵文件深度排查 — deleted FD / mmap / 继承 FD / 容器 namespace 四类「删了没释放」的证据链
k8s-production-incident-case-studiesK8s 生产 10 大故障复盘(PDB / 滚动更新 / HPA 等实战案例)
k8s-top10-troubleshooting-checklistK8s 高频问题一站式排查清单