CPU看似空闲,为什么Pod仍无法进入
目标
在真实的 k3s 中,区分由资源请求导致的调度失败和由配额导致的 API 创建拒绝。
为什么重要
即使当前使用率很低,声明的请求也可能无法放入节点。即使准入被允许,真正启动也是另一个阶段。 不要通过删除策略或回收别人的 Pod 来让它通过,而是比较默认值、合计、身份和真实响应。 实验使用个人 VM 中的小型待机应用,不是 CPU 饱和、throttling、OOM 的负载实验。不要带入生产 kubeconfig 或真实机密。 这是 50 分钟的实验。需要时请在到期前延长,并记住会话结束时 VM 和文件会被回收。
已准备的环境与辅助工具
kcna-capacity 是实验空间,kcna-capacity-control/control 是正常对比组。两个空间都采用 restricted/v1.36 策略。 应用以固定 digest、Always pull、non-root、cap drop ALL、RuntimeDefault、只读根文件系统、不挂载 ServiceAccount 令牌的方式运行。 不要改动节点和对比组。学员的所有文件都放在 /root/kcna-resources 之下。 输入文件由学员编写,观测文件由读取真实 API 和 Pod 状态的 capture 生成。不要自己编造成功、UID、事件。 辅助程序是在 python3 /opt/fixtures/kcna_resources_lab.py 之后加上 act、capture、grade、prepare 和步骤编号。 observe 只读取当前状态。grade 也不会更改学员文件或 Kubernetes 对象。 act 的 API 响应保存在 /opt/fixtures/kcna-resource-action-N.json 中。不要重复同一个请求,去重新制造过去的 403、201。
步骤
- 用 capture 1 把 /root/kcna-resources/baseline.json 保存下来。调查 Node 的真实 allocatable CPU 和 UID、空的 kcna-capacity 命名空间,以及 kcna-capacity-control/control 的 UID、容器 ID、Ready 和重启次数。
- 在 oversized-resources.json 中写入 requests 和 limits 对象。两个 cpu 值都写成比实际节点 allocatable 多 CPU 1 的值,以 m 为单位的字符串,memory 分别为 32Mi 和 64Mi。用 act 2 和 capture 2 保存 unschedulable.json。oversized Pod 必须存在,但处于未放置的 Pending,并且必须有该 UID 的 Unschedulable 条件和 Insufficient cpu 事件。
- 在 fit-resources.json 中以 JSON 写入 requests={cpu:50m,memory:32Mi}、limits={cpu:200m,memory:64Mi}。act 3 只回收已调查的 oversized UID,并创建小的 fit Pod。用 capture 3 保存 fitted.json,并确认不同的 Pod UID 和正常启动。
- 在 defaults-policy.json 中写入 type=Container、defaultRequest={cpu:100m,memory:32Mi}、default={cpu:200m,memory:64Mi}。用 act 4 和 capture 4 保存 defaults.json。比较省略资源的 defaulted 的实际请求 100m,与已有 fit 保留的 50m。
- 在 quota.json 中把 requests.cpu=200m、requests.memory=128Mi、limits.cpu=1、limits.memory=256Mi、pods=4 全部写成字符串。act 5 创建 team-budget 之后,请求创建省略资源的 extra。用 capture 5 保存 quota_denied.json。在用量为 150m 时,额外的 100m 必须被真实的配额 HTTP 403 拒绝,并且不能存在 extra。
- 在 increase.json 中写入 requests.cpu=300m。用 act 6 和 capture 6 保存 quota_admitted.json。确认同一个 extra 创建请求的真实 HTTP 201、新的 Pod UID 和 Ready,以及用量 250m。不要改动其他配额轴和已有 Pod。
- 在 lower.json 中写入 requests.cpu=100m。用 act 7 和 capture 7 保存 quota_lowered.json。用量为 250m 的已有 Pod 必须以相同的 UID 和容器保持不变,新的 10m small 请求必须被真实的 HTTP 403 拒绝。不要把策略缩减解读为已有 Pod 被自动终止。
- 在 recover.json 中写入 remove=extra、requests.cpu=200m、replacement={requests:{cpu:50m,memory:32Mi},limits:{cpu:200m,memory:64Mi}}。act 8 只回收已调查的 extra,在用量减少之后调整预算并创建 small。用 capture 8 保存 budget_restored.json。请证明真实的 HTTP 201、Ready、最终请求合计 200m,以及对比组得到保留。
参考
正文中 cpu:50m 这样的写法是在说明字段和值。保存的文件必须是正确的 JSON,像示例那样用引号括起键和字符串。 CPU 1 就是 1000m。第 2 步要先读取这台 VM 的实际 allocatable 再计算,不要背特定 VM 的值。 已完成的输入和观测要保留。如果进行中写错了输入,请读取错误原因后修正,但不要重写已保存的过去观测。 准备只补上缺失的先前步骤。它不会创建当前任务的输入和观测,也不会覆盖你写错的已有输入。 评分限时 60 秒,步骤准备限时 90 秒。预算和用量要确认收敛,不要只凭固定 sleep 就假定成功。 如果中断之后刚好出现了对象但没有身份记录,不会随意接管任意对象,而是直接失败。不会通过自动初始化来清除学习记录。 记录哈希是为了防止失误,并不是阻止 root 恶意篡改的远程证明装置。 官方依据:资源管理 · 默认值策略 · 配额。
调查放置预算和正常对比组
用 capture 1 把 /root/kcna-resources/baseline.json 保存下来。调查 Node 的真实 allocatable CPU 和 UID、空的 kcna-capacity 命名空间,以及 kcna-capacity-control/control 的 UID、容器 ID、Ready 和重启次数。
使用率图表和 allocatable 回答的是不同的问题。
看似清闲也进不去的请求
在 oversized-resources.json 中写入 requests 和 limits 对象。两个 cpu 值都写成比实际节点 allocatable 多 CPU 1 的值,以 m 为单位的字符串,memory 分别为 32Mi 和 64Mi。用 act 2 和 capture 2 保存 unschedulable.json。oversized Pod 必须存在,但处于未放置的 Pending,并且必须有该 UID 的 Unschedulable 条件和 Insufficient cpu 事件。
不要只看 Pending,请同时查看 nodeName、PodScheduled 条件和同一 UID 的事件。
用合适大小的请求启动
在 fit-resources.json 中以 JSON 写入 requests={cpu:50m,memory:32Mi}、limits={cpu:200m,memory:64Mi}。act 3 只回收已调查的 oversized UID,并创建小的 fit Pod。用 capture 3 保存 fitted.json,并确认不同的 Pod UID 和正常启动。
不要改变节点资源,也不要删除对比组。
对省略的资源应用默认值
在 defaults-policy.json 中写入 type=Container、defaultRequest={cpu:100m,memory:32Mi}、default={cpu:200m,memory:64Mi}。用 act 4 和 capture 4 保存 defaults.json。比较省略资源的 defaulted 的实际请求 100m,与已有 fit 保留的 50m。
查看输入中没有的值是否出现在已保存的对象里,并同时比较已有对象。
连创建 Pod 本身都被拒绝的配额
在 quota.json 中把 requests.cpu=200m、requests.memory=128Mi、limits.cpu=1、limits.memory=256Mi、pods=4 全部写成字符串。act 5 创建 team-budget 之后,请求创建省略资源的 extra。用 capture 5 保存 quota_denied.json。在用量为 150m 时,额外的 100m 必须被真实的配额 HTTP 403 拒绝,并且不能存在 extra。
请通过响应正文区分 Forbidden 的原因是 RBAC 还是 team-budget。
区分准入通过与真正运行
在 increase.json 中写入 requests.cpu=300m。用 act 6 和 capture 6 保存 quota_admitted.json。确认同一个 extra 创建请求的真实 HTTP 201、新的 Pod UID 和 Ready,以及用量 250m。不要改动其他配额轴和已有 Pod。
201 是创建被接受。请继续确认同一个 UID 是否真的在运行。
降低上限之后,已有 Pod 依然保留
在 lower.json 中写入 requests.cpu=100m。用 act 7 和 capture 7 保存 quota_lowered.json。用量为 250m 的已有 Pod 必须以相同的 UID 和容器保持不变,新的 10m small 请求必须被真实的 HTTP 403 拒绝。不要把策略缩减解读为已有 Pod 被自动终止。
即使 quota 的 used 大于 hard,已有对象也可能保留下来。
只回收已调查的请求并恢复预算
在 recover.json 中写入 remove=extra、requests.cpu=200m、replacement={requests:{cpu:50m,memory:32Mi},limits:{cpu:200m,memory:64Mi}}。act 8 只回收已调查的 extra,在用量减少之后调整预算并创建 small。用 capture 8 保存 budget_restored.json。请证明真实的 HTTP 201、Ready、最终请求合计 200m,以及对比组得到保留。
不仅要看删除的名称,还要确认 UID 和用量账簿的收敛。