4. 内存资源配额实战:内存 Requests/Limits 配置策略,OOM Killer 机制,内存压力与 Swap 管理
内存是 Kubernetes 集群中最敏感的稀缺资源之一。与 CPU 不同,内存不可压缩:一旦 Pod 超出限制,系统无法像 CPU 节流那样平滑降频,而是直接触发 OOM Killer 终止进程。本章从 Requests/Limits 配置策略出发,深入 OOM Killer 工作机制,并分析 Swap 对内存隔离的影响,帮助你在生产环境中建立稳健的内存资源管理体系。
4.1 内存 Requests 与 Limits 配置策略
Kubernetes 通过 requests.memory 和 limits.memory 控制 Pod 的内存行为。Requests 用于调度决策,Limits 用于运行时约束。两者配置不当会导致资源浪费、调度失败或频繁 OOM。
4.1.1 核心配置模型
| 参数 | 作用域 | 调度影响 | 运行时行为 |
|---|---|---|---|
requests.memory |
Pod 级别 | 节点必须满足该值才能调度 | cgroup memory.soft_limit_in_bytes,尽力保障 |
limits.memory |
Pod 级别 | 不影响调度 | cgroup memory.limit_in_bytes,硬限制,超限即 OOM |
最佳实践:生产环境建议
requests.memory 等于 limits.memory,即 Guaranteed QoS 类。这能避免 Burstable Pod 在节点内存紧张时被优先驱逐,同时让 cgroup 硬限制精确保护节点稳定性。
4.1.2 配置策略对比
| 策略 | Requests | Limits | QoS 类 | 适用场景 |
|---|---|---|---|---|
| 严格保障 | 等于 Limits | 等于 Requests | Guaranteed | 数据库、有状态服务、延迟敏感应用 |
| 弹性超卖 | 小于 Limits | 大于 Requests | Burstable | 批处理、离线任务、可降级服务 |
| 无限制 | 未设置 | 未设置 | BestEffort | 仅测试环境,生产禁止 |
避坑:不要将
limits.memory 设置得远大于 requests.memory。当节点内存不足时,Burstable Pod 会先被驱逐,且 OOM 分数更高。如果应用存在内存泄漏,过大的 Limits 只会延迟问题暴露,最终导致节点级 OOM。
4.1.3 配置示例
apiVersion: v1
kind: Pod
metadata:
name: memory-demo
spec:
containers:
- name: app
image: nginx:alpine
resources:
requests:
memory: "256Mi"
limits:
memory: "512Mi"
env:
- name: GOMEMLIMIT
value: "480MiB" # 为 Go 应用预留 32MiB 余量
关键指标:建议为应用运行时(如 JVM、Go runtime)预留 10%-15% 的内存余量,避免 cgroup 硬限制与 GC 触发时机冲突导致频繁 OOM。
4.2 OOM Killer 机制与调优
当 Pod 内存使用超过 limits.memory 或节点整体内存不足时,Linux 内核 OOM Killer 介入。Kubernetes 通过 cgroup 和 oom_score_adj 控制 OOM 优先级,但运维人员仍需理解其触发链路与调优手段。
4.2.1 OOM Killer 触发流程
- 内存分配失败:进程通过
malloc()或mmap()申请内存,内核发现物理内存 + Swap 不足。 - 内核唤醒 OOM Killer:遍历所有进程,根据
oom_score选择“最坏”进程。 - 计算 oom_score:基于进程内存占用、CPU 时间、运行时长、
oom_score_adj等。 - 发送 SIGKILL:终止目标进程,释放内存。
- Kubelet 响应:检测到 Pod 容器退出,根据重启策略决定是否重建。
4.2.2 oom_score_adj 调优
Kubernetes 自动为不同 QoS 类设置 oom_score_adj,但你可以通过 Pod 注解或 Admission Controller 进一步调整。
| QoS 类 | 默认 oom_score_adj | 含义 |
|---|---|---|
| Guaranteed | -998 | 几乎不会被 OOM Killer 选中 |
| Burstable | min(max(2, 1000 - (1000 * memory_request_bytes) / machine_memory_bytes), 999) | 根据内存请求比例动态调整 |
| BestEffort | 1000 | 最容易被 OOM Killer 选中 |
调优建议:对于关键基础设施组件(如 CoreDNS、kube-proxy),可通过
spec.containers[].securityContext.oomScoreAdj 手动设置更低的 oom_score_adj,确保它们在节点内存紧张时存活。
4.3 内存压力与 Swap 管理
Kubernetes 官方要求节点关闭 Swap,因为 Swap 会破坏内存隔离性,导致 Pod 内存使用统计失真,并可能引发性能抖动。但在某些边缘场景(如裸金属、大数据预处理),合理使用 Swap 可以提升资源利用率。
4.3.1 Swap 对 Kubernetes 的影响
| 影响维度 | 具体问题 |
|---|---|
| 内存隔离失效 | Pod 的 cgroup 限制仅控制物理内存,Swap 可绕过限制,导致节点内存超卖失控 |
| OOM 判断偏差 | Kubelet 基于 cgroup memory.usage_in_bytes 判断,但该值不包含 Swap,导致实际内存压力被低估 |
| 性能抖动 | Swap 触发时,磁盘 I/O 延迟可达毫秒级,对延迟敏感应用造成不可预测的响应时间 |
| 驱逐决策错误 | 节点内存压力驱逐 Pod 时,Swap 可能掩盖真实压力,导致驱逐滞后 |
4.3.2 内存压力检测与驱逐策略
Kubelet 通过 --eviction-hard 和 --eviction-soft 参数设置内存压力阈值。当节点内存使用超过阈值时,Kubelet 开始驱逐 Pod。
# kubelet 启动参数示例
--eviction-hard=memory.available<100Mi
--eviction-soft=memory.available<200Mi
--eviction-soft-grace-period=memory.available=1m30s
--eviction-minimum-reclaim=memory.available=0Mi
4.3.3 Swap 管理最佳实践
- 生产集群:严格关闭 Swap。在节点初始化时通过
swapoff -a禁用,并在/etc/fstab中注释 Swap 条目。 - 边缘/裸金属场景:如果必须启用 Swap,建议设置
vm.swappiness=1,仅作为极端情况下的最后保障。 - Kubelet 配置:从 Kubernetes 1.28 开始,可以通过
--feature-gates=NodeSwap=true启用有限 Swap 支持,但仅限 Burstable 和 BestEffort Pod。 - 监控指标:关注
node_memory_MemAvailable_bytes、container_memory_working_set_bytes、container_memory_failures_total三个指标,及时发现内存压力。
实战检查命令:
kubectl top pod -n <namespace> 查看 Pod 实时内存使用。kubectl describe node <node-name> 查看节点内存压力状态。cat /sys/fs/cgroup/memory/kubepods/besteffort/memory.limit_in_bytes 查看 cgroup 硬限制。