多租户资源与事故响应
目标
创建三个租户,用配额和 LimitRange 划分资源,亲自计算并记录超额分配(overcommit)的比例,复现调度事故并用数字对其分类,在不删除需求的前提下恢复,然后编写平台自身的记录规则和告警并部署到集群。
为什么重要
在这里练习的不只是工具操作,还有依据证据做出判断。出了事故先数什么,把这个值写在哪里,以及什么才能称为恢复,就是这样的顺序。
最后一项尤其重要。删除约束让 Pod 启动,与满足约束让 Pod 启动,在界面上是无法区分的。两者都是 Running,两者都会让告警关闭。区分它们的是人,而练熟这种判断,就是本实验的目的。
这个集群的节点有三台,每台 8 核。所以整个集群的 allocatable CPU 是 24 核。把配额的合计除以这个值,就是超额分配的比例。
这个环境使用真实的 API 服务器、调度器以及 KWOK 虚拟节点。Running 是为了学习调度状态,并不是 GPU 或 ledger 进程真正运行的证据。不运行 Prometheus Operator 和 Alertmanager。最后两个步骤中的 PromQL,会用镜像中自带的 promtool 3.0.1 进行真实评估,CR 则会验证到 API 存储的内容。
工作目录是 /root/cnpe-ops。时间序列测试使用虚拟时间,所以不必真的等待 25 分钟。每次评分都在 45 秒内检查,不会修改学员文件或 API 对象。
步骤
- 创建命名空间
tenant-red、tenant-green、tenant-gold,并给每个加上platform.labhub.io/tenant标签。值是从名称中去掉tenant-之后的部分。然后在/root/cnpe-ops/inventory.txt中写入tenants和tenant_list两行。列表按名称排序,只用逗号连接。 - 在
/root/cnpe-ops/quotas.yaml中写入三个租户的 ResourceQuota。名称分别是在命名空间名称后加上-quota,例如tenant-red-quota,并且必须包含requests.cpu。三个配额的requests.cpu合计除以 24 所得的值,必须大于 1.00 且不超过 1.25,并且任何租户都不能低于 6 核。然后在/root/cnpe-ops/capacity.txt中写入quota_cpu、allocatable_cpu、overcommit三行。 - 在
/root/cnpe-ops/limitranges.yaml中写入三个租户的 LimitRange。名称像tenant-red-limits这样命名。同时设置defaultRequest和default,但defaultRequest的 CPU 必须低于default的 CPU,并用max阻止单个容器请求 4 核 CPU。 - 在
/root/cnpe-ops/ledger.yaml中编写 Deploymentledger并应用到tenant-red。replicas 为 3,Pod 标签为app=ledger,nodeSelector只有一个platform.labhub.io/pool=gpu,容器请求为 CPU 500m 和内存 512Mi,镜像用摘要固定。此时 Pod 无法启动。 - 在
/root/cnpe-ops/triage.txt中写入selector_key、selector_value、replicas、required_cpu四行。required_cpu写入容器请求乘以副本数之后的值,单位为毫核。 - 只给一个节点加上
platform.labhub.io/pool=gpu标签来恢复。Deployment 的nodeSelector和 replicas 必须保持不变。一直确认到三个 Pod 都变为 Running。 - 在
/root/cnpe-ops/platform-rules.yaml中设置组platform-slo和interval: 1m。记录规则platform:provision_success:ratio5m是最近 5 分钟成功请求 rate 之和 / 全部请求 rate 之和。先按序列计算 rate,再求和,如果只有失败序列则为 0%,如果只有成功序列则为 100%,对于无流量和没有观测,则不输出比例序列。PlatformProvisionFailing在这个比例低于 95% 的状态持续 10 分钟时触发,恢复后解除。设置severity: critical、有意义的summary和runbook_url。语法检查之后,用python3 /opt/fixtures/cnpe_slo_contract.py rules让 13 个时间序列事件通过。95% 是本实验的阈值,不是推荐用于生产的 SLO。 - 创建命名空间
monitoring,并在/root/cnpe-ops/platform-rules-cr.yaml中编写 PrometheusRuleplatform-slo并应用。规则文件、清单的 spec.groups 以及 API 的 spec.groups 必须完全一致。在/root/cnpe-ops/ops-report.txt中不重复地以查询得到的整数记录tenants、pool_nodes、ledger_running、alert_rules四行。本实验的目标分别是 3、1、3 以及实际的告警规则数。请用python3 /opt/fixtures/cnpe_slo_contract.py deploy确认。API 存储成功并不意味着生产 Prometheus 已加载规则,或者告警已送达。
参考
- 用标签统计出来的值,就是平台自己所知道的值。请用
kubectl get ns -l platform.labhub.io/tenant来统计。 - 节点的 allocatable 可以用
kubectl get nodes -o json | jq '[.items[].status.allocatable.cpu | tonumber] | add'重新统计。 - 要看 LimitRange 实际填充了什么,最可靠的方法是用
--dry-run=server提交一个没有填写请求的 Pod,并读取结果。 - 加上标签之后,请给调度器留出跑一轮的时间。如果立即确认并下判断,就会把正确的处理撤销。
- 一个常见的错误是为了让 Pod 启动而删除
nodeSelector。那不是恢复,而是删除需求。 - 另一个是把与已验证的规则文件不同的内容作为 PrometheusRule 部署。这样 promtool 通过就什么也无法保证了。
- 请阅读 Prometheus 规则单元测试和告警规则。语法与触发行为是两回事。
- runbook.example.invalid 是用于说明格式的地址。在生产中必须替换为有真实负责人、诊断和恢复流程的文档。
让机器能够统计租户
创建命名空间 tenant-red、tenant-green、tenant-gold,并给每个加上 platform.labhub.io/tenant 标签。值是从名称中去掉 tenant- 之后的部分。然后在 /root/cnpe-ops/inventory.txt 中写入 tenants 和 tenant_list 两行。列表按名称排序,只用逗号连接。
如果只有命名规则而没有标签,租户列表就只存在于人的记忆中。加上标签之后,请用标签选择器重新统计,并写下结果。
把承诺的量与实际拥有的量相除
在 /root/cnpe-ops/quotas.yaml 中写入三个租户的 ResourceQuota。名称分别是在命名空间名称后加上 -quota,例如 tenant-red-quota,并且必须包含 requests.cpu。三个配额的 requests.cpu 合计除以 24 所得的值,必须大于 1.00 且不超过 1.25,并且任何租户都不能低于 6 核。然后在 /root/cnpe-ops/capacity.txt 中写入 quota_cpu、allocatable_cpu、overcommit 三行。
配额是在每个命名空间中独立设置的,并不参照集群容量。所以合计必须由人另外统计。三个节点各 8 核,所以分母是 24。
确定为没有填写的人补上的值
在 /root/cnpe-ops/limitranges.yaml 中写入三个租户的 LimitRange。名称像 tenant-red-limits 这样命名。同时设置 defaultRequest 和 default,但 defaultRequest 的 CPU 必须低于 default 的 CPU,并用 max 阻止单个容器请求 4 核 CPU。
default 是 limit 的默认值,defaultRequest 是 request 的默认值。不会用默认值覆盖已经声明的 request。在这个任务中,把 CPU 的默认 request 设得小于 limit,以区分预留量。是否为 Guaranteed,必须同时确认所有容器的 CPU 和内存条件。max 是按容器的上限。
复现事故并保留下来
在 /root/cnpe-ops/ledger.yaml 中编写 Deployment ledger 并应用到 tenant-red。replicas 为 3,Pod 标签为 app=ledger,nodeSelector 只有一个 platform.labhub.io/pool=gpu,容器请求为 CPU 500m 和内存 512Mi,镜像用摘要固定。此时 Pod 无法启动。
现在这些节点上没有池标签。所以这个工作负载无法启动。这种状态在这一步是正常的。评分的对象不是 Pod 的当前状态,而是这是一个要求什么的工作负载。
把分类的结果写成数字
在 /root/cnpe-ops/triage.txt 中写入 selector_key、selector_value、replicas、required_cpu 四行。required_cpu 写入容器请求乘以副本数之后的值,单位为毫核。
不要写感觉,而要写四个数字。所需的 CPU 总量,是容器请求乘以副本数的值,用毫核表示。请直接从集群中读取这些值。
不删除约束地恢复
只给一个节点加上 platform.labhub.io/pool=gpu 标签来恢复。Deployment 的 nodeSelector 和 replicas 必须保持不变。一直确认到三个 Pod 都变为 Running。
删除选择器,Pod 会立即启动,但那不是恢复,而是删除需求。请修正节点一侧,使约束得到满足。而且只加需要的数量。如果加在两个节点上,那个池的含义就模糊了。
编写平台自身的记录规则和告警
在 /root/cnpe-ops/platform-rules.yaml 中设置组 platform-slo 和 interval: 1m。记录规则 platform:provision_success:ratio5m 是最近 5 分钟成功请求 rate 之和 / 全部请求 rate 之和。先按序列计算 rate,再求和,如果只有失败序列则为 0%,如果只有成功序列则为 100%,对于无流量和没有观测,则不输出比例序列。PlatformProvisionFailing 在这个比例低于 95% 的状态持续 10 分钟时触发,恢复后解除。设置 severity: critical、有意义的 summary 和 runbook_url。语法检查之后,用 python3 /opt/fixtures/cnpe_slo_contract.py rules 让 13 个时间序列事件通过。95% 是本实验的阈值,不是推荐用于生产的 SLO。
值为 0 的向量也会激活告警。如果在告警中直接使用 bool 比较,即使正常时也会留下序列。请区分只有失败时的空分子,与没有全部流量时的分母。先读一读失败消息中的虚构事件和时刻,再检查公式和 for。
部署已验证的规则并记录当前状态
创建命名空间 monitoring,并在 /root/cnpe-ops/platform-rules-cr.yaml 中编写 PrometheusRule platform-slo 并应用。规则文件、清单的 spec.groups 以及 API 的 spec.groups 必须完全一致。在 /root/cnpe-ops/ops-report.txt 中不重复地以查询得到的整数记录 tenants、pool_nodes、ledger_running、alert_rules 四行。本实验的目标分别是 3、1、3 以及实际的告警规则数。请用 python3 /opt/fixtures/cnpe_slo_contract.py deploy 确认。API 存储成功并不意味着生产 Prometheus 已加载规则,或者告警已送达。
只有组名相同,而表达式、阈值、for 不同,就不是同一条规则。请把已验证的文件原样包装成 CR,再比较 API 的完整 spec.groups。查询失败时不要记录为 0,而要先解决 API 错误。