深色模式
资源限制 Requests 与 Limits
摘要:不给容器设资源限制,一个失控进程就能拖垮整台节点。本文讲清 requests 与 limits 的语义差异、CPU 与内存超限的不同后果、三种 QoS 等级的驱逐优先级,并给出可操作的取值方法。
适用环境
- 可用 K8s 集群 +
kubectl - 已安装 metrics-server(用于观测实际用量)
- 示例镜像:
busybox、nginx:alpine
操作步骤
一、两个字段的语义
| 字段 | 作用 | 谁用 |
|---|---|---|
requests | 预约保底资源 | 调度器决定 Pod 落哪个节点 |
limits | 上限硬限制 | kubelet/内核在运行期强制执行 |
yaml
spec:
containers:
- name: app
image: nginx:alpine
resources:
requests:
cpu: 250m # 0.25 核
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
调度只看 requests:节点剩余可分配量 ≥ 所有 Pod requests 之和才允许调度。
二、CPU 超限 vs 内存超限
- CPU 超限:被 限流(throttling),进程变慢,不会被杀。
- 内存超限:触发 OOMKilled,容器被杀重启,Pod 状态变 CrashLoopBackOff。
bash
kubectl describe pod <pod> | grep -A5 "Last State"
# Reason: OOMKilled → 内存 limits 太小
kubectl get pod <pod> -o jsonpath='{.status.containerStatuses[0].lastState.terminated.reason}'1
2
3
2
3
CPU 限流可用 metrics 观察:
bash
kubectl top pod <pod>1
三、三种 QoS 等级
| 等级 | 条件 | 节点资源不足时被驱逐顺序 |
|---|---|---|
| Guaranteed | requests == limits(全部容器都要设且相等) | 最后(最安全) |
| Burstable | 至少设了 requests 或 limits,但不全等 | 中间 |
| BestEffort | 一个都没设 | 最先被杀 |
bash
kubectl get pod <pod> -o jsonpath='{.status.qosClass}'1
注意
一个 Pod 里只要有一个容器完全不设 requests/limits,整个 Pod 的资源声明就不满足 Guaranteed 条件。核心服务建议每个容器都显式写全。
四、怎么定合理数值
- 先不设 limits,只设一个保守的 requests,让应用正常跑一周。
- 用监控看 P99 用量:
bash
kubectl top pod -l app=web --containers1
requests= 日常均值(保证调度密度合理),limits= 峰值的 1.5-2 倍(给突发留余量)。
建议
Java 类应用注意:JVM 堆内存只是进程内存的一部分,limits 要按进程总内存(堆 + 元空间 + 线程栈 + 直接内存)来设,通常比 -Xmx 大 30%-50%。
五、命名空间级别的默认值与上限
避免有人忘记设置,用 LimitRange 兜底:
yaml
apiVersion: v1
kind: LimitRange
metadata:
name: default-limits
namespace: dev
spec:
limits:
- type: Container
default:
cpu: 500m
memory: 512Mi
defaultRequest:
cpu: 100m
memory: 128Mi
max:
cpu: "2"
memory: 2Gi1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
default 是 limits 的默认值,defaultRequest 是 requests 的默认值,由准入控制器自动注入。
六、验证限流与 OOM
内存超限实验:
bash
kubectl run mem-test --rm -it --restart=Never \
--image=busybox --limits=memory=64Mi -- \
sh -c 'x=a; while true; do x=$x$x$x$x; done'1
2
3
2
3
观察:kubectl get pod mem-test -w 会变成 OOMKilled。
七、查看节点资源余量
bash
kubectl describe node <节点名> | grep -A8 "Allocated resources"
kubectl get node <节点名> -o jsonpath='{.status.allocatable}'1
2
2
验证
- [ ]
kubectl get pod <pod> -o jsonpath='{.status.qosClass}'返回期望等级 - [ ]
kubectl describe node的资源分配段能看到 requests/limits 占比 - [ ] 内存超限实验得到 OOMKilled
- [ ] 新建未设资源的 Pod 被 LimitRange 自动注入默认值
常见坑
- 设了 limits 但没设 requests:调度器按 requests=limits 处理,容易调度失败,且 QoS 是 Burstable。
- CPU limits 设太小导致服务变慢:CPU 超限是静默限流,表现为延迟升高而非报错,很难定位。对延迟敏感的服务可考虑不设 CPU limits。
- 内存 limits 设太小频繁重启:表现为周期性 CrashLoopBackOff,看
lastState的 reason 确认。 - requests 设得过大:节点装不下几个 Pod,资源利用率极低(见成本与装箱相关章节)。
- 在 Pod 运行时改 resources:会触发 Pod 原地重启(部分版本支持原地调整),生产变更需走发布窗口。