返回首页

K8s 资源限制配置指南 — Request / Limit / QoS / CPU Throttling

📅 创建于 2026-05-11 🔄 更新于 2026-05-11 📝 1124 字

K8s 资源限制配置指南

K8s 资源限制(Resource Limits)是 Pod 调度的核心依据,也是保障集群稳定性的关键配置。 配置错误会导致 OOMKill、CPU Throttling、资源浪费、调度不均等问题。

核心概念:Request vs Limit

每个容器可设置 CPU 和内存的 Request 和 Limit:

resources:
  requests:
    memory: "256Mi"
    cpu: "250m"
  limits:
    memory: "512Mi"
    cpu: "500m"

Request — 调度依据

  • Request 是容器最少需要的资源量
  • 调度器(kube-scheduler)只根据 Request 做调度决策,不看 Limit
  • 调度器检查:Node 已分配 Request 总量 + 新 Pod 的 Request ≤ Node 实际容量
  • Request 只影响调度,不影响实际资源使用
  • 容器实际使用超过 Request 仍可运行,但 Node 会发生资源超售

Limit — 资源上限

  • Limit 是容器可以使用的资源上限
  • 内存 Limit 超限 → OOMKill,进程被强制终止
  • CPU Limit 超限 → 无法获取更多 CPU 时间片,CPU 使用被节流(Throttling)
  • Limit 不影响调度,只影响运行时行为

Request 和 Limit 的关系

  • Request ≤ Limit(可以相等)
  • CPU: 可以不相等(推荐 Request = Limit,或 Limit 是 Request 的整数倍)
  • Memory: 强烈建议 Request = Limit,否则导致 OOMKill 优先级异常(见下文)

CPU:可压缩资源

CPU 是"可压缩资源"(compressible resources)。Node 资源紧张时,K8s 通过降低 CPU 时间片"挤出"更多资源, 容器只是变慢,但不会停。

CPU 计量单位

  • 单位:millicores(毫核),简称 m
  • 1 CPU = 1000m,500m = 0.5 CPU
  • 可直接写小数,K8s 自动转 m

CPU Throttling 详解

CPU Throttling 是一个容易被忽视的性能问题。Linux CFS(Completely Fair Scheduler) 默认以 100ms 周期分配 CPU 时间片:

  • 若 CPU Limit = 500m,容器在每个 100ms 周期内最多获 50ms 的 CPU 时间
  • 超过 50ms 的使用请求被推迟到下一个周期
  • 即使"名义上"有 500m CPU,也可能因 Throttling 导致响应延迟增加

减少 CPU Throttling 的方法:

  1. 适当提高 CPU Limit
  2. 使用 Burstable QoS(Request < Limit),让容器突发时有更多 CPU 可用
  3. 调整 kubelet 的 --cpu-cfs-quota--cpu-cfs-period 参数

内存:不可压缩资源

内存是"不可压缩资源"(non-compressible resources)。一旦容器申请了内存, K8s 无法像压缩 CPU 那样"回收"内存。超限即触发 OOMKill。

OOMKill 的机制

Linux OOM Killer 根据 oom_score 选择要杀掉的进程:

QoS 等级 oom_score_adj OOM 优先级
Guaranteed -997 最低(最难杀)
Burstable min(max(2, 1000 - 1000×memoryRequest/nodeMemory), 999) 中等
BestEffort 1000 最高(最先杀)

内存 Request ≠ Limit 的陷阱(关键)

当 Request < Limit 时,Pod 处于 Burstable QoS。此时 OOM Killer 会综合考虑 实际内存使用量和 Request 值 计算 oom_score。导致一个反直觉的现象

实际使用内存很少、但 Request 设置得很高的 Pod,反而更容易被 OOMKill。

示例:

  • Pod A (Burstable): requests: 2Gi, limits: 4Gi,实际只用 512Mi
  • Pod B (Guaranteed): requests: 512Mi, limits: 512Mi,实际只用 512Mi

Node 内存紧张时,Pod A 反而更容易被 OOMKill,即使它的 Request 是 2Gi。

结论: 对于内存,设置 Request = Limit,使 Pod 处于 Guaranteed QoS, oom_score_adj 固定为 -997,最不容易被 OOMKill。

QoS 等级详解

K8s 为每个 Pod 自动分配 QoS(Quality of Service)等级:

QoS 等级 条件 OOM 优先级 调度优先级
Guaranteed 所有容器 CPU 和内存均设置了 Request = Limit 最低 最高
Burstable 不满足 Guaranteed,但至少一个容器设置了 Request 中等 中等
BestEffort 没有任何容器设置 Request 和 Limit 最高 最低
# 查看 Pod 的 QoS 等级
kubectl get pod <pod-name> -o jsonpath='{.status.qosClass}'
kubectl describe pod <pod-name> | grep -E "QoS|Memory|Limit|Request"

注意: Guaranteed Pod 不会被 OOMKill 但不代表不会被驱逐(Eviction)。 节点压力过大时 kubelet 仍根据 eviction thresholds 驱逐,但 Guaranteed 是最后被驱逐的。

详情参见 pod-troubleshooting 的 Pending/OOMKilled 章节。

资源超售(Overcommit)

由于 Request < Limit 是常见做法(特别是 Burstable QoS),Node 上的"已分配 Requests 总和" 通常会超过 Node 实际容量,这叫资源超售。

示例:Node: 4 CPU cores

  • Pod A: requests.cpu=1, limits.cpu=2
  • Pod B: requests.cpu=1, limits.cpu=2
  • Pod C: requests.cpu=1, limits.cpu=2
  • 已分配 requests.cpu = 3(调度器认为 OK)
  • 三个 Pod 全部跑满则需 6 CPU → 过载

资源超售本身不是问题,问题是超售 Pod 全部用到 Limit 时 Node 过载, 此时 CPU Throttling 和 OOMKill 会大量发生。

各类应用配置建议

Web 服务(Nginx / Apache)

resources:
  requests:
    memory: "64Mi"    # Nginx 本身占用很小
    cpu: "50m"        # 基本只做转发
  limits:
    memory: "128Mi"   # 给点余量
    cpu: "200m"

Java 应用(Spring Boot / Tomcat)

Java 启动时需较多内存,稳定运行时相对稳定,但峰值可能更高。

env:
  - name: JAVA_OPTS
    value: "-Xmx512m -Xms256m"  # 明确 JVM 堆内存
resources:
  requests:
    memory: "768Mi"   # 覆盖 JVM heap + overhead
    cpu: "500m"
  limits:
    memory: "768Mi"   # Request = Limit → Guaranteed
    cpu: "1000m"      # CPU 可适当高于 memory

JVM 和容器内存配合: -Xmx 应等于或略小于容器 memory limit, 建议设置为 limit 的 75-80%,留出给 native 内存、direct buffer、mmap 等非堆内存。

Go 应用

Go 运行时管理自己的内存(GC),对容器内存限制配合较好。

resources:
  requests:
    memory: "128Mi"
    cpu: "100m"
  limits:
    memory: "512Mi"
    cpu: "500m"
env:
  - name: GOMEMLIMIT   # Go 1.19+,限制运行时可用内存
    value: "400MiB"

数据库(MySQL / PostgreSQL)

强烈建议 Guaranteed QoS(Request = Limit):

resources:
  requests:
    memory: "2Gi"
    cpu: "1000m"
  limits:
    memory: "2Gi"      # Request = Limit → Guaranteed
    cpu: "2000m"

Redis

内存密集型,对 memory limit 非常敏感:

command: ["redis-server", "--maxmemory", "1536mb", "--maxmemory-policy", "allkeys-lru"]
resources:
  requests:
    memory: "1536Mi"   # 和 --maxmemory 对应
    cpu: "500m"
  limits:
    memory: "1536Mi"   # 两者必须对齐
    cpu: "1000m"

Redis 关键原则: 容器 memory limit 必须 ≥ Redis maxmemory, 否则 Redis 不知道自己实际能用多少内存;反过来则浪费容器层隔离。

LimitRange:Namespace 级默认资源限制

apiVersion: v1
kind: LimitRange
metadata:
  name: default-limits
  namespace: my-app
spec:
  limits:
    - type: Container
      default:                # 容器未设置 limits 时的默认值
        cpu: "100m"
        memory: "128Mi"
      defaultRequest:          # 容器未设置 requests 时的默认值
        cpu: "100m"
        memory: "128Mi"
      min:                     # 最小允许值
        cpu: "10m"
        memory: "16Mi"
      max:                     # 最大允许值
        cpu: "4"
        memory: "8Gi"
      maxLimitRequestRatio:    # 限制 limit/request 比例
        cpu: 10
        memory: 10

ResourceQuota:限制 Namespace 总资源

apiVersion: v1
kind: ResourceQuota
metadata:
  name: production-quota
  namespace: production
spec:
  hard:
    requests.cpu: "10"
    requests.memory: "20Gi"
    limits.cpu: "20"
    limits.memory: "40Gi"
    pods: "50"                 # Pod 数量限制

ResourceQuota 超限时新 Pod 无法创建。详情见 resource-rbac-scheduling-troubleshooting

监控与排查命令

# 查看 Pod 资源使用(需 metrics-server)
kubectl top pods --all-namespaces
kubectl top pod <pod-name> -n <namespace> --containers

# 查看 Node 资源分配
kubectl describe node <node-name> | grep -A 10 "Allocated resources"

# 检查 Pod QoS 等级
kubectl get pods -o custom-columns=NAME:.metadata.name,QOS:.status.qosClass,\
CPU_REQ:.spec.containers[0].resources.requests.cpu,\
MEM_REQ:.spec.containers[0].resources.requests.memory

# 检查 OOMKill 事件
kubectl describe pod <pod-name> -n <namespace> | grep -E "Last State|Exit Code|OOMKilled"
kubectl get events -n <namespace> --field-selector involvedObject.name=<pod-name> | grep -E "OOM|Kill"
ssh <node> "dmesg | grep -i 'killed process'"

# 排查 CPU Throttling(需登录 Node)
cat /sys/fs/cgroup/cpu/kubepods/burstable/<pod-id>/cpu.stat
# 关键指标: nr_throttled 和 throttled_time

常见问题与修复

问题 现象 根因 修复
Pod Pending 无法调度 Node 资源不足满足 Requests 降低 Requests / 扩容 Node / 清理低优 Pod
OOMKilled 不断 反复被杀 memory limit 过小或有内存泄漏 增大 limit / 检查泄漏 / 设 Guaranteed QoS
响应慢但资源未超限 CPU Throttling CPU limit 过紧 提高 CPU limit 或设 Guaranteed QoS
内存用很少却被 OOMKill Burstable QoS 陷阱 Request < Limit 导致 内存设 Request = Limit
多容器 Pod 总资源需累加 Sidecar 模式 调度按总 Request,OOM 按单容器 Limit

核心要点总结

项目 CPU 内存
资源类型 可压缩(compressible) 不可压缩(non-compressible)
超限后果 CPU Throttling(变慢) OOMKill(进程被杀)
Request 用途 调度依据 调度依据
Limit 用途 时间片上限 内存上限
配置建议 Request = Limit 或 Request < Limit 强烈建议 Request = Limit
QoS 影响 Request = Limit → Guaranteed Request = Limit → Guaranteed

排查顺序:

  1. kubectl top pod — 查看实际使用量
  2. 检查 Pod 的 requests/limits 配置
  3. 检查 QoS 等级(Guaranteed / Burstable / BestEffort)
  4. 登录 Node 查看 cgroup 统计或 dmesg 看 OOM 事件
  5. 调整 requests/limits 或扩容

资源限制配置没有一劳永逸的标准值,需要结合业务特点和实际监控数据持续调整。 监控数据是优化资源限制的最终依据。

关联页面

页面关联点
docker-production-pitfallsDocker 原生资源限制视角(与 K8s 资源限制互补)
jvm-container-oom-offheap-troubleshootingJVM 堆外内存(DirectByteBuffer/Metaspace/线程栈)导致容器 OOMKi
k8s-capacity-planning-qos-cost-optimizationK8s 容量规划方法论与成本优化 — 从流量到资源预算的完整框架,含 QoS 策略、弹性伸缩协同、落
k8s-java-memory-tuning-production-guideKubernetes 下 Java 内存调优完整指南 — 内存预算模型、生产参数配置、四层诊断流程、
k8s-pod-pending-troubleshooting-guidePod Pending 排障指南 — 7 个排查方向(资源不足/污点/亲和性/存储/配额/选择器/端
resource-rbac-scheduling-troubleshooting资源配额/OOMKilled 排障