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)可以用
gatorCLI 在本地验证
# Gatekeeper gator CLI 本地验证
gator test --policies=gatekeeper-policies/ --files=k8s/
前移策略的落地路径
把策略 enforcement 前移不是一步到位的事,可以按这个节奏推进:
- 盘点现有策略:把集群中 Gatekeeper/Kyverno 的策略导出,整理成统一目录
- 选一个 CLI 工具对齐:用 Kyverno 就选 kyverno CLI,用 Gatekeeper 就选 gator,用 OPA 就选 conftest——不要混用
- 先挂 CI:在 PR 流水线加一步策略检查,fail 时阻止合入,这是投入最小收益最大的第一步
- 再加 pre-commit:开发者本地跑同样的检查,反馈从分钟级变成秒级
- Admission 保留为兜底:策略不变,
validationFailureAction保持Enforce - 定期 audit:每周跑一次 Kyverno audit 或 Gatekeeper audit 报告,清理存量不合规资源
需要注意的代价:
- 策略文件需要版本管理,和 manifest 放在同一个仓库或独立策略仓库
- Rego 策略(OPA/conftest)和 Kyverno YAML 策略语法不同,选一条路走下去,避免双倍维护
- pre-commit hook 会增加 commit 时间,策略数量多时考虑只检查变更文件而非全目录
策略 enforcement 不该是部署时才响起的警报,而应该从写代码的那一刻就开始工作。把防线推到开发者触手可及的地方,才是 Kubernetes 策略真正生效的位置。