Skip to main content

Command Palette

Search for a command to run...

Kyverno 和 Gatekeeper 的简单对比

Updated
3 min read

引言

在生产环境中应用 Kubernetes 时,出于安全、合规等管控目的,经常需要对工作负载进行审计、校验以及变更,例如下列场景:

  1. 为了便于在监控和日志中识别特定应用,我们希望业务应用的 Pod 具备合适的标签结构,标识本 Pod 的业务角色。

  2. 根据职责分离的设计原则,只允许 admin 用户在形如 xxx-system 的命名空间创建工作负载。

  3. 为了防范供应链攻击,限制仅有来自特定仓库的容器镜像才能运行,并且镜像拉取策略必须设置为 Always。

  4. 恶意用户或者应用可以通过加载主机目录窃取消息或者实施破坏,因此应该限制这种加载行为。

  5. ...

Kubernetes 提供的 Validating 和 Mutation Webhook 能够完成这些任务,如图所示:

策略引擎们更进一步,使用 DSL 来编写 Webhook 的逻辑,目前使用最为广泛的策略引擎大概是 Kyverno 和 Gatekeeper 两个产品了,因为都是面向 Kubernetes 工作的,通过 CRD 和 Webhook 技术达成目的,因此两个产品的功能和思路颇有一些相似之处,本文将对这两个产品进行一个对比,便于读者们根据实际需求进行采用。

实际上在本文之前,就已经翻译过一篇两个产品的对比,因为版本更迭,其中部分结果已经发生变化。

项目简介

Kyverno 从 2019 年 5 月 24 日在 Github 发布第一个 Release 之后,于 2020 年 11 月 19 日进入 CNCF 版图成为沙箱项目。一年半的时间里,发布了 98 个 Release,收获 1.7k Star,先后有 88 个贡献者参与了该项目的建设之中。目前其策略库中包含了 100 条策略,Kyverno 官方的架构图如下:

从上图可以看到,Kyverno 的 Webhook 组件通过 Mutating 和 Validating 两个接口和 Kubernetes 进行交互,用于处理 Kubernetes API Server 的 AdmissionReview 请求。Policy 控制器负责检视集群中的策略对象;而 GenerateControl 实现了 Kyverno 的一个亮点功能,根据策略针对部分事件生成特定资源。

Gatekeeper 的情况比较复杂,它是在 OPA 项目基础上的一个实现,所以我们经常会看到 OPA/GateKeeper 这样的说法。OPA 的全称是 Open Policy Agent 是一个开源的通用策略引擎,在 2018 年进入 CNCF 沙箱,2021 年 1 月毕业。目前 OPA 的版本为 v0.36.0,它的架构图如下

这里不难看出,OPA 的工作流程是也是很贴近 Kubernetes 的 AdmissionReview 流程的。Gatekeeper 在 OPA 的基础之上,根据 Kubernetes 生态的习惯做法,定制了多种 CRD,并融入到 Admission Controller 的体系之中。Gatekeeper V3 的架构体现了这一思路:

上图中 Gatekeeper 将 OPA 封装起来,通过 CRD 和 Admission Controller 的交互,实现了策略的自动化管理。Gatekeeper 的策略库中也包含了几十条实用策略。

相似的架构和工作原理,决定了两者的功能也是有颇多的相似之处的,下面将从作用范围选择、策略编写、审计和可见、独特功能几个方向分别对两者进行介绍。

作用范围

Kyverno 提供了 match 和 exclude 两个关键字,用于指定策略的作用范围。两者结合能够相对精确地对 API Server 的请求进行过滤,确定是否启用策略。Kyverno 的选择条件包括四个分类,分别是:

  • resources: 通过标签、命名空间、名称、注解、API 类型等进行选择;

  • subjects: 通过发起动作的用户、用户组和 ServiceAccount 进行选择;

  • roles 和 clusterroles:发起动作的用户角色以及集群角色。

多个条件同时作用时候,可以使用 any 和 all 关键字,分别用于指定 OR 或 AND 的逻辑关系。例如下面的规则:

spec:
  validationFailureAction: enforce
  background: false
  rules:
    - name: match-criticals-except-given-users
      match:
        all:
        - resources:
            kinds:
            - Pod
            selector:
              matchLabels:
                app: critical
      exclude:
        any:
        - subjects:
          - kind: User
            name: susan
          - kind: User
            name: dave

这个选择器匹配 app=critical 的 Pod,但是排除了 susandave 的用户。

再看 Gatekeeper 中对目标对象的选择,它的 spec.match 字段支持以下几种选择:

  • kinds:一个数组型字段,元素为 apiGroup 和 kind 的组合;

  • scope:用于定义作用范围,可以是 Namespaced, Cluster*

  • namespaces:一个数组型字段,元素为命名空间名称;可以支持尾部通配符,例如 kube-*

  • excludedNamespaces:数组字段,需要排除的命名空间,同样支持尾部通配符;

  • labelSelector:其中包含 matchLabelsmatchExpressions 两个字段,分别用于指定标签选择器和表达式选择器;

  • namespaceSelector:标签选择器,用于选择命名空间;

  • name:支持尾部通配符,用于过滤对象名称。

对比看来,Gatekeeper 的字段类型更丰富一些,但是 Kyverno 的优势是加入了针对特定动作发起者直接进行限制,Gatekeeper 也不是没有这种能力,它需要在策略中直接实现针对这些内容的过滤,所以二者的能力类似,但侧重点、便利程度是有所不同的。

策略编写

Kyverno 和 Gatekeeper 的策略编写方式是迥然不同的,例如一个简单的标签验证策略,在 Kyverno 中的实现:

  pattern:
    spec:
      containers:
      - name: "*"
        resources:
          limits:
            memory: "?*"
            cpu: "?*"
          requests:
            memory: "?*"
            cpu: "?*"

而在 Gatekeeper 中就相当不同了:

package limit
resources_defined(x) {
  x.resources; x.resources.limits; x.resources.requests
}
violation[{"msg": msg}] {
  ctr_list = input.review.object.spec.template.spec.containers
  some i
  ctr = ctr_list[i]
  not resources_defined(ctr)
  msg = sprintf("%v containers without 'resource' fields", [ctr.name])
}

Rego 语言给 OPA/Gatekeeper 带来强大能力的同时,也带来一定的学习成本,而 Kyverno 的最复杂语法(YAML)元素大概是:

  • Anchors:用于表述 if-then-else 语义,“如果存在 hostPath,则其内容不能是 /var/lib”:

      =(hostPath):
          path: “!/var/lib”
    
  • foreach:用于对一个列表中的多个元素定义多种策略,例如 Pod 中的 Containers 数组。

虽然简单,但是用主要用于描述结构内容的 YAML 来表达逻辑,还是颇有些古怪的;Rego 加持的 OPA/Gatekeeper 就不会有这种烦恼了——它的一个困扰是,每种 Constraint 都是一种 CRD,当然,这种 CRD 非常灵活,能够很好地表达 Contraint 的参数化能力。

再说 Mutation,Kyverno 是使用 RFC 6902 JSONPatchStrategic Merge Patch 两种方式来进行变更的,例如对 Configmap 对象的变更:

patchesJson6902: |-
  - path: "/data"
    op: add
    value: {"ship.properties": "{\"type\": \"starship\", \"owner\": \"utany.corp\"}"}

这只是个最简单的例子,Keyverno 的 Mutation 相关语法难度远大于验证语法。

而 OPA/Gatekeeper 的这个功能就让我有些意外了,它特别定义了三种 CRD,分别是:

  • AssignMetadata:用于对资源元数据进行变更

  • Assign:对元数据之外的内容进行变更

  • ModifySet:从列表中加入或者移除内容

例如要加入一个注解:

apiVersion: mutations.gatekeeper.sh/v1beta1
kind: AssignMetadata
metadata:
  name: demo-annotation-owner
spec:
  match:
    scope: Namespaced
  location: "metadata.annotations.owner"
  parameters:
    assign:
      value: "admin"

两个产品都提供了对策略进行测试的能力,不过 Kyverno 很遗憾地没有提供对 arm64 的支持。

审计和可观测性

Kyverno 提供了一个单独的可观测性服务,可以用 Prometheus 抓取如下内容:

  • 策略和规则的数量

  • 策略和规则的执行结果

  • 策略执行的延迟

  • 请求数量

  • 策略变更数量

另外 Kyverno 还提供了两个用于生成报告的 CRD:PolicyReportClusterPolicyReport,例如:

$ kubectl get polr -A
NAMESPACE     NAME                  PASS   FAIL   WARN   ERROR   SKIP   AGE
default       polr-ns-default       338    2      0      0       0      28h
flux-system   polr-ns-flux-system   135    5      0      0       0      28h
$ kubectl describe polr polr-ns-default | grep "Result: \+fail" -B10
  Message:        validation error: Running as root is not allowed. The fields spec.securityContext.runAsNonRoot, spec.containers[*].securityContext.runAsNonRoot, and spec.initContainers[*].securityContext.runAsNonRoot must be `true`. Rule check-containers[0] failed at path /spec/securityContext/runAsNonRoot/. Rule check-containers[1] failed at path /spec/containers/0/securityContext/.
  Policy:         require-run-as-non-root
  Resources:
    API Version:  v1
    Kind:         Pod
    Name:         add-capabilities-init-containers
    Namespace:    default
    UID:          1caec743-faed-4d5a-90f7-5f4630febd58
  Rule:           check-containers
  Scored:         true
  Result:         fail
--
  Message:        validation error: Running as root is...

Gatekeeper 提供的指标包括:

  • Constraint:Constraints 的数量

  • Constraint Template:包括这一类对象的数量、处理延迟

  • Webhook:请求数量、响应时间、Mutation 请求数量、Mutation 响应时间

  • Audit:触发次数、审计延迟、审计运行时间戳

  • 同步:缓存对象数量、同步延迟、最后同步时间戳

  • Watch:Watch 的对象种类数量

Kyverno 策略中提供了 validationFailureAction 字段,用于定义策略失败后的动作,可选动作包括 audit 和 enforce。

Gatekeeper 策略中包含 DryRun、Warn 和 Deny 三种应对措施。

独特功能

Kyverno 在功能方面有两个有趣的东西:

  • 创建对象:可以根据策略,在特定条件下触发对象的创建,例如随 Service 创建 Ingress 之类;

  • 校验镜像签名:可以使用 cosign 对镜像签名进行校验,保障供应链安全。

太长不看

OPA 是 CNCF 的老牌项目,Kyverno 属于后起之秀,在策略编写方面,Kyverno 的表达方式更合乎 YAML 工程师们的做事风格;而 Rego 语言虽然语法还是很奇怪,然而一旦习惯了这种设定,强大的实现能力还是要优于 YAML 语法糖的。

Mutation 方面,Gatekeeper 支持的比较晚,也非常生硬。Kyverno 的高可用和监控能力则是比较晚才出现。

More from this blog

龙虾恐慌:AIOps 又要改名了?

ChatGPT 开始,把 AI 拉近到普罗大众的面前,让无数人感受到 AI 的亲民魅力。而龙虾,则把大模型驱动的自动化能力,突然间变得水灵灵、活泼泼地走进千家万户。它不只是“风口上的猪”,而是风口本身。热度高到让 Mac mini 一度断货,不知道这在不在库克的预料之内。 每代人都有每代人的鸡蛋,春节期间,我就领了我的鸡蛋。翻出古老的 MacBook Air M1,充值各种大模型。当然了,这个工具

Mar 9, 20261 min read

再见 2025

我猜不少人以为这个号废了吧?并没有,只是今年变化有点大,一直有种抄起键盘,无从说起的感觉,所以一直偷懒到今天,2025 的最后一天。 今年是我的第四个本命年,去年末一期播客里,大内说本命年不是灾年,是变化年,有危也有机。可是讲真啊,只看到危,没看到机。 各种因缘际会,从鹅厂跳槽到前东家,已经接近四年,第一个合同期已经进入尾声。除了前两年还在云原生领域嗷嗷叫,后两年基本都是些鸡零狗碎的东西了,用老东家的术语说是——偏离主航道,可谓是前景暗淡了。 一旦确定要滚蛋,反倒心思轻松起来,每天骑着我的小红车...

Jan 5, 20261 min read

辅助编程?dora 说:我知道你很急可是请你别急

从 OpenGPT 把大模型的火烧旺了之后,这三年来,相信很多组织或摩拳擦掌、或躬身入局,希望借助聪明能干的大模型,或想偿还技术宅,或想降本增效,或想弯道超车。一时间,沉寂许久的 AIxx 又活过来了,LLM Ops、Vibe Coding、中医大模型、GPT 算命等等,全都老树发新芽,焕发了勃勃生机。那么视角拉回从业者最关注的饭碗相关的领域之一——AI 辅助开发,产生了什么触动,应该如何拥抱呢? DORA 的年度报告中给出了很有意思的结论——强者恒强。 执行摘要部分总结了几个有趣的点: 问题...

Oct 6, 20251 min read

[译]dora:ai 辅助软件开发状态报告

执行摘要 在 2025 年,科技领导者面临的核心问题已不再是“是否要采用 AI”,而是“如何实现其价值”。 DORA 的研究基于超过 100 小时的定性访谈和来自全球近 5,000 名技术专业人士的问卷调查。研究揭示了一个关键事实:AI 在软件开发中的主要角色是“放大器”。它会放大高效能组织的优势,也会凸显组织的缺陷。 关键结论:AI 是放大器 AI 投资的最大回报并非来自工具本身,而是来自组织底层系统的战略性建设: 高质量的内部平台 清晰的工作流 团队的协同能力 缺少这些基础,AI ...

Oct 2, 202514 min read

僭越了,有人在用 Rust 写 Kubernetes

一个新语言问世,最爱做的事情之一,就是重写存量软件了。 云原生喝酒 SIG 重点扶持项目——rk8s(https://github.com/rk8s-dev/rk8s) 也可以归在这个范畴里,只不过这个项目重写的东西比较大,是 Kubernetes。 从 2025 年 1 月第一个 Commit 开始,到现在有了 200 多次 Commit,十几万行代码。当然距离 Kubernetes 的几百万行代码还差得远——老马就是喜欢整这种大无畏项目。 另外该项目也是国内第一个脱离 Cargo 转向使用 ...

Sep 27, 20253 min read

【伪】架构师

342 posts