Kubernetes

K8s 资源限制与 QoS

·15 分钟阅读·5751 字

requests/limits 详解、资源单位、QoS 等级(Guaranteed/Burstable/BestEffort)、OOM 优先级

📋 目录

K8s 资源限制与 QoS

Pod 资源 requests/limits 配置详解、QoS 等级分类及驱逐优先级。


本文从原理、配置、调度逻辑、QoS 等级、OOM 机制、排障、最佳实践完整讲解,结合 Linux 内核 Cgroups、K8s 调度、节点资源驱逐场景,覆盖日常运维与高频面试考点。


📖 前置基础:为什么要做资源限制?

K8s 容器资源管控底层依赖 Linux Cgroups(控制组),核心作用:

  1. 资源隔离:限制单个容器/ Pod 最大可用 CPU、内存,防止单个应用耗尽节点资源,引发节点雪崩
  2. 调度依据requests 是调度器选择节点的核心标准,保证应用拿到最低资源。
  3. OOM 优先级:结合 QoS 等级,节点资源紧张时有序驱逐 Pod,保障核心业务稳定。
  4. 容量规划:精准统计集群资源使用率,方便扩节点、成本优化。

K8s 主要管控两类核心资源:

  • CPU:可压缩资源(资源不足会降频、限流,不会直接杀死进程)
  • 内存(Memory):不可压缩资源(资源溢出直接触发 OOM Kill)

⚡ requests 与 limits 详解(核心配置)

在容器 spec.containers[].resources 中定义,每个容器单独配置,不是 Pod 全局配置。

📖 两个核心字段定义

1.1 requests(资源请求 / 最低保障)

  • 含义:容器运行最少需要的资源,是 K8s 调度器的调度依据
  • 调度逻辑:
    Scheduler 只会把 Pod 调度到剩余资源 ≥ Pod 所有容器 requests 总和的节点上。
  • 特点:
  1. 节点资源充足时,容器可以使用超过 requests 的资源;
  2. 仅做预留保障,不是硬限制。

1.2 limits(资源上限 / 硬限制)

  • 含义:容器允许使用的最大资源,超过即触发限制行为。
  • 特点:
  1. 硬边界,不允许突破;
  2. 超限行为: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)
  • 示例:256Mi1Gi

🔧 标准 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 全局判定,不是单个容器:

  1. 只要任意一个容器不满足 requests=limits → 整个 Pod 降级为 Burstable;
  2. 只有全部容器都满足 requests=limits,Pod 才是 Guaranteed;
  3. 全部容器都没配资源 → BestEffort。

示例:

  • 容器A:req=lim,容器B:req<lim → 整个 Pod = Burstable

🚀 节点资源压力 & Pod 驱逐机制(生产核心)

📖 节点四种压力状态

当节点资源达到阈值,kubelet 会给节点打上压力污点,同时开始驱逐 Pod:

  1. MemoryPressure:内存不足(最常见)
  2. DiskPressure:磁盘空间不足
  3. PIDPressure:进程数超限
  4. NetworkPressure:网络异常

⚡ Pod 驱逐顺序(严格按此执行)

节点资源紧张时,kubelet 驱逐顺序:

  1. 第一步:优先驱逐 BestEffort 所有 Pod
  2. 第二步:驱逐 Burstable 中内存使用 > requests 的 Pod
    同等级内:实际内存使用越超配,越先被驱逐
  3. 第三步:最后才考虑驱逐 Guaranteed Pod
    一般情况下,Guaranteed Pod 不会被驱逐;只有节点资源彻底耗尽,才会触发。

关键结论:
核心业务一定要做成 Guaranteed,最大限度避免被主动驱逐。

🔧 OOM 评分机制(内核层)

Linux 内核会为每个进程计算 oom_score 分数:

  • 分数越高 → 越容易被 OOM Killer 杀死
    K8s 会根据 QoS 自动调整 oom_score:
  1. BestEffort:oom_score 最高 → 优先杀
  2. Burstable:分数中等
  3. 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 杀死。
    排障:
  1. 临时调高 limits 内存;
  2. 分析应用内存泄漏、堆内存溢出(Java 看 GC、堆快照);
  3. 优化程序内存占用。

⚡ 现象2:CPU 使用率一直卡在 limits 线

根因:业务流量高、逻辑慢,CPU 打满上限被限流。
排障:

  1. 评估业务压力,适当调高 CPU limits;
  2. 代码优化、接口异步、减少阻塞;
  3. 横向扩 Pod 副本分担压力。

🔧 现象3:Pod 无法调度,事件提示 Insufficient cpu/memory

根因:
节点剩余资源 < Pod 所有容器 requests 总和,调度器找不到可用节点。
排障:

  1. 调低 Pod requests;
  2. 清理节点无用 Pod/镜像,释放资源;
  3. 新增节点扩容集群。

⚙️ 现象4:核心业务 Pod 意外被驱逐

根因:

  • 核心 Pod 未配置资源,变成 BestEffort/Burstable;
  • 节点压力大,被优先驱逐。
    解决方案:
    给核心容器严格配置 requests = limits,升级为 Guaranteed。

🔒 生产环境最佳实践(规范)

📖 资源配置通用规范

  1. 所有容器必须配置 requests + limits,禁止裸跑(BestEffort);
  2. 先压测:根据压测结果设置合理值,避免配置过大浪费、过小被限流/OOM。

⚡ 按业务等级区分 QoS

  • **一级核心业务(订单、支付、数据库、Redis、Kafka)**强制 requests = limitsGuaranteed
  • 二级普通业务
    requests < limitsBurstable,弹性运行
  • 日志、监控、临时任务
    可按需放宽,尽量单独规划节点,避免影响业务节点。

🔧 CPU & 内存配置建议

  • CPU:根据峰值负载设置,预留 20%~30% 余量;
  • 内存:内存 limits 一定要大于应用最大堆/常驻内存,预留系统开销。

⚙️ 集群运维规范

  1. 监控节点资源使用率、Pod OOM 次数、驱逐事件;
  2. 禁止节点大量混合部署 BestEffort 高负载 Pod;
  3. 中间件(有状态应用)一律使用 Guaranteed。

5. 安全红线

  • 不允许生产业务 Pod 不配置任何资源;
  • 不允许核心服务使用 Burstable / BestEffort。

📊 高频面试题汇总

  1. requests 和 limits 作用分别是什么?
    requests 是调度依据、资源预留;limits 是资源硬上限,防止资源溢出。
  2. CPU 和内存超限行为有什么不同?
    CPU 超限被限流,不杀容器;内存超限触发 OOM,直接杀死容器。
  3. K8s 三种 QoS 等级,划分规则和驱逐顺序?
    Guaranteed(req=lim,最后驱逐)、Burstable(有req,中等)、BestEffort(无资源配置,最先驱逐)。
  4. 为什么核心业务要设置为 Guaranteed?
    降低 OOM 分数、节点压力时最后被驱逐,保障稳定性。
  5. 一个 Pod 多个容器,QoS 如何判定?
    全局判定,全部容器 req=lim 才是 Guaranteed,否则降级。
  6. Pod 出现 OOMKilled 是什么原因?怎么解决?
    内存超过 limits,排查内存泄漏、调高内存限制、优化应用。
  7. 只配置 limits 不配置 requests,会发生什么?
    requests 会默认等于 limits。

关联文档

关联文档