K8s 资源限制与 QoS
Pod 资源 requests/limits 配置详解、QoS 等级分类及驱逐优先级。
本文从原理、配置、调度逻辑、QoS 等级、OOM 机制、排障、最佳实践完整讲解,结合 Linux 内核 Cgroups、K8s 调度、节点资源驱逐场景,覆盖日常运维与高频面试考点。
📖 前置基础:为什么要做资源限制?
K8s 容器资源管控底层依赖 Linux Cgroups(控制组),核心作用:
- 资源隔离:限制单个容器/ Pod 最大可用 CPU、内存,防止单个应用耗尽节点资源,引发节点雪崩。
- 调度依据:
requests是调度器选择节点的核心标准,保证应用拿到最低资源。 - OOM 优先级:结合 QoS 等级,节点资源紧张时有序驱逐 Pod,保障核心业务稳定。
- 容量规划:精准统计集群资源使用率,方便扩节点、成本优化。
K8s 主要管控两类核心资源:
- CPU:可压缩资源(资源不足会降频、限流,不会直接杀死进程)
- 内存(Memory):不可压缩资源(资源溢出直接触发 OOM Kill)
⚡ requests 与 limits 详解(核心配置)
在容器 spec.containers[].resources 中定义,每个容器单独配置,不是 Pod 全局配置。
📖 两个核心字段定义
1.1 requests(资源请求 / 最低保障)
- 含义:容器运行最少需要的资源,是 K8s 调度器的调度依据。
- 调度逻辑:
Scheduler 只会把 Pod 调度到剩余资源 ≥ Pod 所有容器 requests 总和的节点上。 - 特点:
- 节点资源充足时,容器可以使用超过 requests 的资源;
- 仅做预留保障,不是硬限制。
1.2 limits(资源上限 / 硬限制)
- 含义:容器允许使用的最大资源,超过即触发限制行为。
- 特点:
- 硬边界,不允许突破;
- 超限行为:CPU 被限流、内存超限触发 OOM。
重要前提:Pod 总资源 = 所有容器 requests/limits 之和,InitContainer 也可独立配置资源。
⚡ 资源单位规范
CPU 单位
K8s CPU 单位为 毫核(m)
1 CPU= 1000m(1 物理/虚拟核心)100m= 0.1 核- 示例:
500m= 半核
内存单位
支持字节标准单位:
Ki/Mi/Gi:二进制(1Mi = 1024Ki)生产推荐K/M/G:十进制(1M = 1000K)- 示例:
256Mi、1Gi
🔧 标准 YAML 配置示例
apiVersion: v1
kind: Pod
metadata:
name: resource-demo
spec:
containers:
- name: app
image: nginx
resources:
# 最低资源保障(调度使用)
requests:
cpu: 100m
memory: 128Mi
# 最大资源上限(硬限制)
limits:
cpu: 500m
memory: 256Mi
⚙️ 不同组合的行为表现
组合1:只配 requests,不配 limits
- 调度:按 requests 预留资源;
- 运行:容器可无上限使用节点空闲 CPU/内存;
- 风险:异常进程会吃光节点内存/CPU。
组合2:只配 limits,不配 requests
- K8s 规则:requests 未显式配置时,默认等于 limits;
- 等价于:
requests = limits。
组合3:requests < limits(最常用)
- 有最低保障,也有上限约束;
- 资源弹性可用,兼顾稳定与利用率。
组合4:requests > limits(非法配置)
- Pod 创建失败,直接报错,无法运行。
🔧 CPU 超限 & 内存超限 行为差异(重点)
📖 CPU 超限(可压缩资源)
当容器 CPU 使用量 > limits.cpu:
- K8s 通过 Cgroups 对容器 CPU 限流、降频;
- 表现:应用变慢、请求延迟升高、CPU 被压在 limit 线;
- 不会杀死容器。
⚡ 内存超限(不可压缩资源)
当容器内存使用量 > limits.memory:
- 内核触发 OOM Killer,直接杀死当前容器进程;
- Pod 会根据
restartPolicy重启容器; - 现象:
kubectl describe pod中出现OOMKilled。
面试高频:为什么内存超限会杀容器,CPU 不会?
答:CPU 是可压缩资源,可降速运行;内存不可回收、不可压缩,溢出只能杀死进程释放内存。
⚙️ Pod QoS 服务质量等级(核心考点)
QoS(Quality of Service)是 K8s 基于 requests / limits 组合 自动划分的三类优先级,无需手动配置,创建 Pod 时自动判定。
作用:
节点整体内存/资源压力过大时,kubelet 按照 QoS 优先级从低到高 驱逐 Pod,优先牺牲低优先级应用,保护核心服务。
📖 三大 QoS 等级划分规则
1. Guaranteed(最高优先级,最后被驱逐)
判定条件:
Pod 内所有容器同时满足:
requests.cpu == limits.cpu 且 requests.memory == limits.memory
特点:
- 资源完全固定,资源配额严格锁定;
- OOM 优先级最低,节点再紧张也最后杀;
- 适用:核心业务、数据库、中间件、支付、订单。
2. Burstable(中等优先级,中间驱逐)
判定条件:
不满足 Guaranteed,且 至少有一个容器配置了 requests(CPU/内存任意其一)。
常见场景:
- 有 requests,无 limits
- requests < limits
特点:
- 有最低资源保障,允许弹性扩容;
- 驱逐优先级居中;
- 适用:普通业务服务、大部分线上应用。
3. BestEffort(最低优先级,最先被驱逐)
判定条件:
Pod 内所有容器都未配置 requests 和 limits。
特点:
- 无任何资源预留、无上限;
- OOM/节点压力时,最先被杀死/驱逐;
- 适用:日志采集、监控、测试、临时任务。
⚡ QoS 判定速记表
| QoS 等级 | 配置规则 | 驱逐优先级 | 适用场景 |
| Guaranteed | 所有容器 requests = limits | 最低(最后杀) | 核心业务、中间件 |
| Burstable | 配置了 requests,且 requests ≠ limits | 中等 | 普通业务服务 |
| BestEffort | 无 requests、无 limits | 最高(最先杀) | 日志、监控、临时Pod |
🔧 查看 Pod QoS 等级
## 方式1:describe 查看
kubectl describe pod <pod-name> | grep QoS
## 方式2:jsonpath 精准提取
kubectl get pod <pod-name> -o jsonpath='{.status.qosClass}'
⚙️ 多容器 Pod QoS 规则(易踩坑)
Pod QoS 以整个 Pod 全局判定,不是单个容器:
- 只要任意一个容器不满足
requests=limits→ 整个 Pod 降级为 Burstable; - 只有全部容器都满足
requests=limits,Pod 才是 Guaranteed; - 全部容器都没配资源 → BestEffort。
示例:
- 容器A:
req=lim,容器B:req<lim→ 整个 Pod = Burstable
🚀 节点资源压力 & Pod 驱逐机制(生产核心)
📖 节点四种压力状态
当节点资源达到阈值,kubelet 会给节点打上压力污点,同时开始驱逐 Pod:
- MemoryPressure:内存不足(最常见)
- DiskPressure:磁盘空间不足
- PIDPressure:进程数超限
- NetworkPressure:网络异常
⚡ Pod 驱逐顺序(严格按此执行)
节点资源紧张时,kubelet 驱逐顺序:
- 第一步:优先驱逐 BestEffort 所有 Pod
- 第二步:驱逐 Burstable 中内存使用 > requests 的 Pod
同等级内:实际内存使用越超配,越先被驱逐 - 第三步:最后才考虑驱逐 Guaranteed Pod
一般情况下,Guaranteed Pod 不会被驱逐;只有节点资源彻底耗尽,才会触发。
关键结论:
核心业务一定要做成 Guaranteed,最大限度避免被主动驱逐。
🔧 OOM 评分机制(内核层)
Linux 内核会为每个进程计算 oom_score 分数:
- 分数越高 → 越容易被 OOM Killer 杀死
K8s 会根据 QoS 自动调整 oom_score:
- BestEffort:oom_score 最高 → 优先杀
- Burstable:分数中等
- Guaranteed:oom_score 最低 → 最难被杀
这是 QoS 防护的内核底层实现。
🌐 InitContainer / Sidecar 资源规则补充
📖 InitContainer 资源
- Init 容器支持独立配置 requests/limits;
- 计算 Pod 总 requests 时:Init 资源 + 主容器资源 累加;
- 调度器会一并计算 Init 资源,保证初始化阶段资源可用。
⚡ Sidecar 边车容器
- Sidecar 也是独立容器,必须单独配置资源;
- 整个 Pod QoS 受 Sidecar 配置影响;
- 示例:主容器
req=lim,Sidecar 只配 req 不配 lim → 整个 Pod 变为 Burstable。
💾 常见异常现象、原因与排障
📖 现象1:Pod 状态 OOMKilled
日志/事件:State: Terminated (Reason: OOMKilled)
根因:
- 容器内存使用超过
limits.memory,被内核 OOM 杀死。
排障:
- 临时调高 limits 内存;
- 分析应用内存泄漏、堆内存溢出(Java 看 GC、堆快照);
- 优化程序内存占用。
⚡ 现象2:CPU 使用率一直卡在 limits 线
根因:业务流量高、逻辑慢,CPU 打满上限被限流。
排障:
- 评估业务压力,适当调高 CPU limits;
- 代码优化、接口异步、减少阻塞;
- 横向扩 Pod 副本分担压力。
🔧 现象3:Pod 无法调度,事件提示 Insufficient cpu/memory
根因:
节点剩余资源 < Pod 所有容器 requests 总和,调度器找不到可用节点。
排障:
- 调低 Pod requests;
- 清理节点无用 Pod/镜像,释放资源;
- 新增节点扩容集群。
⚙️ 现象4:核心业务 Pod 意外被驱逐
根因:
- 核心 Pod 未配置资源,变成 BestEffort/Burstable;
- 节点压力大,被优先驱逐。
解决方案:
给核心容器严格配置requests = limits,升级为 Guaranteed。
🔒 生产环境最佳实践(规范)
📖 资源配置通用规范
- 所有容器必须配置 requests + limits,禁止裸跑(BestEffort);
- 先压测:根据压测结果设置合理值,避免配置过大浪费、过小被限流/OOM。
⚡ 按业务等级区分 QoS
- **一级核心业务(订单、支付、数据库、Redis、Kafka)**强制
requests = limits→ Guaranteed - 二级普通业务
requests < limits→ Burstable,弹性运行 - 日志、监控、临时任务
可按需放宽,尽量单独规划节点,避免影响业务节点。
🔧 CPU & 内存配置建议
- CPU:根据峰值负载设置,预留 20%~30% 余量;
- 内存:内存 limits 一定要大于应用最大堆/常驻内存,预留系统开销。
⚙️ 集群运维规范
- 监控节点资源使用率、Pod OOM 次数、驱逐事件;
- 禁止节点大量混合部署 BestEffort 高负载 Pod;
- 中间件(有状态应用)一律使用 Guaranteed。
5. 安全红线
- 不允许生产业务 Pod 不配置任何资源;
- 不允许核心服务使用 Burstable / BestEffort。
📊 高频面试题汇总
- requests 和 limits 作用分别是什么?
requests 是调度依据、资源预留;limits 是资源硬上限,防止资源溢出。 - CPU 和内存超限行为有什么不同?
CPU 超限被限流,不杀容器;内存超限触发 OOM,直接杀死容器。 - K8s 三种 QoS 等级,划分规则和驱逐顺序?
Guaranteed(req=lim,最后驱逐)、Burstable(有req,中等)、BestEffort(无资源配置,最先驱逐)。 - 为什么核心业务要设置为 Guaranteed?
降低 OOM 分数、节点压力时最后被驱逐,保障稳定性。 - 一个 Pod 多个容器,QoS 如何判定?
全局判定,全部容器 req=lim 才是 Guaranteed,否则降级。 - Pod 出现 OOMKilled 是什么原因?怎么解决?
内存超过 limits,排查内存泄漏、调高内存限制、优化应用。 - 只配置 limits 不配置 requests,会发生什么?
requests 会默认等于 limits。
关联文档
- K8s Pod 详解 — Pod 资源 requests/limits
- K8s 滚动更新与发布策略 — 滚动更新资源管理
- 20-areas/cloud-native/kubernetes/scheduler — 调度器资源分配
关联文档