Resource 资源限制与 QoS
requests/limits 决定了 Pod 会被调度到哪、能抢到多少资源;QoS 等级决定 OOM 时谁先被杀;LimitRange 与 ResourceQuota 在 Namespace 层治理资源。这是工作负载可用的地基——在发布前配好,才能避免"资源竞争引发雪崩"。
本篇目标:掌握 CPU/Memory 计量单位与换算,深入理解 requests/limits 的底层机制(CFS、OOM Killer),能解释 QoS 等级,并会用 LimitRange 与 ResourceQuota 做命名空间配额管控。
1. K8s 中的计算资源计量单位
写错单位是配置 requests/limits 最常见的错误之一。先分清 CPU 与内存两套单位。
1.1 CPU 单位:毫核
CPU 以"核"为单位,可以用小数,也可以用毫核(m,millicore):
| 写法 | 含义 |
|---|---|
1 或 1000m | 1 个 CPU 核(1 个 vCPU / 超线程) |
500m | 0.5 核 |
100m | 0.1 核 |
10m | 0.01 核 |
1m | 1 毫核,最小有效值;低于 1m 的配置无效 |
0.5 核 = 500m
0.25 核 = 250m
1.5 核 = 1500m
1.2 内存单位:二进制 vs 十进制
内存的坑在于两套后缀并存:二进制(Ki/Mi/Gi)按 1024 进,十进制(K/M/G)按 1000 进。容器运行时和 cgroup 按二进制字节计量,而云监控面板往往显示十进制 MB/GB。
| 后缀 | 基数 | 换算 |
|---|---|---|
Ki | 二进制 | 1Ki = 2^10 = 1,024 字节 |
Mi | 二进制 | 1Mi = 2^20 = 1,048,576 字节 |
Gi | 二进制 | 1Gi = 2^30 = 1,073,741,824 字节 |
K | 十进制 | 1K = 10^3 = 1,000 字节 |
M | 十进制 | 1M = 10^6 = 1,000,000 字节 |
G | 十进制 | 1G = 10^9 = 1,000,000,000 字节 |
换算示例:压测显示应用常驻内存约 128MB(十进制,128,000,000 字节),二进制单位下为 128,000,000 ÷ 1,048,576 ≈ 122.07 Mi,正确做法写 128Mi(实际可用约 134.2MB,自带余量)。
2. Requests 与 Limits 深度解析
2.1 两者的区别
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
| 概念 | 作用 | 超出后果 |
|---|---|---|
| request | 调度器为 Pod 预留资源的依据;也决定 QoS 等级 | 无 request → 调度结果不可预测,容量规划失效 |
| CPU limit | 容器可使用的 CPU 上限 | 超出时被节流(throttle),应用变慢但不被杀 |
| memory limit | 容器可使用的内存上限 | 超出时容器被 OOMKilled,Pod 重启 |
2.2 CPU 的底层:CFS 配额
CPU 是可压缩资源。limit: 500m 的底层实现是 CFS(Completely Fair Scheduler)配额:Kubernetes 把 CPU 请求与限制换算成 cpu.cfs_quota_us / cpu.cfs_period_us,内核按此限流。
CFS 周期默认 100ms(cpu.cfs_period_us = 100000us)
limit 500m → 每 100ms 周期最多使用 50ms 的 CPU 时间
超出部分被节流(throttle),进程不会被杀死——但延迟会显著上升
节流的常见表现:应用"看起来活着"但响应慢、HPA 与探针读数失真。排查时看容器内的 CPU throttle 指标(container_cpu_cfs_throttled_seconds_total)。
2.3 内存的底层:OOM Killer
内存是不可压缩资源。容器超过 memory limit 后,内核 OOM Killer 直接杀掉容器进程:
内存超限 → 内核 OOM Killer → 容器被 OOMKilled → kubelet 按 restartPolicy 重启
kubectl get pod <pod> -n learning -o jsonpath='{.status.containerStatuses[*].lastState.terminated.reason}'
# OOMKilled 退出码 137
解决方向:调大 memory limit、排查内存泄漏、降低应用内存占用。不能靠无限调大 limit 解决泄漏。
3. 服务质量等级(QoS Classes)
Kubernetes 根据 requests 和 limits 的配置方式为 Pod 分配 QoS 等级,OOM 时按优先级杀 Pod:
| QoS 等级 | 条件 | OOM 优先级 |
|---|---|---|
| Guaranteed | 每个容器的 CPU 和内存 request == limit(且都设置了) | 最后被杀 |
| Burstable | 至少一个容器设置了 request,但不满足 Guaranteed 条件 | 中等 |
| BestEffort | 没有任何容器设置 request 或 limit | 最先被杀 |
kubectl get pod <pod-name> -n learning -o jsonpath='{.status.qosClass}'
生产关键服务至少应是 Burstable,核心服务建议 Guaranteed。
4. LimitRange 与 ResourceQuota
4.1 ResourceQuota:限制 Namespace 总量
apiVersion: v1
kind: ResourceQuota
metadata:
name: learning-quota
namespace: learning
spec:
hard:
requests.cpu: "4"
requests.memory: 8Gi
limits.cpu: "8"
limits.memory: 16Gi
pods: "50"
persistentvolumeclaims: "10"
count/services: "20"
requests.*/limits.*限制 Namespace 内所有 Pod 的 requests / limits 总和;pods、persistentvolumeclaims限制资源数量;count/<资源>可限制任意资源的数量;- 超出配额时创建请求被拒绝,Pod 停留在
Pending,事件里会出现exceeded quota。
4.2 LimitRange:给单 Pod 设默认值与边界
apiVersion: v1
kind: LimitRange
metadata:
name: learning-limitrange
namespace: learning
spec:
limits:
- type: Container
default:
cpu: 500m
memory: 512Mi
defaultRequest:
cpu: 100m
memory: 128Mi
max:
cpu: "2"
memory: 2Gi
min:
cpu: 10m
memory: 16Mi
default/defaultRequest:未声明时的默认 limits / requests,避免"忘记写"导致无限制或落入BestEffort;max/min:单个容器的上限与下限,越界拒绝;type还可设为Pod或PersistentVolumeClaim(限制单个 PVC 大小)。
5. 生产建议
- 资源值来自压测与监控,至少
request等于正常负载平均用量,limit留 20%-50% 余量; - 不要用
kubectl top pods的瞬时值直接当 request——取峰值或 P95; - QoS 至少 Burstable,核心服务 Guaranteed;
- 配额只管上限不是安全边界,权限靠 RBAC(第 17 章);
- HPA 的
averageUtilization以requests.cpu为分母(第 16 章),资源值写在清单里才能让扩缩容有意义。
6. 练习
练习 A
- 给一个 Deployment 配置 requests/limits,用
kubectl get pod -o jsonpath='{.status.qosClass}'查看 QoS 等级 - 删除容器的
resources.requests后重新部署,观察 QoS 等级变化(Guaranteed → Burstable/BestEffort) - 用
kubectl top pods -n learning对比实际用量与 requests,估算当前配置的余量是否合理
练习 B
- 给
learning设置 ResourceQuota(pods: "2"、requests.memory: 512Mi),尝试创建第 3 个 Pod,观察被拒绝的事件 - 设置 LimitRange(
default内存 128Mi),创建一个不写 resources 的 Pod,确认默认值被注入 - 故意把 memory limit 设到 8Mi 并运行一个吃内存的程序,观察 OOMKilled 与退出码 137
下一篇:Probe 健康检查机制。