TT Lab
开始
学习 学习路径 课程

CNPE — 云原生平台工程师

区分配额、默认值与QoS所表达的信息

在 TT Lab 中继续学习

一句话总结

ResourceQuota 是租户可使用的上限,requests 是调度的输入,实际使用量是观测值。不要把上限、请求和实际使用当作同一个数字。

为什么需要它

假设练习集群的 allocatable CPU 是 24 核,三个租户的 requests.cpu 配额分别是 12、9、9 核。上限之和是 30 核,与容量的比值是 1.25。这既不表示现在已经预留了 30 核,也不表示 CPU 已经用掉了 125%。这是一个规划上的信号:如果所有团队同时按上限提出请求,可能无法满足。必须另外调查实际被调度的 Pod 的 requests 之和及使用量。

把各团队的上限相加的计算只是起点。集中在特定节点池或存储区域的工作负载,即使整体有余量也可能无法启动,而且还需要为一个节点发生故障时预留余量。不能仅凭配额的 hard 相加来决定服务器的采购量。

工作原理

准入审查与位置分配

ResourceQuota 会在 admission 阶段检查 Namespace 范围内的资源和对象数量限制。它并不是持续测量 CPU 实际使用量的机制。也可以用 count/appclaims.platform.labhub.io 这样的键限制 CR 的个数。RBAC、策略和另外的平台逻辑也可以限制发放,所以配额并不是唯一的手段。请在官方 ResourceQuota 文档中确认计算对象。

LimitRange 处理每种对象的最小值、最大值和默认值等。default 是 limit 的默认值,defaultRequest 是 request 的默认值。它在创建 Pod 时应用,即使修改了策略,已有的 Pod 也不会自动被修正。 不要用多个 LimitRange 给出相互冲突的默认值。请以官方 LimitRange 文档为基础,养成在提交前比较文件与 admission 之后的对象的习惯。

Guaranteed 是有条件的

这里的说明是使用各容器自己的 resources、而不声明 Pod 级别 resources 的示例。要成为 Guaranteed,所有普通容器和 init 容器的 CPU 和内存,必须各自存在 request 和 limit,并且二者相同。即使只有内存被填成相同的值,只要缺少 CPU 条件,就不是 Guaranteed。只要有一个资源声明、却达不到 Guaranteed 的条件,就是 Burstable。请把官方 QoS 实验与最终的 status.qosClass 进行对照。

示例容器的最终资源 QoS 判断理由
CPU 100m/100m,内存 64Mi/64Mi Guaranteed 两种资源的 request 和 limit 都相同
没有 CPU,内存 64Mi/64Mi Burstable 缺少 CPU 条件
CPU 25m/100m,内存 32Mi/64Mi Burstable request 小于 limit
所有容器都没有 CPU 和内存声明 BestEffort 两种资源都没有 request 和 limit

如果容器明确给出了 limit 而省略了相应的 request,request 会被设置为 limit 的值。请把这种情况,与“两者都省略而获得 LimitRange 默认值的情况”区分开。已经写明的 request 不会被默认值覆盖。阅读官方内存默认值示例时,请交换这两种输入来预测结果。

阅读练习:看已存储的对象,而不是输入

下面是只能在自己专用的学习集群中运行的示例,不要用于计费或生产集群。如果 cnpe-default-demo 命名空间已经存在,不要复用,请另选一个新名称。先预测收到两个默认值的 Pod,然后再读取实际结果。

kubectl create namespace cnpe-default-demo
kubectl -n cnpe-default-demo apply -f - <<'YAML'
apiVersion: v1
kind: LimitRange
metadata:
  name: defaults
spec:
  limits:
  - type: Container
    default:
      cpu: 100m
      memory: 64Mi
YAML
kubectl -n cnpe-default-demo run sample --image=busybox:1.36 -- sleep 3600
kubectl -n cnpe-default-demo get pod sample -o jsonpath='{.spec.containers[0].resources}'
kubectl -n cnpe-default-demo get pod sample -o jsonpath='{.status.qosClass}'

预期是 CPU 100m、内存 64Mi 的 request 和 limit,QoS 为 Guaranteed。镜像下载失败与资源默认值应用失败是两回事。要观察到容器的运行,需要能够访问镜像的环境和真实的 kubelet。不要把 kwok 的 Running 标记当作真实运行的证据。

反例是在另一个命名空间中去掉 CPU 默认值,只创建新的 Pod。确认即使内存的 request 和 limit 相同,它也是 Burstable。只修改原命名空间的策略、再读取已有 Pod,并不是这个反例。留下记录之后,请删除只包含你自己创建的 Pod 和策略的命名空间。

kubectl delete namespace cnpe-default-demo

在现场相遇的样子

假设在一次虚构的成本会议上,只看到请求 4 核、实际使用 0.3 核的工作负载,就立刻把 request 降到了 0.3。如果没有查看峰值时段、启动成本和故障余量,故障可能比节省的金额更早出现。应该查看各时间段的使用分布和 SLO,在小范围内做出变更之后,同时确认调度、延迟和重启。QoS 等级本身并不保证性能或无故障。

如果配额的用量为空,或者出现 status unknown 错误,就要确认对象的 status、CRD 的 Established、API discovery 和控制器的状态。如果不分青红皂白地删除重建,可能会让 admission 的保护暂时消失。应在确认原因和恢复范围之后再处理,并分别测试新请求被允许和被拒绝的情况。

观测案例:通过了准入,却没有位置

2026-09-11,在另一台单节点 k3s v1.36.4+k3s1 的 VM 上,实际确认了以下内容。这不是对生产服务的压力测试,而是用小小的 sleep 容器把 admission 和调度分开的实验。并不是施加负载让 CPU 用掉 9 核,而是请求了 9 核。

确认的字段 观测值 含义
Node status.allocatable.cpu 8 这个节点可供 Pod 使用的 CPU 容量
ResourceQuota status.hard.requests.cpu 10 这个 Namespace 的请求量上限
Pod spec.containers[0].resources.requests.cpu 9 单个容器请求的 CPU
ResourceQuota status.used.requests.cpu 9 尚未运行的 Pod 的请求也会被计算
Pod status.phase / spec.nodeName Pending / 无 已存储到 API,但没有被分配到节点
PodScheduled 条件 False、Unschedulable、Insufficient cpu 调度器报告 CPU 不足

请求 9 在配额 10 之内,所以对象被创建了,但在 8 核的节点上放不下 9 的请求。此时即使把配额提高到 20,节点也不会变大。如果所有节点都是 8 核,仅仅增加同样规格的节点,这个单个 Pod 也无法进入。必须验证请求是否算错了,或者审查足够大的节点、拆分应用等设计变更。请在官方资源故障排查文档中对照比节点更大的 Pod 的案例。

实验中,把同名的 Pod 删除之后,用 25m 的请求重新创建。发放了新的 UID,确认了 Running、Ready 和真实的 exec,配额上限保持不变。这是恢复可运行性的证据,而不是合适的请求量或节省成本的证据。不要把 sleep 容器的 25m 直接套用到真实的订单服务器上。CPU 使用时间序列、吞吐量、延迟和错误率,在这个实验中都没有测量。

反过来,在另一个 Namespace 中,用两个请求 50m 的 Pod 占满配额 100m 之后,再创建第三个 50m 的 Pod,API 返回了 Forbidden 和 exceeded quota,第三个对象没有被存储。这种情况下,调度器要看的 Pod 本身就不存在。如果是 Deployment 创建 Pod 失败,还必须查看上层对象的事件。只找 Pod 的 Pending,会漏掉 admission 的失败。

租户边界要分别测试读取和变更

在同一台 VM 的另一个实验中,只给 ServiceAccount 授予了自己 Namespace 的 pods get 和 list。用这个账号实际发送查询,自己的 Pod 可以读取,但查询其他 Namespace 的 Pod 以及修改自己的 ResourceQuota 都是 Forbidden。被拒绝之后管理员重新读取的配额,也仍然是 100m。这就是为什么不能只看存在 Role YAML,而要同时看被允许的请求、被拒绝的请求和未发生变化的状态。

这个实验是管理员用 --as 代理进行的 API 权限检查。并没有验证真实的登录、令牌签发、网络阻断和内核隔离。如果只设置配额,却赋予租户修改配额的权限,租户就可以自己提高上限。反过来,阻止了 API 查询,也不意味着无法与其他团队的服务端口通信。请区分阅读 Kubernetes 多租户文档中的控制平面和数据平面。

请自己判断一下。① 配额 used 是 9,是否意味着有 9 核的 CPU 正在运行?② 用同样的名称恢复的 Pod,是原来的对象吗?③ 如果其他团队的查询被拒绝,网络隔离也就完成了吗?答案全都是否,依据分别是请求量的计算、UID 的变化和测试范围的差别。

下一项测验要确认什么

请判断 24 和 30 分别是什么的和,只限制内存的 Pod 的 QoS,以及修改策略对已有 Pod 的效果。本单元的目标,是不把单个正常示例的成功,扩大为整个租户隔离或成本优化的证据。