Skip to main content

Command Palette

Search for a command to run...

使用 Kyverno 定义 Kubernetes 策略

Updated
3 min read

Kubernetes 的日常使用过程中,在对象提交给集群之前,我们会有很多机会,很多方法对资源的 Yaml 定义进行检查和处理。很多读者应该也会知道,资源提交之后,还有机会使用 Admission Controller 对资源动动手脚,这其中其实有很多可以提炼出来的标准动作,可以用统一的控制器来进行处理,Kyverno 就是这样一个工具。有了 Kyverno 的帮助,YAML 程序员可以根据条件对资源进行筛选,符合条件的资源可以:

  • 验证资源:对资源定义进行检查,不符合条件的资源拒绝创建,从而保证集群资源的合规性。

  • 修改资源:在资源定义中进行注入,强制资源部分行为的一致性。

  • 生成资源:在资源创建时,同时创建相关的资源。

安装

安装过程是很简单的,安装清单文件位于 https://github.com/nirmata/kyverno/raw/master/definitions/install.yaml,使用 kubectl 直接部署即可:

$ kubectl create -f https://github.com/nirmata/kyverno/raw/master/definitions/install.yaml
customresourcedefinition.apiextensions.k8s.io/policies.kyverno.io created
namespace/kyverno created
service/kyverno-svc created
serviceaccount/kyverno-service-account created
clusterrolebinding.rbac.authorization.k8s.io/kyverno-admin created
deployment.extensions/kyverno created

有一点需要注意的是资源的类型范围,可以在主进程的命令行参数中设定不需要处理的资源类型,缺省设置为:

  containers:
    - name: kyverno
      image: nirmata/kyverno:latest
      args: ["--filterKind","Node,Event,APIService,Policy,TokenReview,SubjectAccessReview"]
      ports:

策略定义

安装完成后,就可以编写策略了,策略的规则不算复杂,具体格式可以从 install.yaml 中的 CRD 定义里面推断出来。

apiVersion : kyverno.io/v1alpha1
kind: Policy
metadata:
  name: sample-policy
spec:
  rules: # 规则数组,spec 的唯一下级
  - name: check-rule-1
    resource: # 定义选择条件,限制生效范围
      kinds: # 生效对象类型数组,必要字段
      - Deployment
      - StatefulSet
      namespace: default  # 命名空间
      name: "*" # 资源名称
      selector: # 用更加复杂一点的方式来定义选择方式
        matchLabels: # 精确匹配标签
          app: some-app
        matchExpressions: # 表达式匹配标签
          key: "operator"
          operator: In
          values:
          - v2
          - v3
    validate:
      ...
    mutate:
      ...
    generate:
      ...

resource 部分是固定的,而 validate mutate generate 三个动作则各有各的结构。

下面用几个例子来演示一下他的功能。

验证资源(validate)

定义一个限制特定命名空间下镜像地址的策略如下:

apiVersion : kyverno.io/v1alpha1
kind: Policy
metadata:
  name: check-registries
spec:
  rules:
  - name: check-registries
    resource:
      kinds:
      - Deployment
      namespace: default
    validate:
      message: "Registry is not allowed"
      pattern:
        spec:
          template:
            spec:
              containers:
              - name: "*"
                image: "docker.io/citizenstig/*"

这个策略文件中,pattern 部分和我们要处理的 deployment 文档结构一致,其中支持通配符,可以用它来对目标进行校验,这里我们要求 default 命名空间中的 Deplyment 对象,containers 下的 image 字段必须符合 docker.io/citizenstig/* 的通配符要求。

例如下面的的 Deployment 就无法创建:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: httpbin
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: httpbin
        version: v1
    spec:
      containers:
        - name: httpbin
          image: citizenstig/httpbin
          imagePullPolicy: IfNotPresent
$ kubectl apply -f httpbin.yaml
Error from server: error when creating "httpbin.yaml": admission webhook "nirmata.kyverno.validating-webhook" denied the request:
Policy check-registries failed with following rules;rulename: check-registries;Rule check-registries: Validation has failed, err Failed to validate value citizenstig/httpbin with pattern docker.io/citizenstig/*. Path: /spec/template/spec/containers/0/image/.

但是如果我们换个命名空间就没问题了:

$ kubectl create ns free
kunamespace/free created
$ kubectl apply -f httpbin.yaml -n free
deployment.extensions/httpbin created

又或者我们不用 Deployment,直接创建 Pod:

apiVersion: v1
kind: Pod
metadata:
  name: static-httpbin
spec:
  containers:
    - name: httpbin
      image: citizenstig/httpbin

果然就能够创建成功了:

$ kubectl apply -f pod.yaml
pod/static-httpbin created

这样的绕过自然是我们不想要的,但是可以改变策略,把限制做到 Pod 上:

  rules:
  - name: check-registries
    resource:
      kinds:
      - Pod
      namespace: default
    validate:
      message: "Registry is not allowed"
      pattern:
        spec:
          containers:
          - name: "*"
            image: "docker.io/citizenstig/*"

这样更新之后,不管是 Deployment 还是静态 Pod 都无法通过了。

如果使用 kubectl edit deploy httpbinimage 字段修改为 docker.io/citizenstig/httpbin,就能看到 deployment 能够正常工作了。

或者我们可以要求所有 Pod 都必须指定 CPU 限制:

validate:
  message: "resources/limits is needed."
  pattern:
    spec:
      template:
        spec:
          containers:
          - resources:
              limits:
                cpu: "*"

这个策略提交之后,上面的 Deploy 就再次无法部署了:

$ kubectl apply -f httpbin.yaml
Error from server: error when creating "httpbin.yaml": admission webhook "nirmata.kyverno.validating-webhook" denied the request:
...
Path: /spec/template/spec/containers/0/resources/limits/. Expected map[string]interface {}, found <nil>.

修改清单,加入资源限制,即可满足条件。

修改资源(mutate)

这里也可以做类似自动注入的内容,例如我们可以要求所有 default 命名空间中的 Deployment,如果 deployment 标签中有 io=heavy,则分配到 ssd=true 的节点上。

apiVersion : kyverno.io/v1alpha1
kind: Policy
metadata:
  name: assign-ssd
spec:
  rules:
  - name: assign-ssd
    resource:
      kinds:
      - Deployment
      namespace: default
      selector:
        matchLabels:
          io: heavy
    mutate:
      overlay:
        spec:
          template:
            spec:
              nodeSelector:
                ssd: true

修改一下上面的 Deployment,加上标签:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: httpbin
  labels:
    io: heavy
spec:
...

提交到集群之后,查看变更结果:

$ kubectl get deployments httpbin -o yaml
apiVersion: extensions/v1beta1
kind: Deployment
...
    spec:
...
      dnsPolicy: ClusterFirst
      nodeSelector:
        ssd: "true"

看到多出来的 nodeSelector 字段,如果查看 Pod 信息,也会发现这个 Deployment 的所有 Pod 都分配到了指定的节点上。

创建资源(generate)

有时候我们在 Kubernetes 上创建资源的时候,可能希望同时提供一些缺省资源,例如一个新的命名空间,我们希望其中包含缺省的 Configmap 或者 SA 或者资源限制。

例如我们要在新建 test-n 的命名空间的同时,创建名为 dummy 的 sa。

apiVersion : kyverno.io/v1alpha1
kind: Policy
metadata:
  name: auto-sa
spec:
  rules:
  - name: auto-sa
    resource:
      kinds:
      - Namespace
      name: "test-*"
    generate:
      kind: ServiceAccount
      name: dummy
      data:
        spec: {}
        metadata:
          labels:
            source: "webhook"

这个策略生效后,每次我们创建形如 test-* 的命名空间,其中都会生成对应的名为 dummy 的 ServiceAccount,并且有标签:source=webhook

Generate 还提供了复制对象的方法,例如每个新命名空间中都应该复制一个名为 connConfigmap,就可以使用如下策略:

apiVersion : kyverno.io/v1alpha1
kind: Policy
metadata:
  name: auto-cm
spec:
  rules:
  - name: auto-cm
    resource:
      kinds:
      - Namespace
      name: "test-*"
    generate:
        kind: ConfigMap
        name: conn
        clone:
          namespace: default
          name: conn

随意验证一下:

$ kubectl create configmap conn \
    --from-literal=mysql=mysql \
    --from-literal=mongodb=mongodb
configmap/conn created
$ kubectl create ns test-6
namespace/test-6 created
$ kubectl get cm,sa -n test-6
NAME             DATA   AGE
configmap/conn   2      6s

NAME                     SECRETS   AGE
serviceaccount/default   1         7s
serviceaccount/dummy     1         6s

这里会发现,随着新的命名空间的创建,新的 SA 和 CM 也都出现了。

结论

相对于其他的类似工具,Kyverno 在灵活、强大和易用之间取得了一个很好的平衡,不需要太多学习时间,就能够提供相当方便的功能,官网提供了大量的针对各种场景的样例,非常值得一看。

参考链接

  • 项目主页:https://kyverno.io/

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

使用 Kyverno 定义 Kubernetes 策略