Kubernetes 策略 enforcement 为什么总是太晚——以及如何把防线前移

2026-05-25 24 预计阅读时间:1 分钟
来源:cncf.io AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:8 分钟

Kubernetes 的 Admission Controller 机制让集群在资源创建时拦截不合规的请求,看起来是一道坚固的门。但问题在于:这扇门开在部署那一刻。开发者写完 YAML、提交代码、跑完 CI、准备上线——然后被 Admission Webhook 一巴掌拍回来。反馈链条太长,代价太高。防线必须前移。

"太晚"到底意味着什么

一个典型场景:开发者提交了一个缺少 resources.limits 的 Deployment,CI 流水线全部通过,镜像构建正常,推到集群时被 Gatekeeper 或 Kyverno 的 Admission Webhook 拒绝。此时开发者需要:

  • 回去看 Policy 报错信息
  • 修改 YAML
  • 重新走一遍 CI
  • 重新部署

如果集群策略频繁变更,这种"写完再拦"的模式会让开发团队反复撞墙。更糟糕的是,Admission Controller 只管"进门口",已经存在于集群中的资源如果策略更新了,它们并不会被自动清理——除非你额外跑审计扫描。

核心矛盾:Kubernetes 的灵活性鼓励快速迭代,但策略 enforcement 的位置却在迭代链条的末端。

策略应该在哪一层生效

把策略想象成安全带,最好在车启动前就系好,而不是在碰撞瞬间自动弹出。合理的分层是:

层级 工具 时机 代价
IDE / 编辑器 IDE lint 插件 写代码时 秒级反馈
Pre-commit conftest / kyverno CLI git commit 前 秒级反馈
CI Pipeline conftest / kubeconform / kyverno CLI PR 合入前 分钟级反馈
Admission Gatekeeper / Kyverno webhook 资源到达集群时 分钟级反馈,需重走流程
Runtime Audit Kyverno audit mode / Gatekeeper audit 资源已在集群 小时级发现,需手动修复

越靠左,反馈越快,修复成本越低。Admission Controller 不应该被取消——它是最后一道硬防线——但不应是唯一防线。

用 conftest 在 CI 中提前拦截

conftest 是基于 Open Policy Agent (OPA) Rego 语言的 YAML/JSON 策略测试工具,可以在 CI 中对 Kubernetes manifests 做策略校验,完全不依赖集群。

先写一条策略——禁止 Deployment 缺少 resource limits:

// policies/deployment_resources.rego
package main

deny[msg] {
  kind := input.kind
  kind == "Deployment"
  container := input.spec.template.spec.containers[_]
  not has_resource_limits(container)
  msg := sprintf("Deployment '%s': container '%s' missing resources.limits", [input.metadata.name, container.name])
}

has_resource_limits(container) {
  container.resources.limits
}

然后在 CI 中对 manifest 目录跑校验:

# 安装 conftest(macOS)
brew install conftest

# 对所有 YAML manifest 执行策略检查
conftest test k8s/ --policy policies/

假设 k8s/deployment.yaml 内容如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  template:
    spec:
      containers:
        - name: app
          image: my-app:latest
          # 没有 resources.limits —— 应被拦截

运行结果:

FAIL - k8s/deployment.yaml - Deployment 'my-app': container 'app' missing resources.limits

CI 中可以这样集成(GitHub Actions 示例):

# .github/workflows/policy-check.yml
name: Policy Check
on: [pull_request]
jobs:
  conftest:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install conftest
        run: |
          curl -LO https://github.com/open-policy-agent/conftest/releases/download/v0.55.0/conftest_0.55.0_Linux_x86_64.tar.gz
          tar -xzf conftest_0.55.0_Linux_x86_64.tar.gz
          sudo mv conftest /usr/local/bin/
      - name: Run policy tests
        run: conftest test k8s/ --policy policies/ --no-fail-on-empty

这样 PR 在合入前就会被策略拦截,开发者几分钟内就能收到反馈,而不是等到部署阶段。

用 Kyverno CLI 做 pre-commit 检查

如果你已经在集群中用 Kyverno 做 Admission enforcement,那最自然的前移方式是用 Kyverno CLI 在本地复用同一套策略,避免维护两套规则。

先写一条 Kyverno 策略(集群中也在用的同一份):

# policies/require-resources.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-resource-limits
spec:
  validationFailureAction: Enforce
  rules:
    - name: check-resource-limits
      match:
        any:
          - resources:
              kinds:
                - Deployment
      validate:
        message: "Containers must have resource limits."
        pattern:
          spec:
            template:
              spec:
                containers:
                  - resources:
                      limits:
                        memory: "?*"
                        cpu: "?*"

本地用 Kyverno CLI 校验:

# 安装 kyverno CLI
brew tap kyverno/tap
brew install kyverno/tap/kyverno

# 对 manifest 执行策略验证
kyverno apply policies/ --resource k8s/deployment.yaml

输出类似:

Policy require-resource-limits -> Rule check-resource-limits failed on resource my-app/Deployment
  Containers must have resource limits.

把它挂到 git pre-commit hook,写代码时就能拦住:

# .git/hooks/pre-commit(或用 husky/pre-commit 管理)
#!/bin/bash
echo "Running Kyverno policy check..."
kyverno apply policies/ --resource k8s/
if [ $? -ne 0 ]; then
  echo "Policy check failed. Fix your manifests before committing."
  exit 1
fi

关键收益:集群策略和本地策略是同一份 YAML,零额外维护成本。

Admission 层仍然不可省略

前移策略并不意味着去掉 Admission Controller。它的价值在于:

  • 兜底:绕过 CI 的紧急操作、手动 kubectl apply、外部系统推送的资源,仍然会被拦截
  • 硬边界:CI 策略可以被开发者绕过(跳过 CI 直接 push),Admission 是集群级强制
  • 审计:Gatekeeper 和 Kyverno 都有 audit 模式,能扫描存量资源是否合规

推荐的做法是 CI 和 Admission 用同一套策略定义,只是 enforcement 位置不同:

  • Kyverno:集群策略 YAML 直接被 Kyverno CLI 在本地复用
  • Gatekeeper:约束模板(ConstraintTemplate + Constraint)可以用 gator CLI 在本地验证
# Gatekeeper gator CLI 本地验证
gator test --policies=gatekeeper-policies/ --files=k8s/

前移策略的落地路径

把策略 enforcement 前移不是一步到位的事,可以按这个节奏推进:

  1. 盘点现有策略:把集群中 Gatekeeper/Kyverno 的策略导出,整理成统一目录
  2. 选一个 CLI 工具对齐:用 Kyverno 就选 kyverno CLI,用 Gatekeeper 就选 gator,用 OPA 就选 conftest——不要混用
  3. 先挂 CI:在 PR 流水线加一步策略检查,fail 时阻止合入,这是投入最小收益最大的第一步
  4. 再加 pre-commit:开发者本地跑同样的检查,反馈从分钟级变成秒级
  5. Admission 保留为兜底:策略不变,validationFailureAction 保持 Enforce
  6. 定期 audit:每周跑一次 Kyverno audit 或 Gatekeeper audit 报告,清理存量不合规资源

需要注意的代价:

  • 策略文件需要版本管理,和 manifest 放在同一个仓库或独立策略仓库
  • Rego 策略(OPA/conftest)和 Kyverno YAML 策略语法不同,选一条路走下去,避免双倍维护
  • pre-commit hook 会增加 commit 时间,策略数量多时考虑只检查变更文件而非全目录

策略 enforcement 不该是部署时才响起的警报,而应该从写代码的那一刻就开始工作。把防线推到开发者触手可及的地方,才是 Kubernetes 策略真正生效的位置。


相关推荐