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 部分是否挂上 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。
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-troubleshooting | Pod 排障全场景(CrashLoopBackOff / ImagePullBackOff / Pending / Terminating) |
| k8s-troubleshooting-principles | K8s 生产排障基本原则与快速定位流程 |
| k8s-resource-limits-configuration | 资源限制配置(Request / Limit / QoS / CPU Throttling) |
| k8s-probes-guide | 三探针完整配置指南(startupProbe / livenessProbe / readinessProbe) |
| k8s-rolling-update-pitfalls | 滚动更新无损发布误区(优雅终止 / 连接排空) |
| linux-disk-space-troubleshooting | Linux 磁盘空间排查(df / du / lsof / journalctl) |
| k8s-production-incident-case-studies | K8s 生产 10 大故障复盘(PDB / 滚动更新 / HPA 等实战案例) |
| k8s-top10-troubleshooting-checklist | K8s 高频问题一站式排查清单 |