默认值、配额与准入拒绝的账本
一句话总结
LimitRange 处理单个对象的默认值和范围,ResourceQuota 处理命名空间的合计。触发配额的请求,不是让 Pod 处于 Pending 等待,而是可能在创建阶段就被拒绝。
为什么需要它
如果一个小团队不小心创建了数千个对象,集群中的其他团队也会受到影响。资源策略不仅限制恶意攻击,也限制一个循环中的笔误所造成的损害。但是,设置了限制这件事本身并不能保证想要的行为。必须实际确认它适用于哪些请求、是否会动到已有对象、用量统计的又是什么。
假设想创建新 Pod 时出现了 Forbidden。如果一概把它看作账号权限问题,就会导致授予更多 RBAC 权限这种错误的应对。即使是同样的 HTTP 状态,响应正文里的原因和相关对象才是关键。配额超限不会因为增加权限而解决。通过删除限制来让它成功,也不是理解了策略的解决方式。
工作原理
这次实验为一个容器设置默认请求 CPU 100m、内存 32Mi,默认限制 CPU 200m、内存 64Mi。并排读取输入中省略了 resources 的新 Pod 和保存在 API 中的 Pod。如果保存的对象里出现了值,它可能不是用户文件里的值,而是 admission 过程中应用的默认值。不要假设已经明确写了 CPU 50m 创建的旧 Pod 被改成了 100m。
ResourceQuota 的 requests.cpu 限制属于该空间的目标 Pod 的 CPU 请求合计。它不是测量当前实际使用了百分之几 CPU 的数字。这个例子中的账簿是这样变化的。
| 状态 | 已保存的请求合计 | 允许上限 | 新请求的结果 |
|---|---|---|---|
| fit 50m + defaulted 100m | 150m | 200m | 再加 extra 100m 就是 250m,因此被拒绝 |
| 调整上限之后 | 150m | 300m | 同样的 extra 请求进来,合计 250m |
| 再次调低上限 | 250m | 100m | 已有 Pod 保持不变,新的 small 请求被拒绝 |
| 回收 extra、重新调整上限 | 150m | 200m | small 50m 进来,合计 200m |
这张表中对上限的修改,是在一次性命名空间中学习策略含义的实验。在生产中,应由审阅过用量、业务需求和团队分配的、有权限的负责人来修改策略。这并不是为了消除报错而建议增加配额。
把配额降到低于当前用量,并不会自动终止已有的 Pod。因此可以观测到 status.used 大于 spec.hard 的状态。这不是策略无效的证据。要确认的是,它是否在保留已有对象的同时拒绝新增创建。如果必须强制降低用量,就要另外做出运维决策,决定终止哪些任务。
ResourceQuota 可能不只限制一条轴。它可以同时检查 requests.cpu、requests.memory、limits.cpu、limits.memory、Pod 数量等,所以不能只算 CPU 就断定成功。这次实验让其余的上限留有余量,以分离出 CPU 请求量的效果。还要区分,是违反了 LimitRange 的单项上限,还是违反了 ResourceQuota 的合计上限。
在现场相遇的样子
把创建 Pod 之前的输入、API 响应和创建之后的查询结果,当作一个证据包来看。对于配额拒绝,要确认真实的 HTTP 403,以及 Kubernetes Status 的 Forbidden 原因、出现 team-budget 和 requests.cpu 的说明。接着确认同名的 Pod 不存在。不要在收到账号权限错误或 TLS 错误的情况下,就记录为想要的策略已生效。
相反,HTTP 201 表示对象创建被接受,并不是应用启动完成。要继续查看新的 Pod UID 是否与响应一致,以及是否已被放置到节点上、处于 Running 和 Ready。调度和 admission 是不同的阶段,所以“这个请求在 quota 之内”和“有运行这个 Pod 的节点”是两回事。
kubectl -n kcna-capacity get limitrange defaults -o json
kubectl -n kcna-capacity get resourcequota team-budget -o json
kubectl -n kcna-capacity get pods -o json
状态账簿在变更之后,可能仍然显示之前的值。删除自己拥有的 extra Pod 之后,要确认对象确实不存在以及 quota 用量已减少,再发送下一个请求。不要只凭固定的短 sleep 就假定已经完成。等待要设置时限,超时后不要用成功来掩盖,而是保留观测结果。
下一项实验要做什么
直接比较在资源看起来还有剩余的情况下也会发生的两种失败。放置失败用真实的 Pod UID 和调度器事件来证明,准入拒绝用 API 状态和对象不存在来证明。在配额缩减之后,确认原有 fit、defaulted、extra 的 UID 和容器保持不变,然后只回收调查过的 extra,并重新接受一个小请求。不要改动其他命名空间中的正常对比组。