跳过正文
Featured image for OPA/Kyverno:K8s 准入控制策略实战

OPA/Kyverno:K8s 准入控制策略实战

·895 字·5 分钟·
目录
K8s 完全指南 - 这篇文章属于一个选集。
§ : 本文

为什么需要准入控制
#

K8s 默认情况下,只要有 kubectl apply 权限,几乎可以创建任意资源:没有 resources.limits 的 Pod、使用 latest 镜像的 Deployment、从任意仓库拉取的镜像……这些问题不会在运行时立刻暴露,但会在某个凌晨两点变成你的噩梦。

我经历过几个典型事故:

  • 某个测试 Pod 没有设置内存限制,OOM 之后 K8s 开始驱逐同节点上的生产 Pod
  • 开发推送了一个 image: myapp:latest,部署时实际拉取了一个三个月前的旧镜像(Registry 的缓存问题)
  • 一批 Pod 没有 team 标签,出了问题根本不知道是哪个团队的服务

这些问题的根源不是开发者的能力,而是缺乏在资源创建时的约束机制。K8s 的 Admission Webhook 体系就是为此而生的。

Admission 控制流程
#

kubectl apply
    │
    ▼
API Server 认证/鉴权
    │
    ▼
Mutating Admission Webhooks(可修改请求)
    │
    ▼
Schema Validation(CRD/OpenAPI)
    │
    ▼
Validating Admission Webhooks(只读验证)
    │
    ▼
资源写入 etcd

OPA Gatekeeper 和 Kyverno 都是通过实现 Validating/Mutating Webhook 来工作的。


Kyverno vs OPA Gatekeeper
#

简单来说,如果你没有 Rego 经验,选 Kyverno;如果你的团队已经在用 OPA,选 Gatekeeper。

维度 Kyverno OPA Gatekeeper
策略语言 Kubernetes 原生 YAML + JMESPath Rego(专用语言)
学习曲线
Mutation 支持 原生支持 需要额外配置
生态策略库 Kyverno Policies 官方库 OPA Library
审计模式 支持 支持
报告能力 PolicyReport(CRD) Audit Controller
社区活跃度 CNCF 孵化项目,活跃 CNCF 毕业项目,稳定

我在大多数新集群选 Kyverno,主要原因是策略即 YAML,团队成员不需要额外学习 Rego,降低了策略维护的门槛。


Kyverno 安装
#

# Helm 安装(推荐)
helm repo add kyverno https://kyverno.github.io/kyverno/
helm repo update

kubectl create ns kyverno

helm install kyverno kyverno/kyverno \
  --namespace kyverno \
  --set replicaCount=3 \    # 生产环境至少3副本
  --version 3.1.4

验证:

kubectl get pods -n kyverno
# kyverno-admission-controller-xxx   1/1   Running
# kyverno-background-controller-xxx  1/1   Running
# kyverno-cleanup-controller-xxx     1/1   Running
# kyverno-reports-controller-xxx     1/1   Running

重要提醒:生产集群安装 Kyverno 之前,先以 audit 模式运行1-2周,观察哪些现有资源会违规,再切换到 enforce 模式。直接 enforce 会导致已有 CD 流程失败。


Policy vs ClusterPolicy
#

  • Policy:命名空间级别,只影响当前 namespace
  • ClusterPolicy:集群级别,影响所有 namespace(可以用 exclude 排除)
# 命名空间级别策略(只影响 team-a namespace)
apiVersion: kyverno.io/v1
kind: Policy
metadata:
  name: require-labels
  namespace: team-a

---
# 集群级别策略(影响全集群,但排除 kube-system)
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-labels-global
spec:
  rules:
    - name: check-labels
      exclude:
        any:
          - resources:
              namespaces:
                - kube-system
                - kyverno
                - cert-manager

常用策略实战
#

策略1:强制资源限制
#

这是最基础也最重要的策略。没有 limits 的 Pod 是潜在的资源炸弹。

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-resource-limits
  annotations:
    policies.kyverno.io/title: "强制资源限制"
    policies.kyverno.io/description: "所有容器必须设置 CPU 和内存的 requests 与 limits"
spec:
  validationFailureAction: Enforce   # Audit(只记录)或 Enforce(拒绝)
  background: true                   # 对已有资源做审计
  rules:
    - name: check-resource-limits
      match:
        any:
          - resources:
              kinds:
                - Pod
      exclude:
        any:
          - resources:
              namespaces:
                - kube-system
                - monitoring
      validate:
        message: "容器 '{{ request.object.spec.containers[].name }}' 必须设置 resources.limits.cpu 和 resources.limits.memory"
        pattern:
          spec:
            containers:
              - resources:
                  limits:
                    memory: "?*"
                    cpu: "?*"
                  requests:
                    memory: "?*"
                    cpu: "?*"

策略2:禁止 latest 镜像标签
#

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: disallow-latest-tag
spec:
  validationFailureAction: Enforce
  background: true
  rules:
    - name: require-image-tag
      match:
        any:
          - resources:
              kinds: [Pod]
      validate:
        message: "镜像必须使用明确的版本标签,禁止使用 :latest 或不带标签"
        foreach:
          - list: "request.object.spec.containers"
            deny:
              conditions:
                any:
                  # 镜像名以 :latest 结尾
                  - key: "{{ element.image }}"
                    operator: Equals
                    value: "*:latest"
                  # 镜像名不包含冒号(没有标签)
                  - key: "{{ element.image }}"
                    operator: NotContains
                    value: ":"

策略3:强制标签规范
#

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-standard-labels
spec:
  validationFailureAction: Enforce
  background: true
  rules:
    - name: check-deployment-labels
      match:
        any:
          - resources:
              kinds: [Deployment, StatefulSet, DaemonSet]
      validate:
        message: "工作负载必须包含 app、team、version 标签"
        pattern:
          metadata:
            labels:
              app: "?*"
              team: "?*"
              version: "?*"
    - name: check-pod-labels
      match:
        any:
          - resources:
              kinds: [Pod]
      validate:
        message: "Pod 必须包含 app 和 team 标签"
        pattern:
          metadata:
            labels:
              app: "?*"
              team: "?*"

策略4:镜像来源白名单
#

只允许从指定私有仓库拉取镜像,防止供应链攻击。

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: restrict-image-registries
spec:
  validationFailureAction: Enforce
  background: true
  rules:
    - name: validate-registries
      match:
        any:
          - resources:
              kinds: [Pod]
      exclude:
        any:
          - resources:
              namespaces: [kube-system, kyverno]
      validate:
        message: "镜像只能来自授权仓库:registry.example.com 或 mirror.example.com"
        foreach:
          - list: "request.object.spec.containers"
            deny:
              conditions:
                all:
                  - key: "{{ element.image }}"
                    operator: NotStartsWith
                    value: "registry.example.com/"
                  - key: "{{ element.image }}"
                    operator: NotStartsWith
                    value: "mirror.example.com/"

策略5:Mutation——自动注入标签
#

Mutation 策略可以在资源创建时自动修改,减少开发者的认知负担。

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: add-default-labels
spec:
  rules:
    - name: add-environment-label
      match:
        any:
          - resources:
              kinds: [Pod]
      mutate:
        patchStrategicMerge:
          metadata:
            labels:
              # 如果没有 environment 标签,自动注入
              +(environment): "production"

+() 语法表示"仅在不存在时添加",不会覆盖已有标签。

策略6:强制 PodDisruptionBudget
#

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-pdb
spec:
  validationFailureAction: Audit   # 先 Audit,观察一段时间
  background: true
  rules:
    - name: check-pdb-exists
      match:
        any:
          - resources:
              kinds: [Deployment]
              operations: [CREATE, UPDATE]
      preconditions:
        all:
          - key: "{{ request.object.spec.replicas }}"
            operator: GreaterThan
            value: 1
      validate:
        message: "副本数 > 1 的 Deployment 必须配置 PodDisruptionBudget"
        deny:
          conditions:
            all:
              - key: "{{ request.object.metadata.name }}"
                operator: Equals
                value: "{{ request.object.metadata.name }}"

这个策略实际上需要跨资源验证(检查同名 PDB 是否存在),完整实现建议用 Kyverno 的 foreach + context.apiCall 特性查询集群状态。


审计模式 vs 强制模式
#

这两种模式决定了违规时的行为:

spec:
  validationFailureAction: Audit    # 只记录,不拒绝
  # 或
  validationFailureAction: Enforce  # 拒绝创建/更新

推荐的上线流程

1. Audit 模式上线
      ↓
2. 观察 PolicyReport(1-2周)
      ↓
3. 修复存量违规资源
      ↓
4. 切换为 Enforce 模式
      ↓
5. 通知所有开发团队

查看审计报告:

# 查看集群级别违规报告
kubectl get clusterpolicyreport

# 查看详情
kubectl get clusterpolicyreport -o jsonpath='{.items[*].results[?(@.result=="fail")]}'  | jq .

# 命名空间级别
kubectl get policyreport -n production -o yaml

OPA Gatekeeper 对比实践
#

如果你需要更复杂的策略逻辑(例如跨资源联动、复杂条件计算),OPA Gatekeeper 更有优势。

安装:

kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/v3.14.0/deploy/gatekeeper.yaml

Gatekeeper 的策略分两层:

  1. ConstraintTemplate:定义约束的 Rego 逻辑
  2. Constraint(具体类型):实例化约束、配置参数
# 1. 定义模板
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: k8srequiredlabels
spec:
  crd:
    spec:
      names:
        kind: K8sRequiredLabels
      validation:
        openAPIV3Schema:
          type: object
          properties:
            labels:
              type: array
              items:
                type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8srequiredlabels

        violation[{"msg": msg}] {
          provided := {label | input.review.object.metadata.labels[label]}
          required := {label | label := input.parameters.labels[_]}
          missing := required - provided
          count(missing) > 0
          msg := sprintf("缺少必需标签: %v", [missing])
        }

---
# 2. 实例化约束
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
  name: require-team-label
spec:
  enforcementAction: deny   # 或 warn、dryrun
  match:
    kinds:
      - apiGroups: ["apps"]
        kinds: ["Deployment"]
  parameters:
    labels: ["app", "team", "version"]

Rego 的优势在于可以写复杂逻辑,缺点是语法陌生,调试也比较麻烦。建议用 OPA Playground 在线测试策略逻辑。


踩坑记录
#

坑1:Kyverno 高可用配置

单副本 Kyverno 在重启时,Webhook 超时会导致所有 Pod 创建请求被拒绝(FailurePolicy: Fail)。生产环境必须 3 副本 + 反亲和。

# Kyverno Helm values
replicaCount: 3
podAntiAffinity:
  requiredDuringSchedulingIgnoredDuringExecution:
    - labelSelector:
        matchLabels:
          app.kubernetes.io/name: kyverno
      topologyKey: kubernetes.io/hostname

坑2:Webhook 超时导致 CD 失败

Kyverno 默认 Webhook 超时是 10s。如果集群负载高或 Kyverno Pod 资源紧张,校验请求可能超时,导致 CD 流水线莫名其妙地失败。监控 Kyverno 的 kyverno_admission_requests_total 指标,以及 kyverno_policy_execution_duration_seconds

坑3:背景扫描(Background Scan)的影响

background: true 会让 Kyverno 定期扫描集群中已有的资源并生成 PolicyReport。在大集群(几千个 Pod)中,这会消耗不少 CPU。建议在 values 中调整:

backgroundController:
  resources:
    requests:
      cpu: 200m
      memory: 256Mi
    limits:
      cpu: 1000m
      memory: 1Gi

坑4:排除系统命名空间

一定要在 ClusterPolicy 中排除 kube-systemkyvernocert-manager 等基础设施命名空间,否则 DaemonSet/系统组件更新时会被自己的策略卡住。


几条落地建议
#

  1. 从最少侵入的策略开始:先 Audit 模式,先做标签和资源限制
  2. 策略代码化:所有策略存入 Git,走 CI 验证,不要在集群里手动改策略
  3. 定期审查 PolicyReport:设置告警,有违规立刻修复,不要让技术债累积
  4. 与 CD 集成:用 kyverno apply 在 CI 阶段预验证 YAML,提前发现问题

策略不是银弹,它只解决"不知不觉违规"这一类问题。真正让开发不爆炸的还是要配合规范文档和 code review。

Wenzhuo Huang
作者
Wenzhuo Huang
搞运维的工程师,写代码的运维人。专注 Kubernetes、AWS、GitOps 与基础设施可靠性。这个博客既是我的技术笔记本,也是我踩过的坑的受害者档案。
K8s 完全指南 - 这篇文章属于一个选集。
§ : 本文

相关文章

Kubernetes 成本优化实战:系统性降本的四条路径

·1066 字·6 分钟
真实的降本案例:从发现成本异常到分析根因,通过 Karpenter 节点弹性伸缩、资源请求规格治理、大机型收敛等手段,系统性降低 AWS EC2 成本。包含具体配置和执行思路。

Kubernetes NetworkPolicy 网络隔离实战

·2505 字·12 分钟
系统讲解 Kubernetes NetworkPolicy 的工作机制与生产实战配置,覆盖 deny-all 基础模板、常见隔离场景、Cilium 扩展、多租户设计、测试验证方法及常见陷阱。

Helm 工程化实践:从 Chart 设计到多环境管理

·1169 字·6 分钟
基于生产踩坑经验,系统梳理 Helm Chart 结构设计、_helpers.tpl 复用技巧、多环境 values 管理策略、私有 Harbor 仓库推送流程,以及 –atomic 升级与回滚的正确姿势。