Requests 与 Limits 的语义
Kubernetes 中每个容器可以配置 CPU 和 Memory 的 requests 与 limits。很多开发者分不清二者的区别,导致 Pod 频繁被驱逐或 OOMKilled。
apiVersion: v1
kind: Pod
metadata:
name: api-server
spec:
containers:
- name: app
image: myapp:latest
resources:
requests:
cpu: "250m" # 0.25 核
memory: "256Mi"
limits:
cpu: "500m" # 0.5 核
memory: "512Mi"
核心区别
| 维度 | Requests | Limits |
|---|---|---|
| 作用阶段 | 调度时 | 运行时 |
| 含义 | Pod 需要的最小资源保证 | Pod 能使用的最大资源上限 |
| 调度器行为 | 调度器根据 requests 判断节点是否有足够资源 | 调度器不关心 limits |
| 运行时行为 | cgroups 中的保障份额 | CPU 被节流(throttle),Memory 触发 OOMKilled |
| 是否可超卖 | 可以(节点上所有 Pod 的 limits 之和可超过节点容量) | 不建议超卖 Memory |
简单理解:
- requests 是调度时的承诺——“这个 Pod 至少需要 0.25 核 CPU 和 256Mi 内存,请找一个够用的节点”
- limits 是运行时的限制——“这个 Pod 最多能用 0.5 核 CPU,内存超过 512Mi 就杀掉它”
CPU vs Memory 的本质差异
CPU 是可压缩资源(compressible):当容器超过 CPU limit 时,内核通过 CFS(Completely Fair Scheduler)进行限流(throttle),容器变慢但不会被杀。
Memory 是不可压缩资源(incompressible):当容器超过 Memory limit 时,内核直接触发 OOM Killer 杀死容器进程,退出码 137。
本文参考 Kubernetes 官方资源管理文档
QoS 等级
Kubernetes 根据每个 Pod 的 requests 和 limits 配置,自动为 Pod 分配 QoS(Quality of Service)等级。QoS 决定了节点资源紧张时 Pod 被驱逐的优先级。
三级 QoS 判定规则
┌─────────────────────────┐
│ 所有容器的 requests │
│ == limits(CPU+Mem)? │
└──────┬──────────┬───────┘
Yes│ │No
▼ ▼
┌──────────┐ ┌──────────────────┐
│Guaranteed│ │ 有容器设了requests? │
│ (最高) │ └────┬─────────┬─────┘
└──────────┘ Yes│ │No
▼ ▼
┌───────────┐ ┌────────────┐
│Burstable │ │BestEffort │
│ (中等) │ │ (最低) │
└───────────┘ └────────────┘
Guaranteed(保证级)
条件:Pod 中每个容器都同时设置了 CPU 和 Memory 的 requests 和 limits,且 requests == limits。
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "500m"
memory: "512Mi"
Guaranteed 级 Pod 在节点资源紧张时最后被驱逐,适合核心服务(数据库、API 网关等)。
Burstable(突发级)
条件:Pod 不满足 Guaranteed 条件,但至少有一个容器设置了 requests。
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "1Gi"
Burstable Pod 允许在空闲时使用更多资源,节点紧张时中等优先级被驱逐。大多数微服务适合这个等级。
BestEffort(尽力级)
条件:Pod 中所有容器都没有设置 requests 和 limits。
resources: {}
BestEffort Pod 最先被驱逐,且不保证任何资源。只适合临时任务、测试 Pod。
QoS 查看方法
# 查看单个 Pod 的 QoS
kubectl get pod <pod-name> -o jsonpath='{.status.qosClass}'
# 查看命名空间下所有 Pod 的 QoS
kubectl get pods -o custom-columns=NAME:.metadata.name,QOS:.status.qosClass
OOMKilled 问题诊断与排查
OOMKilled 的两种形态
| 类型 | 原因 | 表现 |
|---|---|---|
| Container OOMKilled | 容器内存超过 limits.memory | Exit Code: 137,Reason: OOMKilled |
| Node OOMKilled | 节点整体内存耗尽,内核杀进程 | Pod 被驱逐,节点进入 MemoryPressure |
诊断步骤
# 1. 查看 Pod 状态,确认是否 OOMKilled
kubectl describe pod <pod-name> | grep -A5 "Last State"
# 输出示例:
# Last State: Terminated
# Reason: OOMKilled
# Exit Code: 137
# 2. 查看退出码
kubectl get pod <pod-name> -o jsonpath='{.status.containerStatuses[0].lastState.terminated.exitCode}'
# 137
# 3. 查看节点资源使用情况
kubectl describe node <node-name> | grep -A10 "Allocated resources"
# 4. 查看节点是否有 MemoryPressure
kubectl describe node <node-name> | grep -i "MemoryPressure"
排查思路
# 情况一:容器被 OOMKilled,但节点内存充足
# → 说明 limits.memory 设置过低,需调大
# 情况二:容器被 OOMKilled,节点内存也紧张
# → 节点超卖严重,检查节点上所有 Pod 的 requests 之和
# 查看节点上所有 Pod 的内存 requests 总和
kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[*].resources.requests.memory}{"\n"}{end}' --field-selector spec.nodeName=<node-name>
常见根因
- Memory Leak:应用存在内存泄漏,长时间运行后内存持续增长直至触发 limit。通过监控观察内存曲线是否单调上升来判断。
- limit 设置过低:JVM 应用未正确设置堆内存参数(
-Xmx),容器 limit 小于 JVM 默认堆大小。 - 节点超卖:节点上所有 Pod 的 requests.memory 之和远超节点实际内存,导致调度时虽然"满足"了 requests 但实际不够用。
# 监控内存趋势(配合 Prometheus)
# 查询:过去 6 小时容器内存使用率
container_memory_working_set_bytes{pod="<pod-name>"}
/ container_spec_memory_limit_bytes{pod="<pod-name>"}
* 100
ResourceQuota 与 LimitRange
ResourceQuota:命名空间级配额
ResourceQuota 限制命名空间能申请的资源总量,防止单个团队耗尽集群资源。
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-quota
namespace: production
spec:
hard:
requests.cpu: "20" # 命名空间内所有 Pod CPU requests 总和上限
requests.memory: 40Gi
limits.cpu: "40"
limits.memory: 80Gi
persistentvolumeclaims: "10" # PVC 数量上限
count/deployments.apps: "20" # Deployment 数量上限
count/pods: "50"
# 查看命名空间配额使用情况
kubectl describe resourcequota team-quota -n production
# 输出:
# Name: team-quota
# Resource Used Hard
# -------- ---- ----
# requests.cpu 12 20
# requests.memory 28Gi 40Gi
# limits.cpu 24 40
# limits.memory 56Gi 80Gi
# pods 18 50
LimitRange:Pod/容器级默认值与约束
LimitRange 为命名空间内的 Pod 设置默认 requests/limits,并限制单个容器能申请的资源范围。
apiVersion: v1
kind: LimitRange
metadata:
name: default-limits
namespace: production
spec:
limits:
- type: Container
default: # 未设 limits 时的默认值
cpu: "500m"
memory: "512Mi"
defaultRequest: # 未设 requests 时的默认值
cpu: "100m"
memory: "128Mi"
max: # 单容器最大可申请
cpu: "4"
memory: "8Gi"
min: # 单容器最小必须申请
cpu: "50m"
memory: "64Mi"
LimitRange 解决的问题是:
- 防止忘记设置资源——未配置的 Pod 自动获得默认值,避免变成 BestEffort
- 防止申请过大——
max限制单个容器不能超过 4 核 8Gi - 防止申请过小——
min确保至少 50m CPU,避免饿死
# 验证:创建一个不设 resources 的 Pod,观察自动注入的默认值
kubectl run test-pod --image=nginx
kubectl get pod test-pod -o jsonpath='{.spec.containers[0].resources}'
# {"limits":{"cpu":"500m","memory":"512Mi"},"requests":{"cpu":"100m","memory":"128Mi"}}
Vertical Pod Autoscaler(VPA)
VPA 自动调整 Pod 的 requests 和 limits,基于历史资源使用数据推荐合适的值。
安装 VPA
# 克隆 autoscaler 仓库
git clone https://github.com/kubernetes/autoscaler.git
cd autoscaler/vertical-pod-autoscaler
# 安装 VPA 组件
./hack/vpa-up.sh
# 验证
kubectl get pods -n kube-system | grep vpa
# vpa-admission-controller-xxx 1/1 Running
# vpa-recommender-xxx 1/1 Running
# vpa-updater-xxx 1/1 Running
VPA 三种模式
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: api-server-vpa
namespace: production
spec:
targetRef:
apiVersion: "apps/v1"
kind: Deployment
name: api-server
updatePolicy:
updateMode: "Auto" # Off | Initial | Auto
resourcePolicy:
containerPolicies:
- containerName: "*"
minAllowed:
cpu: 100m
memory: 128Mi
maxAllowed:
cpu: 2
memory: 4Gi
| 模式 | 行为 | 适用场景 |
|---|---|---|
Off | 只给出推荐值,不修改 Pod | 评估阶段,观察推荐值是否合理 |
Initial | 只在 Pod 创建时设置 requests,不修改已运行的 Pod | 保守策略,避免重启 |
Auto | 自动修改 requests,会驱逐并重建 Pod | 生产使用(需接受重建) |
VPA 推荐值解读
kubectl describe vpa api-server-vpa -n production
# 输出:
# Recommendation:
# Container Recommendation:
# Target: cpu 250m, memory 384Mi # 推荐值(最合适)
# Lower Bound: cpu 100m, memory 256Mi # 下界(最小可接受)
# Upper Bound: cpu 500m, memory 768Mi # 上界(最大可能需要)
# Uncapped Target: cpu 250m, memory 384Mi # 不受 minAllowed/maxAllowed 约束时的推荐
VPA 注意事项
- VPA 会驱逐 Pod——
Auto模式下修改 requests 需要重建 Pod,可能导致短暂不可用。不要对无法容忍重启的有状态服务使用 VPA Auto 模式。 - VPA 与 HPA 不能同时基于同一资源——如果 HPA 基于 CPU 伸缩,VPA 也在调 CPU requests,两者会互相干扰。可以让 VPA 只管 Memory,HPA 管 CPU。
- 先 Off 后 Auto——新部署的 VPA 先用
Off模式观察 3-7 天推荐值,确认合理后再切Auto。 - 设置 minAllowed / maxAllowed——防止 VPA 推荐极端值。
生产实践:资源推荐值设定方法论
经验法则
没有放之四海而皆准的公式,但以下方法论适用于大多数场景:
第一步:基线测量
# 部署应用后,先不设 limits(但设 requests 保底)
resources:
requests:
cpu: "100m"
memory: "256Mi"
# 运行 7 天,收集 Prometheus 指标
# P95 CPU 使用率、P95 Memory 使用率、峰值 Memory
第二步:设定公式
requests.cpu = P95 CPU 使用值 × 1.5
requests.memory = P95 Memory 使用值 × 1.2
limits.cpu = requests.cpu × 2 (或 P99 CPU × 2)
limits.memory = requests.memory × 1.5 (或峰值 Memory × 1.2)
第三步:验证与迭代
# 部署后观察 7 天
# - CPU throttle 率 < 5%? → limits.cpu 合理
# - Memory 使用率 < 80%? → limits.memory 合理
# - Pod 被 OOMKilled? → 加大 limits.memory
# - Pod 调度失败? → 检查集群总 requests 容量
按应用类型推荐
| 应用类型 | requests.cpu | requests.memory | limits.cpu | limits.memory | QoS |
|---|---|---|---|---|---|
| API 服务 | 250m | 256Mi | 500m | 512Mi | Burstable |
| 数据库(MySQL/PG) | 1000m | 2Gi | 2000m | 4Gi | Guaranteed |
| 消息队列(Redis) | 500m | 1Gi | 1000m | 2Gi | Guaranteed |
| 日志采集(Fluentd) | 100m | 128Mi | 200m | 256Mi | Burstable |
| 批处理任务 | 500m | 512Mi | 2000m | 2Gi | Burstable |
| 前端静态服务 | 50m | 64Mi | 100m | 128Mi | Burstable |
核心服务设为 Guaranteed
# 生产环境核心服务:requests == limits,QoS = Guaranteed
resources:
requests:
cpu: "1000m"
memory: "2Gi"
limits:
cpu: "1000m"
memory: "2Gi"
核心服务(数据库、网关、认证服务)建议设为 Guaranteed,确保:
- 调度时资源 100% 保证,不会因超卖导致资源竞争
- 节点资源紧张时最后被驱逐
- CPU 不会被 throttle(因为 requests == limits,CFS quota 等于保障值)
监控资源使用率
# CPU 使用率(相对于 limits)
sum(rate(container_cpu_usage_seconds_total{pod=~"api-.*"}[5m])) by (pod)
/
sum(kube_pod_container_resource_limits{resource="cpu", pod=~"api-.*"}) by (pod)
* 100
# Memory 使用率(相对于 limits)
container_memory_working_set_bytes{pod=~"api-.*"}
/
container_spec_memory_limit_bytes{pod=~"api-.*"}
* 100
# CPU throttle 检测
rate(container_cpu_cfs_throttled_seconds_total{pod=~"api-.*"}[5m]) * 100
总结
Kubernetes 资源管理是生产稳定性的基石。核心要点:
- requests 决定调度,limits 决定运行——二者缺一不可,不设资源的 Pod 是定时炸弹
- QoS 等级直接影响生存权——核心服务设 Guaranteed,普通服务设 Burstable,临时任务才用 BestEffort
- OOMKilled 是最常见的容器异常——退出码 137,排查路径是
describe pod → 看 limits → 看监控曲线 → 区分泄漏还是配置过小 - ResourceQuota + LimitRange 是集群级护栏——防止团队超卖资源,自动为忘记配置的 Pod 注入默认值
- VPA 是好工具但需谨慎——先 Off 观察,再 Auto 应用,与 HPA 避免管理同一资源
- 资源设定没有银弹——基线测量 → 公式估算 → 上线观察 → 持续迭代,这才是正确的方法论
资源管理做不好,再好的架构设计也会在流量高峰时崩塌。把 requests 和 limits 当作每个 Pod 的安全带,这是 SRE 的底线。
参考资料与致谢
本文在撰写过程中参考了以下资料,感谢原作者的贡献:
- Kubernetes 官方资源管理文档 — Kubernetes 官方,参考了Kubernetes 官方资源管理文档相关内容