让 Kyverno“进入生产环境”:用真实准入上下文验证 Kubernetes 策略

2026-07-22 23 预计阅读时间: 1 分钟
来源: cncf.io AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:7 分钟

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 前观察现有资源和流水线会命中多少违规项

让策略引擎“以为自己在生产”真正有价值的地方,不是欺骗软件,而是把隐藏的生产假设变成可创建、可审查、可销毁的测试夹具。只有这些上下文也进入版本控制,策略代码才具备稳定的回归验证能力。


相关推荐