一、Pod Evicted 是什么:kubelet 的”保命”机制
Pod Evicted(Pod 被驱逐)是 Kubernetes 生产环境最常见的异常之一:当节点在内存、磁盘或 PID 资源告急时,kubelet 会主动杀掉并驱逐低优先级 Pod 来保障节点本身不被拖垮。本文从驱逐机制、定位命令到根治方案,手把手带你排查 Pod Evicted,并区分 DiskPressure、MemoryPressure 与主动维护驱逐三种场景,让你下次看到 Evicted 状态不再慌。
和 节点 NotReady、ImagePullBackOff 不同,Evicted 通常不是镜像或网络问题,而是资源天花板被触顶。Pod 消失后会被控制器(Deployment/StatefulSet)立刻重建,于是你看到的现象是”Pod 反复被干掉又起来”,业务表现为间歇性 5xx 或请求抖动。
二、为什么偏偏是你被驱逐:QoS 与驱逐优先级
kubelet 驱逐谁、留谁,遵循 Kubernetes 的 QoS(服务质量)等级。理解了等级,就知道为什么隔壁 Pod 稳如老狗,你的却”首当其冲”。
2.1 三类 QoS 等级
| QoS 等级 | requests/limits 设置 | 驱逐优先级 | 典型场景 |
|---|---|---|---|
| Guaranteed | CPU 与内存都设了相等且非 0 的 requests=limits | 最后被驱逐 | 核心数据库、支付服务 |
| Burstable | 至少一类资源设了 requests(limits 可不设或 > requests) | 中间 | 普通业务服务 |
| BestEffort | 完全不设置任何 requests/limits | 最先被驱逐 | 临时任务、批处理 Job |
2.2 驱逐顺序的两个维度
当节点触发驱逐阈值时,kubelet 先在同一 QoS 内按资源实际使用超出 requests 的比例排序(用得越狠越先走),再跨 QoS 按 BestEffort → Burstable → Guaranteed 的顺序清理。换句话说:不设 requests 的 Pod 永远第一个倒下。
2.3 主动驱逐:drain 与维护窗口
除了资源触发的被动驱逐,运维执行 kubectl drain 做节点升级、打补丁或换盘时,也会主动驱逐节点上的所有 Pod。这类 Evicted 是”计划内”的:kubelet 先发 SIGTERM,等优雅退出超时后再强制删除,并由调度器在其他节点重建。区分二者很简单——被动驱逐的 Message 一定带 MemoryPressure / DiskPressure 等条件;主动驱逐则来自 drain 流程,节点会先进入 SchedulingDisabled(cordon)状态。生产上务必给关键服务配 PodDisruptionBudget,否则一次例行维护就可能把无状态网关全量干掉。
三、三大触发信号:DiskPressure / MemoryPressure / PIDPressure
kubelet 通过节点 Conditions 暴露压力信号,Evicted 只是结果,根因藏在下面三种压力里:
需要注意 kubelet 把阈值分为硬驱逐(evictionHard)与软驱逐(evictionSoft)两类:硬驱逐一旦触顶立即杀 Pod,不留商量余地;软驱逐则会等待 evictionSoftGracePeriod 配置的宽限期,给 Pod 一点自我恢复的时间。日常运维更常遇到的是硬驱逐,因为默认值偏保守(如内存可用低于 100Mi 就动手),节点稍有波动就会触发。理解这张表,定位时才知道该往哪个资源维度排查。
| 压力信号 | 触发条件 | 典型现象 | 首选修复 |
|---|---|---|---|
| MemoryPressure | 节点可用内存低于 memory.available 阈值 | Pod OOM、频繁重建、节点卡顿 | 给 Pod 设置内存 limits、扩容节点 |
| DiskPressure | 容器文件系统(imagefs)或根分区可用空间不足 | 镜像拉取失败、日志写满磁盘、Pod 被逐 | 限制容器日志体积、清理镜像、加盘 |
| PIDPressure | 节点进程数超过 pid.available 上限 | 无法 fork 新进程、命令卡死 | 限制容器的 pid 上限、排查 fork 炸弹 |
其中 DiskPressure 是线上最高发的 Evicted 根因:默认 containerd/docker 不会自动收敛容器日志,一个疯狂打日志的 Pod 能在几小时内写满 40G 系统盘,随后 kubelet 一边驱逐 Pod、一边因磁盘满连新镜像都拉不下来,形成雪崩。
四、定位四步法:从 Events 到根因
4.1 第一步:describe 看 reason
被驱逐的 Pod 会进入 Failed 状态并带上 Evicted reason,Events 里藏着直接原因:
kubectl describe pod <pod-name> -n <ns>
# 关键输出示例:
# Reason: Evicted
# Message: The node had condition: [DiskPressure].
# 低优先级 Pod 因节点磁盘压力被 kubelet 驱逐
4.2 第二步:过滤 Evicted 事件
用 field-selector 把 Evicted 事件单独拎出来,避免被海量正常事件淹没:
kubectl get events --field-selector reason=Evicted -A
# 或按涉及对象过滤
kubectl get events -n <ns> --field-selector involvedObject.kind=Pod
4.3 第三步:看节点 Conditions 与阈值
定位到节点后,检查它的压力信号和 kubelet 驱逐阈值配置:
kubectl get nodes -o wide
kubectl describe node <node> | grep -A6 Conditions
# 关注 MemoryPressure / DiskPressure / PIDPressure 是否为 True
# 查看当前生效的驱逐硬阈值
kubectl get node <node> -o yaml | grep -A10 evictionHard
4.4 第四步:登节点查磁盘与进程
若怀疑 DiskPressure,直接 SSH 到节点统计大目录(缓存与日志混用磁盘时极易踩坑):
df -h /var/lib/containerd # 容器存储所在盘
du -sh /var/log/containers/* 2>/dev/null | sort -rh | head
du -sh /var/lib/docker/* 2>/dev/null | sort -rh | head
# 找出膨胀最快的容器日志,定位"写盘元凶"
五、根治方案:从 requests 到日志轮转
5.1 给 Pod 设置 requests/limits
最基础的防线:任何常驻服务都应设 requests,核心服务设 Guaranteed。同时用 ephemeral-storage 给容器文件系统划上限,超限即被 kubelet 优先清理而非拖垮整机:
resources:
requests:
memory: "512Mi"
cpu: "250m"
ephemeral-storage: "2Gi"
limits:
memory: "1Gi"
ephemeral-storage: "5Gi"
5.2 限制容器日志体积(关键)
在 containerd 配置里给每个容器日志设上限,避免单 Pod 写满系统盘。修改 /etc/containerd/config.toml 后重启 containerd:
[plugins."io.containerd.grpc.v1.cri".container_log]
max_size = "50MB"
max_container_log_files = 5
Docker 用户则在 /etc/docker/daemon.json 设 log-opts 的 max-size 与 max-file。改完记得在 CI 流水线里加一步重启校验,避免配置漂移。
5.3 用 emptyDir 限制临时文件
业务若会往本地写临时文件,给 emptyDir 设 sizeLimit,超出直接写入失败而非无限增长:
emptyDir:
sizeLimit: 1Gi
medium: ""
5.4 调优驱逐阈值(谨慎)
在 kubelet 配置(/var/lib/kubelet/config.yaml)中可抬高阈值,给运维留缓冲,但别调太松以免节点真被拖死:
经验值:内存阈值建议留 10%、磁盘 15%,并配合 evictionMinimumReclaim 保证每次驱逐后回收出一段安全余量,避免”刚驱逐完又立刻触顶”的抖动。修改后务必 systemctl restart kubelet 并观察节点 Conditions 是否回落,切忌一次性把阈值改到极低——那样节点会在”假健康”状态下默默把业务拖垮,比 Evicted 更隐蔽。
evictionHard:
memory.available: "10%"
nodefs.available: "15%"
nodefs.inodesFree: "5%"
imagefs.available: "15%"
六、防御性配置:PDB、PriorityClass、优雅终止
除了补资源,还要降低”被驱逐”对业务的影响:
- PodDisruptionBudget:主动维护(drain)时保证最小可用副本,避免一刀切。
- PriorityClass:给核心服务更高优先级,资源紧张时优先保命。
- 优雅终止:配置
preStop与合理的terminationGracePeriodSeconds,让连接平滑 draining 而非硬切断。
# PriorityClass 示例
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000000
globalDefault: false
七、排障清单(速查表)
| 现象 | 先看 | 根因大概率 | 动作 |
|---|---|---|---|
| Pod 反复重建 | describe 的 Reason | 资源超限被逐 | 加 requests/limits |
| 节点上其他 Pod 也异常 | node Conditions | DiskPressure/MemoryPressure | 清日志/加盘/扩容 |
| 镜像拉取变慢/失败 | df -h imagefs | 磁盘满 | 清镜像 + 限日志 |
| 命令卡死无法 fork | ps 进程数 | PIDPressure | 限 pid + 查 fork 炸弹 |
八、结语
Pod Evicted 的本质是节点资源保护机制,而非 Bug。排查链路始终是:看 Events 的 reason → 定位节点 Conditions → 登节点核实磁盘/内存/进程 → 从 requests、日志轮转、emptydir 三处根治。把它和 Pod Pending、CrashLoopBackOff 并列收进你的 K8s 排障工具箱,下次告警响起就能在 5 分钟内定责。




