第 36 篇 k8s之资源管理:Requests、Limits 与 QoS IT策士 10余年一线大厂经验专注 IT 思维、架构、职场进阶。我会在各个平台持续发布最新文章助你少走弯路。在前面的文章中我们为 Redis 配置了持久化存储为 Flask 配置了健康检查和滚动更新。我们的应用越来越“生产化”了。但还有一个关键问题没有解决Pod 应该占用多少 CPU 和内存如果某个 Pod 突然内存泄漏会不会拖垮同一台节点上的其他 Pod在 Docker Compose 时代这个问题不那么突出——一台机器上跑的服务数量有限资源竞争不激烈。但在 K8s 集群中一台节点可能同时运行几十个 Pod资源的合理分配直接决定了集群的稳定性和调度效率。Kubernetes 提供了两个核心参数来管理容器资源Requests资源请求和Limits资源上限。它们共同决定了 Pod 会被调度到哪个节点、运行时的资源使用上限以及在资源紧张时 Pod 的“优先级”。今天我们就来彻底搞懂这套机制并把它应用到贯穿案例的 Flask Redis 应用中。一、Requests 和 Limits资源管理的两个维度1.1 基本概念Requests 和 Limits 分别回答了两个不同的问题Requests“调度时我需要至少多少资源” Scheduler 根据 Requests 为 Pod 选择节点——节点上所有 Pod 的 Requests 总和不能超过节点的总资源。这是调度的“准入线”。Limits“运行时我最多能用多少资源” 容器运行时的资源使用不能超过 Limits。CPU 超限会被限流变慢内存超限会被杀掉OOMKilled。这是运行的“天花板”。用一个日常类比来理解Requests 是你在餐厅预订的座位数量——餐厅只有在你预订的座位有空时才让你进。Limits 是你能点的菜的上限——你不能超过这个上限消费。你可以点菜实际使用少于预订座位数但不能超过菜的上限。1.2 可管理的资源类型K8s 主要管理两种资源CPU以“核”core为单位。1 CPU 1 个物理/虚拟核心。可以用小数表示如0.5半个核、250m250 millicpu 0.25 核。m后缀表示千分之一核——100m是 0.1 核。这是 CPU 最常用的计量单位。内存Memory以字节为单位。常见单位MiMebibyte2^20 字节、GiGibibyte2^30 字节。注意MMegabyte10^6 字节和Mi的区别——K8s 使用二进制单位Mi/Gi。1.3 CPU 和内存的超额行为这两种资源在超过 Limits 时的行为截然不同CPU 是可压缩资源容器超过 CPU Limits 时K8s 不会杀死容器而是限流throttle——让容器变慢等待下次调度。这就像高速公路的限速器你没法超过限速但车不会停。内存是不可压缩资源容器超过内存 Limits 时K8s 必须立即杀死容器OOMKillOut of Memory Kill。内存不像 CPU 可以“等一等再分配”——一旦进程申请了内存就必须立即满足。如果容器占用的内存超过了 Limitskubelet 会直接杀掉容器Pod 的RESTARTS计数加 1LAST STATE显示OOMKilled。二、三种 QoS 等级K8s 根据容器的 Requests 和 Limits 配置自动为 Pod 分配一个QoSQuality of Service服务质量等级。这个等级决定了在节点资源紧张时哪些 Pod 优先被驱逐Eviction。三种 QoS 等级的驱逐优先级背后是一个简单的原则为资源做出明确承诺Guaranteed的 Pod 享有最高的保护完全不做承诺BestEffort的 Pod 在资源紧张时最先被牺牲。如果你有一个 BestEffort 等级的批处理任务和一个 Guaranteed 等级的核心业务 Pod当节点内存不足时kubelet 会优先驱逐批处理任务。三、动手配置并验证3.1 为贯穿案例配置资源限制将 Requests 和 Limits 应用到 Flask 应用的生产级 Deployment 中apiVersion: apps/v1 kind: Deployment metadata: name: flask-deployment spec: replicas:3selector: matchLabels: app: flask-counter template: spec: containers: - name: flask image: flask-redis-counter:3.0 resources: requests: cpu:100mmemory:128Milimits: cpu:500mmemory:256Mi配置解读Requests 为每个 Flask Pod 请求 100m CPU 和 128Mi 内存Scheduler 确保节点上有足够资源才调度。Limits 限制每个 Pod 最多使用 500m CPU 和 256Mi 内存。这个 Pod 的 QoS 等级是 BurstableRequests ≠ Limits。部署后验证资源配置kubectl apply-fflask-deployment-resources.yaml kubectl get pods-lappflask-counter# NAME READY STATUS RESTARTS AGE# flask-deployment-xxxxxxxxx-xxxxx 1/1 Running 0 30s查看 QoS 等级kubectl get podpod-name-ojsonpath{.status.qosClass}# Burstable3.2 对比三种 QoS 等级的行为BestEffort最不稳定containers: - name: besteffort-demo image: alpine command:[stress,--vm,1,--vm-bytes,200M]# 不设置 resources → QoS BestEffortBurstable中等containers: - name: burstable-demo image: alpine command:[stress,--vm,1,--vm-bytes,200M]resources: requests: memory:64Milimits: memory:128Mi# Requests Limits → QoS BurstableGuaranteed最稳定containers: - name: guaranteed-demo image: alpine command:[stress,--vm,1,--vm-bytes,200M]resources: requests: memory:128Micpu:100mlimits: memory:128Micpu:100m# Requests Limits → QoS Guaranteed当节点内存不足时kubelet 会按 BestEffort → Burstable → Guaranteed 的顺序驱逐 Pod。对于生产环境的核心服务建议设置 Requests Limits 来获得 Guaranteed 等级。但注意这也会导致资源预留更多——即使容器实际只用 50Mi 内存K8s 也会为它预留完整的 128Mi。3.3 如何合理设定 Requests 和 LimitsRequests 的设定依据——监控数据Requests 应该基于应用在正常运行时的实际资源消耗来设定而不是凭感觉。你可以通过kubectl top pod观察应用在稳定状态下的 CPU 和内存使用将其作为 Requests 的基准值。例如Flask 应用在无负载时使用约 30Mi 内存那么 Requests 设为 64Mi 是合理的既能保证调度正确又不会预留过多空闲资源。Limits 的设定依据——压测数据Limits 应该基于应用的峰值资源需求来设定。通过压测工具模拟高并发流量观察 CPU 和内存的峰值再留出一定的余量。对于内存余量建议在 20%-30% 左右以应对突发流量。对于 CPULimits 可以设为 Requests 的 3-5 倍利用 CPU 的可压缩特性让应用在流量高峰时能“借用”空闲 CPU 资源平时则释放给其他 Pod。避免过度限制Requests 设置得过高会浪费集群资源节点上实际空闲的资源被预留无法调度新 Pod。Limits 设置得过低会导致 OOMKill内存或性能严重下降CPU 被过度限流。如果你不确定可以用kubectl top pod观察一段时间后再做调整而不是一开始就设置非常严格的 Limits。四、LimitRange 与 ResourceQuota单个容器的 Requests/Limits 解决了 Pod 级别的资源约束。但在多团队共享集群时还需要命名空间级别的资源管控。4.1 LimitRangeLimitRange 为命名空间中的 Pod 和容器设置默认的 Requests/Limits防止团队创建没有任何资源约束的 PodapiVersion: v1 kind: LimitRange metadata: name: default-limits spec: limits: - type: Container default: cpu:500mmemory:256MidefaultRequest: cpu:100mmemory:128Mimax: cpu:2memory:1Gimin: cpu:50mmemory:64Mi这样设置后该命名空间中新创建的容器如果不指定resources会自动使用defaultRequest和default。同时任何容器都不能超过max或低于min。4.2 ResourceQuotaResourceQuota 限制整个命名空间的资源使用总量防止某个团队过度消耗集群资源apiVersion: v1 kind: ResourceQuota metadata: name: team-quota spec: hard: requests.cpu:10requests.memory:20Gilimits.cpu:20limits.memory:40Gipersistentvolumeclaims:10当命名空间中的资源使用超过 Quota 时新的 Pod 创建请求会被拒绝。LimitRange 和 ResourceQuota 通常一起使用形成命名空间级别的资源管控体系。五、ResourceQuota 与 QoS 的协同ResourceQuota 和 QoS 等级在资源紧张时形成互补的决策链路。当节点内存不足时kubelet 首先按照 QoS 等级排出 Pod 的驱逐优先级BestEffort → Burstable → Guaranteed但在同一 QoS 等级内部会优先驱逐那些超出 Requests 更多的 Pod。这意味着什么如果你有两个 Burstable 等级的 Pod一个内存 Requests 为 128Mi实际使用了 500Mi超出 372Mi另一个 Requests 为 256Mi实际使用了 300Mi超出 44Mikubelet 会优先驱逐前者——它在同一 QoS 等级中“超量”使用更多。这也是为什么将 Requests 设定为真实平均使用量如此重要它不仅仅是调度凭证更是资源紧张时的保护机制。六、与 Docker Compose 的对比在 Docker Compose 中资源限制是可选的且相对简单deploy: resources: limits: cpus:0.50memory: 256M但在 K8s 中Requests 和 Limits 是两个独立的维度它们的交互产生了 QoS 等级、驱逐优先级、调度策略等复杂的机制。Compose 没有“资源请求”的概念所以调度选择哪台宿主机完全是手动的。K8s 的 Scheduler 则依据 Requests 自动做出最优决策大大降低了大规模集群中资源分配的运维成本。七、命令速查表八、本篇总结Requests调度依据声明 Pod 需要的最小资源。Scheduler 确保节点资源足够Requests 总和不能超过节点总资源。Limits运行上限限制 Pod 能使用的最大资源。CPU 超限被限流内存超限被 OOMKill。QoS 等级由 Requests 和 Limits 的配置关系自动决定分为 Guaranteed、Burstable、BestEffort 三个等级决定了资源紧张时 Pod 被驱逐的优先级。配置原则Requests 基于监控数据设定为平均使用量Limits 基于压测数据设定为峰值余量。生产核心服务建议设置为 GuaranteedRequests Limits。LimitRange 和 ResourceQuota从命名空间层面确保资源使用的规范和公平是多团队共享集群的必备管控手段。通过本篇你的 Pod 不会再“无限制地吃资源”集群的稳定性和可预测性得到了质的提升。下一篇——第 37 篇调度进阶亲和性、污点与容忍我们将学习如何精细控制 Pod 应该被调度到哪些节点上实现更高级的调度策略。想了解更多还可以去各个平台搜索「IT策士」一起升级 IT 思维