共计 7157 个字符,预计需要花费 18 分钟才能阅读完成。
前言
凌晨三点,告警响起。
登录系统一看,核心服务的 Pod 被 Evicted(驱逐)了。查看日志发现一行:The node was low on resource: memory。
你可能会有这些疑问:
- 🤔 为什么节点还有资源,我的 Pod 却被杀了?
- 🤔 驱逐的顺序是什么?哪些 Pod 最危险?
- 🤔 如何保护关键服务不被驱逐?
今天,我们就来深入剖析 Kubernetes 的 Pod 驱逐策略,从原理到实战,帮你彻底搞懂这个关键机制。
1. 什么是 Pod 驱逐?
核心概念
Node-pressure Eviction(节点压力驱逐) 是 kubelet 在节点资源不足时,主动终止 Pod 以回收资源的过程。
graph TD
A[节点资源监控] --> B{达到驱逐阈值?}
B -->| 否 | A
B -->| 是 | C[选择要驱逐的 Pod]
C --> D[发送 SIGTERM 信号]
D --> E{30 秒内优雅退出?}
E -->| 是 | F[Pod 成功终止]
E -->| 否 | G[发送 SIGKILL 强制杀死]
style A fill:#e1f5ff
style B fill:#fff4e1
style C fill:#ffe1e1
style F fill:#e1ffe1
驱逐 vs OOM Killer
很多人会混淆这两个概念,我们来对比一下:
| 特性 | Pod Eviction(驱逐) | OOM Killer(内存杀手) |
|---|---|---|
| 触发者 | kubelet 主动触发 | Linux 内核被动响应 |
| 可预测性 | ✅ 可配置阈值 | ❌ 不可预测 |
| 终止方式 | 优雅关闭(有宽限期) | 立即强杀 |
| 选择逻辑 | 基于 QoS 和优先级 | 基于 OOM 分数 |
| 推荐 | ✅ 应该依赖 | ❌ 应该避免 |
⚠️ 关键区别:驱逐是可控的、优雅的,而 OOM Killer 是不可控的、粗暴的。合理配置驱逐策略可以避免触发 OOM Killer。
2. 驱逐信号与阈值
2.1 Kubelet 监控的资源信号
Kubelet 持续监控以下资源:
| 驱逐信号 | 说明 | 系统 |
|---|---|---|
memory.available |
节点可用内存 | All |
nodefs.available |
节点文件系统可用空间 | All |
nodefs.inodesFree |
节点文件系统可用 inode | Linux |
imagefs.available |
镜像文件系统可用空间 | All |
imagefs.inodesFree |
镜像文件系统可用 inode | Linux |
pid.available |
可用进程 ID 数量 | Linux |
内存信号计算方式(重要):
memory.available = 节点总内存 - 工作集内存(working set)
📌 注意:不是
free命令显示的可用内存!Kubernetes 使用 cgroup 的数据。
2.2 硬驱逐 vs 软驱逐
graph LR
A[资源压力] --> B{阈值类型}
B -->|Hard 硬驱逐 | C[立即驱逐 <br/> 无宽限期]
B -->|Soft 软驱逐 | D[观察期]
D --> E{持续超过 <br/> 宽限期?}
E -->| 是 | C
E -->| 否 | F[继续观察]
style C fill:#ff6b6b
style D fill:#4ecdc4
style F fill:#95e1d3
硬驱逐(Hard Eviction)
特点:阈值到达立即驱逐,无宽限期
默认配置:
eviction-hard:
memory.available: "100Mi" # Linux
memory.available: "500Mi" # Windows
nodefs.available: "10%"
nodefs.inodesFree: "5%"
imagefs.available: "15%"
软驱逐(Soft Eviction)
特点:有观察期,更温和
配置示例:
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
evictionSoft:
memory.available: "200Mi"
nodefs.available: "15%"
evictionSoftGracePeriod:
memory.available: "1m30s" # 观察 1 分 30 秒
nodefs.available: "2m" # 观察 2 分钟
💡 实战建议:生产环境建议同时配置软驱逐和硬驱逐,软驱逐作为预警,硬驱逐作为保底。
3. 驱逐的优先级与顺序
3.1 QoS 等级:生死线
Kubernetes 根据 Pod 的资源配置,自动分配三种 QoS(Quality of Service)等级:
graph TD
A[Pod 创建] --> B{资源配置检查}
B -->| 所有容器 <br/>requests=limits| C[Guaranteed<br/> 保证级]
B -->| 部分容器 <br/> 有 requests 或 limits| D[Burstable<br/> 突发级]
B -->| 没有任何 <br/> 资源配置 | E[BestEffort<br/> 尽力而为级]
C --> F[驱逐优先级: 最低 <br/> 最后才会被驱逐]
D --> G[驱逐优先级: 中等]
E --> H[驱逐优先级: 最高 <br/> 首先被驱逐]
style C fill:#2ecc71
style D fill:#f39c12
style E fill:#e74c3c
✅ Guaranteed(保证级)- 最安全
条件:所有容器都设置了 CPU 和内存的 requests 和 limits,且相等
apiVersion: v1
kind: Pod
metadata:
name: guaranteed-pod
spec:
containers:
- name: nginx
image: nginx
resources:
requests:
memory: "1Gi"
cpu: "500m"
limits:
memory: "1Gi" # 与 requests 相等
cpu: "500m" # 与 requests 相等
特点:除非系统守护进程需要资源,否则不会被驱逐
⚠️ Burstable(突发级)- 中等风险
条件:至少一个容器设置了 requests 或 limits,但不满足 Guaranteed
apiVersion: v1
kind: Pod
metadata:
name: burstable-pod
spec:
containers:
- name: nginx
image: nginx
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi" # 大于 requests
cpu: "1000m" # 大于 requests
⚠️ 特点:可以使用超过 requests 的资源,但超出越多越危险
❌ BestEffort(尽力而为级)- 最危险
条件:没有任何容器设置 requests 和 limits
apiVersion: v1
kind: Pod
metadata:
name: besteffort-pod
spec:
containers:
- name: nginx
image: nginx
# 完全没有 resources 配置
❌ 特点:资源压力时首先被驱逐
3.2 完整驱逐顺序
graph TD
A[开始驱逐选择] --> B[按 QoS 分组]
B --> C[第 1 组: BestEffort]
B --> D[第 2 组: Burstable 超出 requests]
B --> E[第 3 组: Guaranteed 和 Burstable 未超出 requests]
C --> F[按资源使用量排序]
D --> F
E --> F
F --> G[按 Priority 值排序]
G --> H[选择最终驱逐对象]
H --> I[发送 SIGTERM]
I --> J{30 秒内退出?}
J -->| 否 | K[发送 SIGKILL 强杀]
J -->| 是 | L[优雅终止]
style C fill:#e74c3c
style D fill:#f39c12
style E fill:#2ecc71
驱逐顺序总结:
- 第一梯队:BestEffort Pod(按使用量从高到低)
- 第二梯队:Burstable Pod 且使用量超过 requests(按超出比例从高到低)
- 第三梯队:Guaranteed Pod 和未超出 requests 的 Burstable Pod(按 Priority 从低到高)
4. 节点资源回收机制
在驱逐 Pod 之前,kubelet 会先尝试回收节点级资源:
回收流程
sequenceDiagram
participant K as Kubelet
participant N as Node
participant C as Containers
participant I as Images
K->>N: 检测到资源压力
K->>K: 开始资源回收
K->>C: 1. 清理死亡的容器
Note over C: 删除已停止的容器
K->>N: 检查资源是否足够
alt 仍不足
K->>I: 2. 删除未使用的镜像
Note over I: 清理不再需要的镜像
end
K->>N: 再次检查资源
alt 还是不足
K->>C: 3. 开始驱逐 Pod
end
不同文件系统配置的回收策略
场景 1:单文件系统(nodefs)
回收顺序:1. 垃圾回收死亡 Pod 和容器
2. 删除未使用的镜像
场景 2:分离镜像文件系统(nodefs + imagefs)
nodefs 压力触发:→ 垃圾回收死亡 Pod 和容器
imagefs 压力触发:→ 删除所有未使用的镜像
5. 如何保护关键 Pod 不被驱逐
5.1 使用 PriorityClass 设置优先级
创建高优先级类:
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000000 # 数值越大优先级越高
globalDefault: false
description: "关键业务 Pod 使用此优先级"
在 Pod 中使用:
apiVersion: v1
kind: Pod
metadata:
name: critical-service
spec:
priorityClassName: high-priority # 引用优先级类
containers:
- name: app
image: my-critical-app
resources:
requests:
memory: "2Gi"
cpu: "1"
limits:
memory: "2Gi"
cpu: "1"
5.2 配置 Guaranteed QoS
最佳实践:关键服务必须配置 Guaranteed QoS
apiVersion: apps/v1
kind: Deployment
metadata:
name: payment-service
spec:
replicas: 3
selector:
matchLabels:
app: payment
template:
metadata:
labels:
app: payment
spec:
priorityClassName: high-priority
containers:
- name: payment
image: payment-service:v1
resources:
requests:
memory: "4Gi"
cpu: "2"
limits:
memory: "4Gi" # 与 requests 相等
cpu: "2" # 与 requests 相等
5.3 系统关键组件的特殊处理
对于 DaemonSet 等系统组件,使用 system-node-critical 优先级:
apiVersion: v1
kind: Pod
metadata:
name: kube-proxy
namespace: kube-system
spec:
priorityClassName: system-node-critical # 系统级最高优先级
containers:
- name: kube-proxy
image: k8s.gcr.io/kube-proxy:v1.28.0
💡 优先级数值参考:
system-node-critical: 2000001000system-cluster-critical: 2000000000- 用户自定义:建议 0-1000000
案例 1:为什么节点资源充足,Pod 还是被驱逐?
问题现象:
# 节点总资源
Total: CPU 16 cores, Memory 32Gi
# 已分配资源(Requests 总和)Allocated: CPU 8 cores (50%), Memory 16Gi (50%)
# 实际使用
Actual Usage: CPU 12 cores (75%), Memory 28Gi (87.5%)
# 现象:某些 Burstable Pod 被驱逐了!
原因分析:
问题出在 资源超售 + 不合理的 requests/limits 配置
graph LR
A[问题根源] --> B[Requests 设置过小]
A --> C[Limits 设置过大]
B --> D[调度器认为 <br/> 节点资源充足]
C --> E[Pod 实际使用 <br/> 远超 requests]
D --> F[允许更多 Pod 调度]
E --> F
F --> G[节点实际使用 <br/> 超负荷]
G --> H[触发驱逐机制]
style H fill:#e74c3c
不合理配置示例:
# ❌ 错误配置:容易被驱逐
resources:
requests:
memory: "100Mi" # 请求很少
cpu: "100m"
limits:
memory: "4Gi" # 限制很大,可以使用 40 倍的内存
cpu: "2"
当多个这样的 Pod 实际使用接近 limits 时,节点内存就会爆满。
正确配置示例:
# ✅ 正确配置:合理估算
resources:
requests:
memory: "2Gi" # 接近实际使用
cpu: "500m"
limits:
memory: "3Gi" # 留出 50% 弹性空间
cpu: "1"
5.4 最佳实践总结
1. 合理配置资源请求
# 原则:requests 设置为正常使用量,limits 留 20-50% 弹性空间
resources:
requests:
memory: "2Gi" # 正常使用量
cpu: "1"
limits:
memory: "3Gi" # 1.5 倍 requests
cpu: "2" # 2 倍 requests
2. 关键服务使用 Guaranteed QoS
spec:
priorityClassName: high-priority
containers:
- name: app
resources:
requests:
memory: "4Gi"
cpu: "2"
limits:
memory: "4Gi" # 相等
cpu: "2" # 相等
3. 监控和告警配置
# Prometheus 告警规则示例
groups:
- name: node-eviction
rules:
- alert: NodeMemoryPressure
expr: kube_node_status_condition{condition="MemoryPressure",status="true"} == 1
for: 5m
labels:
severity: warning
annotations:
summary: "节点 {{$labels.node}} 内存压力"
- alert: PodEvicted
expr: kube_pod_status_phase{phase="Failed",reason="Evicted"} == 1
labels:
severity: critical
annotations:
summary: "Pod {{$labels.pod}} 被驱逐"
4. 配置合理的驱逐阈值
# Kubelet 配置建议
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
# 软驱逐:作为预警
evictionSoft:
memory.available: "1Gi"
nodefs.available: "15%"
evictionSoftGracePeriod:
memory.available: "2m"
nodefs.available: "5m"
# 硬驱逐:作为保底
evictionHard:
memory.available: "500Mi"
nodefs.available: "10%"
# 最小回收量
evictionMinimumReclaim:
memory.available: "500Mi"
nodefs.available: "1Gi"
6. 知识要点速记
Q1: 为什么我的 Guaranteed Pod 还是被驱逐了?
A: 如果系统守护进程(如 kubelet、系统服务)需要更多资源,即使 Guaranteed Pod 也可能被驱逐。解决方案:
- 使用
--system-reserved和--kube-reserved为系统预留资源 - 使用
system-node-critical优先级
Q2: limits 设置过大有什么问题?
A:
- 会导致节点资源超售
- Pod 实际使用可能远超 requests
- 容易触发驱逐甚至 OOM Killer
- 建议 limits 不超过 requests 的 1.5-2 倍
Q3: 如何查看 Pod 的 QoS 等级?
# 查看 Pod 详情中的 qosClass 字段
kubectl get pod <pod-name> -o yaml | grep qosClass
# 或使用 describe
kubectl describe pod <pod-name> | grep "QoS Class"
Q4: DaemonSet 的 Pod 会被驱逐吗?
A: 会!但可以通过设置高优先级来保护:
spec:
priorityClassName: system-node-critical
7. 相关资料
- Kubernetes 官方文档 - Node-pressure Eviction
- Kubernetes 官方文档 - Pod QoS Classes
- Kubernetes 官方文档 - Pod Priority and Preemption
8. 总结
Kubernetes 的 Pod 驱逐机制是保障集群稳定性的关键:
- 驱逐是可控的:通过 QoS、Priority、阈值等多层保护
- 顺序是有规律的:BestEffort → Burstable(超限) → Guaranteed
- 可以提前预防:合理配置资源、设置优先级、监控告警
核心原则:
- ✅ 关键服务用 Guaranteed + 高优先级
- ✅ requests 接近实际使用,limits 留适当弹性
- ✅ 配置软驱逐做预警,硬驱逐做保底
- ✅ 监控节点压力,提前扩容