Kubelet Eviction机制的触发条件与工作原理
Kubernetes中Pod被驱逐是运维团队日常面对的高频问题。Kubelet在节点资源压力过大时会主动终止Pod以释放资源,这个过程称为Eviction。理解Eviction的触发条件和工作机制,是做好K8s稳定性工程的基础。
Kubelet监控的资源维度包括:内存、磁盘inode、磁盘文件系统空间、PID。当可用资源低于阈值时,Kubelet按Pod的QoS等级和资源使用量排序,优先驱逐BestEffort(未设置request/limit)的Pod,然后是Burstable(设置了部分资源限制)的Pod,Guaranteed(request等于limit)的Pod最后才会被影响。
# 查看节点Eviction阈值配置
$ kubectl describe node node-01 | grep -A20 "Allocatable"
# 查看被驱逐的Pod事件
$ kubectl get events -n production --field-selector reason=Evicted
$ kubectl get events -n production --field-selector reason=Preempting
默认的硬驱逐阈值是 memory.available<100Mi,即节点可用内存低于100MB时触发。软驱逐阈值默认未配置,建议生产环境添加软阈值,给应用优雅退出的时间窗口:
# kubelet配置中的Eviction参数
--eviction-hard=memory.available<100Mi,nodefs.available<10%
--eviction-soft=memory.available<500Mi,nodefs.available<15%
--eviction-soft-grace-period=memory.available=60s,nodefs.available=120s
--eviction-max-pod-grace-period=60
内存驱动的Pod驱逐:案例与根因分析
内存驱动的Eviction是最常见的类型。以下是一个真实排查场景:线上环境中,某节点的Java应用Pod频繁被驱逐。查看节点状态发现 MemoryPressure 条件为True:
$ kubectl describe node worker-03 | grep -A5 "Conditions"
MemoryPressure True Mon, 04 Aug 2026 08:00:00 +0800
DiskPressure False Mon, 04 Aug 2026 08:00:00 +0800
PIDPressure False Mon, 04 Aug 2026 08:00:00 +0800
根因是JVM堆外内存(NIO DirectBuffer)持续增长,超过了容器memory limit。JVM的-XX:MaxDirectMemorySize默认等于-Xmx值,但在使用Netty等NIO框架时,堆外内存不受GC管理,容易泄漏。修复方案是显式设置堆外内存上限,并调整Pod的memory limit为堆内存+堆外内存+系统开销:
# Dockerfile中JVM参数调整
ENV JAVA_OPTS="-Xmx2g -XX:MaxDirectMemorySize=512m -XX:MaxMetaspaceSize=256m"
# deployment.yaml资源配额
resources:
requests:
memory: "3Gi"
cpu: "1"
limits:
memory: "3.5Gi" # 堆2G + 堆外512M + Metaspace 256M + 系统开销768M
cpu: "2"
磁盘压力导致的Pod驱逐排查
除了内存,磁盘空间不足也是Pod驱逐的常见原因。容器运行时写入的日志、emptyDir临时数据、镜像层缓存都可能占满磁盘。排查磁盘压力:
# 检查节点磁盘使用
$ df -h /var/lib/kubelet /var/lib/containerd
$ du -sh /var/log/pods/*
$ du -sh /var/lib/containerd/io.containerd.snapshotter*
容器日志是最常见的磁盘消耗源。一个高频请求的服务Pod,日志量可达GB/天级别。解决方案:第一,配置日志轮转策略;第二,限制单个Pod的日志大小;第三,使用Sidecar方式收集日志而非写入本地文件:
# containerd日志轮转配置
# /etc/containerd/config.toml
[plugins."io.containerd.grpc.v1.cri".containerd]
default_runtime_name = "runc"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
runtime_type = "io.containerd.runc.v2"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
# 单容器日志最大100MB,最多保留3个文件
max_container_log_line_size = 262144
# Kubelet配置日志轮转
--container-log-max-files=5
--container-log-max-size=50Mi
Pod Disruption Budget与优雅驱逐
对于需要保证可用性的服务,应该配置Pod Disruption Budget(PDB),限制同时被驱逐的Pod数量。PDB不会阻止Eviction,但会让K8s调度器在驱逐前检查是否符合PDB约束:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-server-pdb
namespace: production
spec:
minAvailable: 2
selector:
matchLabels:
app: api-server
PDB配合preStop hook实现优雅关闭:
spec:
terminationGracePeriodSeconds: 60
containers:
- name: api-server
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 15"] # 等待Service端点摘除
资源配额优化的实践总结
防止Pod驱逐的根本在于合理的资源配额设置。以下是生产环境的经验值:Guaranteed级别的Pod(request等于limit)不应超过节点Allocatable的70%;预留10%给系统守护进程(kubelet、containerd);预留20%作为缓冲区应对突发负载。所有在线服务Pod都应设置request和limit,避免以BestEffort等级运行。
监控层面,除了Prometheus的 node_memory_available_bytes 指标外,还应关注 kubelet_evictions_total 计数器。当该指标出现增长趋势时,说明节点资源规划需要调整——要么扩容节点,要么迁移部分Pod,要么降低单Pod的资源request。Kubernetes资源管理的核心逻辑就是:精确设置request让调度器做出正确决策,合理设置limit防止单Pod占用过多资源,两者缺一不可。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/kubernetespod-qu-zhu-wen-ti-quan-jie-xi-cong-eviction-ji/