Kyverno 可以在工作负载进入集群前校验、修改或生成 Kubernetes 资源,并直接使用 YAML 表达规则,不需要再引入一门独立的策略语言。真正棘手的问题往往不是把规则写出来,而是让测试环境提供足够真实的生产上下文:命名空间标签、准入请求、调用者身份以及集群内对象都可能改变策略结果。
“让策略引擎以为自己在生产环境”不应理解为伪造一个简单的环境变量。更可靠的做法,是在隔离集群中重建策略实际依赖的信号,然后通过 Kubernetes API Server 触发完整的准入链路。
策略判断的不是“环境”,而是上下文
Kyverno 的校验规则最终面对的是一份 AdmissionReview。它通常包含待创建或更新的对象、命名空间、操作类型和用户信息。策略还可以读取集群中的其他资源,例如命名空间标签。
因此,一条只在生产环境生效的规则,需要先明确“生产”的权威定义。常见选择包括:
- 命名空间标签,例如
environment=production - 集群级配置对象,例如由平台团队维护的 ConfigMap
- 独立的生产集群边界
- 准入请求中的用户或用户组
其中,命名空间标签容易理解和测试,但标签本身也必须受到保护。如果普通开发者可以随意把 environment=production 改成 staging,策略边界就只剩下一层可修改的字符串。
不要依赖镜像名称、集群域名或客户端自行提交的注解来猜测环境。这些信号容易漂移,也容易被绕过。
可以这样实践:在隔离集群重建生产信号
来源摘要没有给出具体实验配置,下面是一套可以改造的最小实践:创建一个临时 Kind 集群,把带有 environment=production 标签的命名空间作为生产上下文,并通过 API Server 验证完整的 Kyverno 准入行为。
运行前需要安装 Docker、Kind、Helm 和 kubectl。
kind create cluster --name kyverno-context
helm repo add kyverno https://kyverno.github.io/kyverno/
helm repo update
helm install kyverno kyverno/kyverno \
--namespace kyverno \
--create-namespace \
--wait
kubectl create namespace production-demo
kubectl label namespace production-demo environment=production
kubectl create namespace staging-demo
kubectl label namespace staging-demo environment=staging
接下来创建一条策略:它读取目标命名空间的标签,仅在生产命名空间中拒绝使用 latest 标签的容器镜像。
cat <<'YAML' | kubectl apply -f -
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-latest-in-production
spec:
background: false
validationFailureAction: Enforce
rules:
- name: block-latest-tag
match:
any:
- resources:
kinds:
- Deployment
context:
- name: namespaceLabels
apiCall:
urlPath: '/api/v1/namespaces/{{request.namespace}}'
jmesPath: 'metadata.labels'
preconditions:
all:
- key: '{{ namespaceLabels.environment || '' }}'
operator: Equals
value: production
validate:
message: 'Production workloads must not use the latest image tag.'
foreach:
- list: 'request.object.spec.template.spec.containers'
deny:
conditions:
any:
- key: '{{ element.image }}'
operator: Equals
value: '*:latest'
YAML
kubectl get clusterpolicy disallow-latest-in-production
这里设置 background: false,因为示例规则依赖 request.namespace,重点是测试实时准入请求,而不是后台扫描。
用服务端 dry-run 触发 API Server 和准入 Webhook,同时避免真的创建工作负载:
kubectl create deployment latest-in-production \
--namespace production-demo \
--image=nginx:latest \
--dry-run=server \
-o yaml
该请求应被拒绝。相同镜像放到 staging 命名空间时,生产前置条件不成立,因此可以通过:
kubectl create deployment latest-in-staging \
--namespace staging-demo \
--image=nginx:latest \
--dry-run=server \
-o yaml
再用固定版本验证生产环境的正向路径:
kubectl create deployment pinned-in-production \
--namespace production-demo \
--image=nginx:1.27.4 \
--dry-run=server \
-o yaml
完成实验后可以直接销毁隔离集群:
kind delete cluster --name kyverno-context
为什么要经过 API Server
只对 YAML 做静态匹配,无法覆盖真实准入路径中的所有变量。经过 API Server 的服务端 dry-run 至少能够验证:
- Webhook 是否正确注册并可达
- Kyverno 是否拥有读取命名空间的权限
- API 默认值和对象结构是否符合规则预期
request.namespace等准入字段是否存在Enforce模式是否真的阻断请求
这类测试仍然不是生产环境的完整复制。临时集群通常缺少生产中的其他 Webhook、身份系统、CRD、网络策略和超时配置。多个变更型 Webhook 还可能改变最终送到校验规则的对象,因此企业环境需要测试 Webhook 的组合行为,而不只是单条 Kyverno 规则。
上线前要守住的边界
采用这类策略时,可以用下面的清单收尾:
- 明确生产信号的唯一来源,并限制谁能修改它
- 同时测试拒绝路径、允许路径和缺少标签时的行为
- 使用服务端 dry-run 或临时集群覆盖真实准入链路
- 检查 Kyverno Webhook 的 failure policy、超时和高可用配置
- 对依赖
request.*的规则区分实时准入与后台扫描语义 - 使用固定镜像版本或 digest,避免测试结果随上游标签变化
- 在启用
Enforce前观察现有资源和流水线会命中多少违规项
让策略引擎“以为自己在生产”真正有价值的地方,不是欺骗软件,而是把隐藏的生产假设变成可创建、可审查、可销毁的测试夹具。只有这些上下文也进入版本控制,策略代码才具备稳定的回归验证能力。