返回首页

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

📅 创建于 2026-07-31 🔄 更新于 2026-07-31 📝 725 字

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 资源配额。

事故现场排查(四步法)

假设监控报警: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 部分是否挂上 MemoryPressureDiskPressure 标签。

第三步:挖出驱逐原因

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。

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)
k8s-production-incident-case-studiesK8s 生产 10 大故障复盘(PDB / 滚动更新 / HPA 等实战案例)
k8s-top10-troubleshooting-checklistK8s 高频问题一站式排查清单