题干与适用场景
平台团队要在 Pod 创建时注入默认安全上下文和观测标签,但不想维护外部 mutating webhook。请基于 Kubernetes MutatingAdmissionPolicy 设计策略、绑定、冲突处理、审计和回滚方案。
Kubernetes v1.36 将 MutatingAdmissionPolicy 标记为 stable。它在 API server 内用 CEL 描述匹配与变更,可通过 ApplyConfiguration 或 JSONPatch 修改入站对象;策略定义与绑定分离。面试重点是把声明式变更放进可审计、可回滚的 admission 链,而不是简单把 webhook YAML 改成另一种格式。
面试官考察点
面试官会看你能否区分 policy 与 binding;能否选择 ApplyConfiguration 或 JSONPatch;能否保证补丁幂等、字段所有权清晰且不覆盖用户值;能否处理 failurePolicy、匹配范围、顺序、冲突、自身保护和升级回滚;能否用审计与指标证明变更真的生效。
回答前需要澄清的问题
目标对象与默认值
确认只处理 Pod 还是也处理 Deployment、Job 等工作负载,哪些字段是强制默认,哪些字段允许用户覆盖,以及服务账号、命名空间和标签选择范围。
版本与运行边界
确认集群是否为 v1.36、是否启用 admissionregistration.k8s.io/v1,现有 webhook 是否仍在链路中,以及策略是否必须跨多个集群复用。
风险与回滚
确认不能被修改的安全字段、允许的故障模式、审计保留期、变更窗口和禁用策略时对已有对象及新请求的影响。
30 秒回答框架
“我先用 policy 定义匹配条件和幂等变更,再用 binding 限定命名空间、资源和参数。简单默认值优先 ApplyConfiguration,需要精确数组操作时才用 JSONPatch。策略不匹配时跳过,变更错误按 failurePolicy 决定 fail 或 ignore;我会避免匹配自身并限制 RBAC。上线采用 dry-run、分层绑定和审计指标,回滚先解除 binding,再按版本恢复策略,确保已有对象不会被误改。”
分步骤深入解答
第一步:拆分策略与绑定
Policy 保存规则、变量、匹配条件和变更表达式;Binding 决定哪些资源与命名空间采用该策略,并可提供参数。这样可以复用同一策略,在不同租户或环境用不同绑定逐步放量。
第二步:选择变更表示
ApplyConfiguration 适合表达结构化默认值,能让变更意图接近对象模型;JSONPatch 适合精确增删路径,但必须正确处理数组索引和 JSON Pointer 转义。两者不能混用成“先覆盖再猜测”,每个字段都要有明确的所有权与覆盖规则。
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingAdmissionPolicy
metadata:
name: pod-default-observability
spec:
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE"]
resources: ["pods"]
mutations:
- applyConfiguration:
expression: >-
Object{metadata: Object{labels: {"observability.example.com/enabled": "true"}}}
---
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingAdmissionPolicyBinding
metadata:
name: pod-default-observability-binding
spec:
policyName: pod-default-observability
matchResources:
namespaceSelector:
matchLabels:
platform.example.com/enabled: "true"第三步:保证幂等与不覆盖
只对缺失字段写入默认值,用户已显式设置的值保持不变。对列表字段要定义按键合并还是整列表替换;多次 admission、重试和 update 都不能不断追加重复元素。对修改后的对象重新评估会不会触发同一规则,避免自激循环。
第四步:控制匹配与自身保护
用 apiGroups、资源、操作、namespaceSelector 和 objectSelector 缩小范围。MutatingAdmissionPolicy 不能匹配自身或其 binding,避免策略把自己的配置改到不可恢复。高风险字段采用显式 allowlist,不让任意用户提供的 CEL 参数扩大写权限。
第五步:定义失败与顺序
区分“不匹配”“表达式返回空”“变更错误”和 API server 暂时失败。failurePolicy 需要结合安全目标选择 Fail 或 Ignore,并配套告警。不要依赖多个 mutator 的隐含调用顺序;若多个策略写同一字段,应拆分所有权、让结果可交换,或在单一策略中集中决策。
第六步:迁移 webhook 与版本化
先以 shadow 或只写审计的方式比较旧 webhook 与新 policy 的结果,再绑定到小范围命名空间。记录策略版本、请求 UID、原对象摘要和变更原因;确认没有冲突后逐步解除 webhook。每次变更保留可回滚的 policy 名称与 manifest,避免原地编辑导致无法比较。
第七步:回滚与观测
紧急回滚先暂停或删除 binding,让新请求停止变更,再恢复上一版 policy。已有对象不会因为解除 binding 自动反向修改,若需要清理必须用单独、可审计的控制器或批处理。监控 admission 延迟、拒绝率、变更计数、表达式错误和按命名空间的命中率。
高质量示范回答
我会把 policy 当作不可变规则版本,把 binding 当作放量和权限边界。首先限定 CREATE Pod 与目标命名空间,只对缺失标签和安全默认值做幂等 ApplyConfiguration;涉及数组路径时使用经过测试的 JSONPatch。failurePolicy、字段 allowlist、RBAC 和自身保护先定义清楚,避免多个策略争抢同一字段。迁移时以旧 webhook 结果做对照,先小范围绑定,再用审计、延迟和错误指标扩大范围。回滚解除 binding 并恢复上一版 policy;已有对象不自动回滚,所以需要独立的清理流程和审批记录。
常见错误
- 错误表现: 把所有字段整对象覆盖。→ 失败原因: 会擦掉用户显式配置并制造所有权冲突。→ 修正方法: 只写缺失字段,定义列表合并规则。
- 错误表现: 以为删除 binding 会恢复旧对象。→ 失败原因: admission 只影响请求,不提供反向变更。→ 修正方法: 另设可审计清理流程,并说明现有对象不会自动回滚。
- 错误表现: 多个 policy 依赖固定执行顺序。→ 失败原因: admission 链顺序变化会产生不同结果。→ 修正方法: 分离字段所有权或集中到单一策略。
- 错误表现: 直接替换生产 webhook。→ 失败原因: 新旧表达式差异可能在高峰期放大。→ 修正方法: 先 shadow、分命名空间放量,保留版本化 manifest。
追问及应对
ApplyConfiguration 和 JSONPatch 怎么选?
结构化默认值优先 ApplyConfiguration,表达意图更清楚;需要精确删除、插入或转义路径时用 JSONPatch。无论选择哪种,都要测试重复执行和数组冲突。
failurePolicy 应该总是 Fail 吗?
安全基线或合规字段通常倾向 Fail,非关键观测标签可以在明确告警和补偿机制下选择 Ignore。决策要与影响范围、可用性目标和审计证据绑定。
如何测试策略不会改坏对象?
用服务器 dry-run、固定输入快照、重复 admission、不同 namespace 标签、用户已设置字段、空列表和表达式错误做矩阵测试,并比较旧 webhook 与新 policy 的差异。
为什么不直接写一个 controller?
Admission 能在对象持久化前阻止或修改请求,适合默认值和入口约束;controller 适合异步收敛和已有对象修复。两者职责不同,可组合但不能用 controller 代替入口安全策略。