LabHub
学习 学习路径 课程

CNPE — 云原生平台工程师

获准创建却无处运行的服务器:资源边界

在 LabHub 中继续学习

目标

在个人 k3s 中亲自处理默认值变更 → 配额拒绝 → CPU 不足导致 Pending → 恢复 → 团队级 API 权限。

为什么重要

平台接受了请求,与是否有位置可运行是两回事。ResourceQuota 限制团队请求总量,却不会为某个节点预留运行位置。LimitRange 即使会填充空请求,也不会修改既有 Pod。混淆两者后,运维人员只会不断提高配额,用户则在 Pending 页面前等待。本练习训练 CNPE 多租户与容量判断能力的一部分,不替代正式考试题目或整个考试范围。

已准备内容

环境中有 Ubuntu 个人 VM、k3s v1.36.4+k3s1、busybox 真实容器和五个专用 Namespace。需要掌握 kubectl 资源查询、JSON、jq、RBAC 基础。KUBECONFIG 是 /etc/rancher/k3s/k3s.yaml,所有相对文件路径均位于 /root/cnpe-capacity 下。baseline.json 和 baseline-origin.json 保存初始 UID、节点容量和 old 数据,请勿修改。new-pod.json 是省略资源的 Pod 材料,oversized-pod.json 是待修改的 1 核 Pod 材料。old、memory-only、first、second、sample 已预先运行。从空 RBAC 开始,只添加必要权限。不要放入外部集群 kubeconfig 或真实凭据。所有变更仅在个人 VM 内进行。

本练习时长 75 分钟,请在默认 60 分钟会话中使用+时间延长。会话结束后 VM 与文件都会消失。请在结束前另行保存所需报告。首次准备可能需要数分钟。

步骤

  1. 保存既有 Pod 的身份证——查看 kubectl get nodes 和 cnpe-cap-defaults 中的 old Pod。把 baseline.json 的 old 对象保存到 before.json。确认 UID 及请求量 25m、32Mi,不要删除或重建 old。
  2. 只改变新入场者的默认值——修改 cnpe-cap-defaults 的 LimitRange defaults。limits 中只保留一条 type Container 规则。defaultRequest 为 cpu 50m、memory 48Mi,default 为 cpu 100m、memory 64Mi。既有 old 的请求量和 UID 必须保持不变。
  3. 只有内存相同就是 Guaranteed 吗——提交准备好的 new-pod.json(不含资源项),创建 cnpe-cap-defaults/new。等待 Ready,并确认新请求为 50m、48Mi。cnpe-cap-memory/memory-only 的内存 request=limit=64Mi,且没有 CPU。在 qos.json 的 new 和 memory_only 中分别记录各 Pod 的 status.qosClass。
  4. 在入口拒绝第三位访客——使用 quota-manifest.json 在 cnpe-cap-quota 中创建 ResourceQuota budget。hard 为 requests.cpu 100m、requests.memory 256Mi、pods 4。既有 first、second 各请求 50m。当 status.used.requests.cpu 达到 100m 后,执行 collect quota。第三个 50m 请求应因 CPU 配额被拒绝,且对象不得存在。
  5. 有入场券却没有座位的服务器——把 oversized-pod.json 中 too-large Pod 的 CPU request 与 limit 都改成 baseline.json 中的 wanted_cpu。该值是节点 allocatable 核心数向上取整后加 1。保持 cnpe-cap-schedule 的既有配额不变并创建 Pod。确认 PodScheduled=False、Unschedulable、Insufficient cpu 以及未被调度;当配额 used CPU 等于请求量后,执行 collect pending。
  6. 不提高配额进行恢复——在 recovery-pod.json 中保持相同 Pod 名称、Namespace 和安全设置,写入 requests cpu 25m、memory 32Mi,limits cpu 25m、memory 64Mi。只删除 too-large,再使用该文件重建。确认新 UID 和 Ready 后执行 collect recovered。保留 pending 材料和既有 schedule 配额。
  7. 只给团队成员读取权限——在 role.json 和 binding.json 中编写并应用 cnpe-cap-quota 的 Role reader 和 RoleBinding reader。只允许既有 ServiceAccount tenant 对 core API 的 pods 执行 get、list。执行 collect rbac,采集成功读取自己的 first、拒绝读取 cnpe-cap-other/sample、拒绝修改 budget 的结果。不要添加 ClusterRoleBinding 或通配符权限。
  8. 区分已经证明与仍未知的事项——在 report.json 中将 node_allocatable_m、pending_request_m、quota_hard_m、recovered_request_m 记录为 millicore 整数。old_pod_recreated 是比较初始与当前 UID 得到的布尔值,memory_only_qos 是实际 QoS。将 recovery_proves 写为 placement_only,isolation_proves 写为 api_permissions_only,cost_slo_verified 写为 false。只能使用这九个字段,并保持最终恢复状态和权限。

参考

采集格式为 python3 /opt/fixtures/cnpe-capacity-lab.py collect <단계>。 阶段为 quota、pending、recovered、rbac。采集器把测试请求和观测资料保存为各阶段的 .json 与 -origin.json。quota 会测试创建小 Pod 是否被拒绝;若被允许,只回收该测试 Pod。rbac 会测试 tenant 的读取请求以及使用相同上限值的 PATCH。文件保存成功并不代表所需状态成功,请确认评分结果。 评分会读取材料和当前 API,不会修改答案或权限。由于会单独保留过去的 Pending 材料,恢复后仍能重新评分第 5 步。若在错误条件下采集,必须修正条件后重新采集。不要删除 old 和其他初始 Pod。

保存副本的比对只用于防止意外编辑,不能防止学生 root 篡改。本练习不测量实际使用量、延迟、成本、SLO 或网络阻断。不要得出 Ready 能保证性能,或 API 权限分离就等于完整租户隔离的结论。

官方文档

保存既有 Pod 的身份证

查看 kubectl get nodes 和 cnpe-cap-defaults 中的 old Pod。把 baseline.json 的 old 对象保存到 before.json。确认 UID 及请求量 25m、32Mi,不要删除或重建 old。

Pod 名称可以重复使用,但重建后 UID 会改变。使用 jq 的 .old 提取嵌套对象。

只改变新入场者的默认值

修改 cnpe-cap-defaults 的 LimitRange defaults。limits 中只保留一条 type Container 规则。defaultRequest 为 cpu 50m、memory 48Mi,default 为 cpu 100m、memory 64Mi。既有 old 的请求量和 UID 必须保持不变。

默认值在 API 准入阶段填充。修改 LimitRange 不会让既有 Pod 重新准入。

只有内存相同就是 Guaranteed 吗

提交准备好的 new-pod.json(不含资源项),创建 cnpe-cap-defaults/new。等待 Ready,并确认新请求为 50m、48Mi。cnpe-cap-memory/memory-only 的内存 request=limit=64Mi,且没有 CPU。在 qos.json 的 new 和 memory_only 中分别记录各 Pod 的 status.qosClass。

本练习按容器判断 Guaranteed 时,同时检查 CPU 和内存条件。Running 与 QoS 是不同字段。

在入口拒绝第三位访客

使用 quota-manifest.json 在 cnpe-cap-quota 中创建 ResourceQuota budget。hard 为 requests.cpu 100m、requests.memory 256Mi、pods 4。既有 first、second 各请求 50m。当 status.used.requests.cpu 达到 100m 后,执行 collect quota。第三个 50m 请求应因 CPU 配额被拒绝,且对象不得存在。

即使 Pod 数量尚未用满 4,请求 CPU 总量也已达到上限。本步骤不测量 CPU 使用率。

有入场券却没有座位的服务器

把 oversized-pod.json 中 too-large Pod 的 CPU request 与 limit 都改成 baseline.json 中的 wanted_cpu。该值是节点 allocatable 核心数向上取整后加 1。保持 cnpe-cap-schedule 的既有配额不变并创建 Pod。确认 PodScheduled=False、Unschedulable、Insufficient cpu 以及未被调度;当配额 used CPU 等于请求量后,执行 collect pending。

分别确认 API 是否创建对象,以及调度器是否把它放到节点上。describe pod 和 ResourceQuota status 是不同证据。

不提高配额进行恢复

在 recovery-pod.json 中保持相同 Pod 名称、Namespace 和安全设置,写入 requests cpu 25m、memory 32Mi,limits cpu 25m、memory 64Mi。只删除 too-large,再使用该文件重建。确认新 UID 和 Ready 后执行 collect recovered。保留 pending 材料和既有 schedule 配额。

这里通过重建进行恢复。不要因为降低请求后成功调度,就断定服务性能已经足够。

只给团队成员读取权限

在 role.json 和 binding.json 中编写并应用 cnpe-cap-quota 的 Role reader 和 RoleBinding reader。只允许既有 ServiceAccount tenant 对 core API 的 pods 执行 get、list。执行 collect rbac,采集成功读取自己的 first、拒绝读取 cnpe-cap-other/sample、拒绝修改 budget 的结果。不要添加 ClusterRoleBinding 或通配符权限。

采集器会模拟 tenant 发出真实请求。即使 PATCH 使用相同的值,没有修改权限也必须被拒绝。

区分已经证明与仍未知的事项

在 report.json 中将 node_allocatable_m、pending_request_m、quota_hard_m、recovered_request_m 记录为 millicore 整数。old_pod_recreated 是比较初始与当前 UID 得到的布尔值,memory_only_qos 是实际 QoS。将 recovery_proves 写为 placement_only,isolation_proves 写为 api_permissions_only,cost_slo_verified 写为 false。只能使用这九个字段,并保持最终恢复状态和权限。

1000m 等于 1 核。CPU 请求量不是使用量,API 拒绝也不能证明网络隔离。