基线钉在哪里才守得住
一句话总结
平台最低基线(baseline)必须以API 服务器能够拒绝的形式存在,而不能只写在 Wiki 文档里,才能真正得到遵守。提升基线时,应先发出警告、调查影响,并为暂时无法遵守的团队提供带到期日的例外。
为什么需要它
平台团队最先制作的产物通常是文档。他们会整理“所有 Pod 不以 root 身份运行”“镜像标签必须固定”“请开放指标端口”等规则,并发布到内部 Wiki。半年后检查集群,却发现遵守这些规则的工作负载还不到一半。
原因很简单:文档不会拒绝任何东西。违反规则的部署仍会成功,而已经成功的部署不会有人主动撤回。相反,如果 API 服务器拒绝请求,用户会当场得知,不修正就无法部署。基线的实体不是文档,而是拒绝违规的能力。
但如果一开始就阻止所有违规,又会产生其他问题。昨天还能执行的部署今天突然被阻止,平台就会被记作故障原因。因此,基线必须同时具备等级与实施流程。
它如何工作
Pod Security Admission 的三个等级与三种模式
Kubernetes 内置 Pod Security Admission 只需命名空间标签即可工作。等级分为 privileged、baseline 和 restricted 三种,每个等级都可以独立设置模式。
| 模式 | 作用 | 使用场景 |
|---|---|---|
enforce |
阻止创建违规 Pod | 当前已经能够遵守的等级 |
audit |
只写入审计日志 | 日后查看统计信息 |
warn |
向创建者显示警告 | 下一步计划提升到的等级 |
由此可以得到一个技巧:**enforce 设置为当前能够遵守的等级,warn 设置为下一步计划提升的等级。**这样,开发者每次部署都会提前看到“下一阶段会阻止此项”的提示,在正式提升时就不会措手不及。
标签中除了等级,还应同时写明版本(enforce-version: v1.30)。等级定义会随 Kubernetes 版本逐步扩展;如果不指定版本,升级集群时策略内容会悄然变化。明明当天没人修改策略,部署却突然被阻止,可能要花一整天才能找到原因。
提升前如何调查
不要凭猜测判断能否提升等级,而应询问服务器。通过服务器 dry-run发送修改命名空间标签的请求,API 服务器会按照新等级评估该命名空间中当前运行的 Pod,并以警告形式返回结果。哪些 Pod 因什么原因不符合要求会完整显示,而且不会创建或修改任何对象。
一致性(conformance)就是可移植性
CNCF 所说的一致性不是一枚“认证印章”,而是一项承诺:只使用稳定的上游 API,就能在任何集群中以相同方式运行。因此,保留已弃用 API 会破坏一致性。使用 extensions/v1beta1 编写的清单在新集群中不会因语法错误被拒绝,而会发生映射失败,因为该 API 组根本不再提供。此时会出现“no matches for kind”;读懂这句话,就能在一分钟内定位原因。
迁移时有时还会增加必填字段。把 Ingress 迁移到 networking.k8s.io/v1 后,每条路径都必须指定 pathType。过去由不同控制器各自解释的部分,现在由 API 明确规定。
可观测性是一份契约
平台若承诺“自动收集指标”,就必须有对象明确说明收集依据。Prometheus Operator 的 ServiceMonitor 承担这一职责。ServiceMonitor 通过 Service 标签选择目标,并通过 Service 端口名称选择抓取端点。任何一项不匹配,对象仍会正常创建,只有指标会悄然为空。因此,建立关系后还必须反向查询,确认它“实际选择了什么”。
例外必须带有到期日
总会有团队暂时无法遵守基线。此时有三种选择:降低基线、阻止该团队,或提供带到期日的例外。前两项分别会让平台失去意义,或让平台成为敌人。应在对象中记录为何例外以及例外持续到何时。没有记录的例外会变成永久豁免;永久豁免不断累积后,基线又会退回为一份文档。
实际现场中的表现
运行本课程的家庭实验室也经历过同样的问题。实验 Pod 以非特权方式启动(移除全部 capability)。如果这项前提只写在文档中,为了方便一定会逐项放开权限。现在,Pod 规范本身会强制执行,所以实验无法运行时,我们会修改实验设计,而不是开放权限。当基线存在于对象中,争论就会转移到设计层面。
可观测性契约方面也发生过同类事故。指标目标因标签中的一个字符而没有匹配成功,但对象状态正常,只有仪表板为空,因此很长时间无人发现。“已经创建”与“确实选择了某些对象”是不同命题,只有反向询问集群才能确认这种区别。
下一项实验将做什么
我们会在一个命名空间中建立基线,并确认它确实会拒绝违规请求。亲眼观察弃用 API 为何被拒绝并迁移到稳定 API;随后通过 ServiceMonitor 建立抓取契约,再反向确认其选择器确实选中了目标 Service。最后,向服务器查询把基线提升一级的影响,并为无法遵守的命名空间留下带到期日的例外。