不写 webhook,怎么在 API Server 里拦住不合规的对象?策略和绑定各管什么?参数怎么传?scale 为什么没被拦?这篇用一条最简单的“Deployment 副本数不超过 3”,把 VAP 的核心概念和几个容易漏的点验证一遍。
只有策略、没有绑定,创建 replicas=10 的 Deployment:退出码 0;['deployment.apps/d1 created'](策略没有生效) 创建绑定(validationActions: [Deny],只作用在命名空间 k87)后 0.9 秒开始生效 replicas=10:退出码 1;['The deployments "d2" is invalid: : ValidatingAdmissionPolicy \'k87-max-replicas\' with binding \'k87-bind\' denied request: 副本数 10 超过上限 3'] replicas=3:退出码 0;['deployment.apps/d3 created'] patch 把 d3 改成 replicas=8(UPDATE 也被拦):退出码 1;['The deployments "d3" is invalid: : ValidatingAdmissionPolicy \'k87-max-replicas\' with binding \'k87-bind\' denied request: 副本数 8 超过上限 3'] kubectl scale deploy/d3 --replicas=8:退出码 0;['deployment.apps/d3 scaled'](scale 走的是 deployments/scale 子资源,策略里只写了 deployments,没有匹配上) 同样的 Deployment 放到 default 命名空间(绑定的 namespaceSelector 没选中它):退出码 0;['deployment.apps/d4 created']
策略 k87-max-replicas 匹配 apps/v1 的 deployments(CREATE、UPDATE),CEL 表达式是 object.spec.replicas <= 3,messageExpression 拼出提示。只有策略没有绑定时,创建 replicas=10 的 Deployment 照样成功。创建绑定(validationActions: [Deny],namespaceSelector 只选 k87)0.9 秒后生效:replicas=10 被拒——ValidatingAdmissionPolicy 'k87-max-replicas' with binding 'k87-bind' denied request: 副本数 10 超过上限 3;replicas=3 通过;patch 把 d3 改成 8 也被拒(UPDATE 同样检查);default 命名空间里的同样对象没被拦。但 kubectl scale 成功了:它走的是 deployments/scale 子资源,策略里只写了 deployments,没有匹配上——子资源要单独列在 resourceRules 里。
创建参数化策略和绑定之后 0.9 秒开始生效(阈值来自 ConfigMap limits) 参数 maxReplicas=5,创建 replicas=4:退出码 0;['deployment.apps/p1 created'] replicas=6:退出码 1;['The deployments "p2" is invalid: : ValidatingAdmissionPolicy \'k87-param-limit\' with binding \'k87-bind-param\' denied request: 副本数 6 超过参数 maxReplicas=5'] 把参数改成 8:0.1 秒后 replicas=6 通过了(改的是 ConfigMap,策略本身没动)
策略声明 paramKind: ConfigMap,表达式 object.spec.replicas <= int(params.data.maxReplicas);绑定的 paramRef 指向 ConfigMap limits(maxReplicas: "5")。创建后 0.9 秒生效:replicas=4 通过,replicas=6 被拒——副本数 6 超过参数 maxReplicas=5。把 ConfigMap 里的 maxReplicas 改成 8,0.1 秒后 replicas=6 就通过了:改的是参数,策略本身没动。同一个策略可以配多个绑定、各用不同的参数,适合按环境区分阈值。
validationActions: [Warn]:退出码 0;['deployment.apps/w1 created', "Warning: Validation failed for ValidatingAdmissionPolicy 'k87-max-replicas' with binding 'k87-bind': 副本数 10 超过上限 3"](创建成功,只返回 Warning)
把 validationActions 改成 [Warn, Deny]:退出码 1;['The ValidatingAdmissionPolicyBinding "k87-bind" is invalid: spec.validationActions: Invalid value: ["Warn","Deny"]: must not contain both Deny and Warn (repeating the same validation failure information in the API response and headers serves no purpose)']
改回 [Deny]:退出码 1;['The deployments "w2" is invalid: : ValidatingAdmissionPolicy \'k87-max-replicas\' with binding \'k87-bind\' denied request: 副本数 10 超过上限 3']
表达式里写错字段名(object.spec.replica):0.0 秒后策略的 status.typeChecking.expressionWarnings=[('spec.validations[0].expression', "apps/v1, Kind=Deployment: ERROR: <input>:1:12: undefined field 'replica' | object.spec.replica <= 3 | ...........^ ")]validationActions: [Warn] 时创建 replicas=10 的 Deployment 成功,返回 Warning: Validation failed for ValidatingAdmissionPolicy … 副本数 10 超过上限 3——适合上线前观察;改成 [Warn, Deny] 绑定本身被拒(spec.validationActions: must not contain both Deny and Warn);改回 [Deny] 才拒绝创建。表达式里写错字段名 object.spec.replica,策略的 status.typeChecking.expressionWarnings 立刻出现 undefined field 'replica' 的警告,能在上线前发现。