使用 Kyverno Generate 规则

使用 Kyverno generate 规则,在创建或更新匹配的资源时自动创建 Kubernetes 资源。Generate 规则定义以下内容:

  • 要监视的源资源,使用 match。
  • 应触发生成的变更,使用可选的 preconditions。
  • Kyverno 应创建的目标资源,使用 generate。
  • Kyverno 是否应使生成的资源与源资源保持同步。

本指南以更新 ConfigMap 作为示例流程。当特定命名空间中的特定 ConfigMap 发生变更时,Kyverno 会创建平台 NotificationMessage 资源。随后,通知服务会向指定的收件人发送电子邮件。

有关上游 Kyverno 参考信息,请参阅官方的 Kyverno generate 规则文档。

Generate 的工作原理

Kyverno generate 规则是 ClusterPolicy 或 Policy 的一部分。

rules:
  - name: <rule-name>
    match:
      any:
        - resources:
            kinds:
              - <source-kind>
            operations:
              - CREATE
              - UPDATE
    preconditions:
      all:
        - key: <expression>
          operator: <operator>
          value: <expression>
    generate:
      apiVersion: <target-api-version>
      kind: <target-kind>
      name: <target-name>
      namespace: <target-namespace>
      synchronize: false
      data:
        <target-resource-content>

当每次触发都应创建独立的目标资源时,使用 synchronize: false。仅当生成的资源应随源资源一同更新或删除时,才使用 synchronize: true。

示例场景

在此示例中:

  • 源命名空间为 app-team-a。
  • 源 ConfigMap 为 app-notification-source。
  • 仅匹配 UPDATE 操作。
  • 仅对 ConfigMap.data 的变更会触发生成。
  • Kyverno 会在 cpaas-system 中创建一个短生命周期的 NotificationMessage。
  • 生成的消息会直接向 platform-operator@example.com 发送电子邮件。

此示例使用的通知消息 API 是 notificationmessages.aitextensions.alauda.io/v1beta1。该 API 由平台通知 API 提供,并会在创建后很快被消费。

前提条件

  • 已安装 Alauda Container Platform Policy Management。
  • 同一集群中已安装平台通知功能。
  • 已配置电子邮件通知服务器。平台默认电子邮件服务器以 platform-email-server Secret 的形式存储在 cpaas-system 命名空间中。
  • cpaas-system 命名空间中存在用于电子邮件消息的通知模板。
  • Kyverno 具有在目标命名空间中创建 NotificationMessage 资源的权限。

此示例不需要通知规则、通知策略、通知接收器或通知发送器资源。收件人的电子邮件地址直接在生成的 NotificationMessage 中指定。

准备目标资源依赖项

生成的 NotificationMessage 引用了一个电子邮件模板。在应用 Kyverno 策略之前创建该模板。

apiVersion: ait.alauda.io/v1beta1
kind: NotificationTemplate
metadata:
  name: configmap-change-email-template
  namespace: cpaas-system
  labels:
    cpaas.io/template.email.body.type: Html
    cpaas.io/template.language: EN
spec:
  templates:
    - type: Email
      subject: "ConfigMap {{ .labels.namespace }}/{{ .labels.name }} changed"
      content: |-
        The watched ConfigMap was updated.

        Namespace: {{ .labels.namespace }}
        Name: {{ .labels.name }}
        Resource version: {{ .labels.resourceVersion }}
        Application: {{ .labels.application }}
        Owner: {{ .labels.owner }}
        Severity: {{ .labels.severity }}

准备源资源

创建 Kyverno 将监视的命名空间和 ConfigMap。

apiVersion: v1
kind: Namespace
metadata:
  name: app-team-a
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-notification-source
  namespace: app-team-a
data:
  application: payment
  owner: platform-ops
  severity: warning

授予 Kyverno 权限

Kyverno 通过其后台控制器创建生成的资源。授予该控制器在目标命名空间中创建 NotificationMessage 资源的权限。

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: kyverno-generate-notificationmessages
  namespace: cpaas-system
rules:
  - apiGroups:
      - aitextensions.alauda.io
      - aiops.alauda.io
    resources:
      - notificationmessages
    verbs:
      - get
      - list
      - watch
      - create
      - update
      - patch
      - delete
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: kyverno-generate-notificationmessages
  namespace: cpaas-system
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: kyverno-generate-notificationmessages
subjects:
  - kind: ServiceAccount
    name: kyverno-background-controller
    namespace: kyverno

如果 Kyverno 安装在其他命名空间中,或使用不同的后台控制器 ServiceAccount 名称,请在应用 YAML 之前更新 subjects 部分。

创建生成策略

创建一个 ClusterPolicy,仅当 app-team-a/app-notification-source 更新且其 data 字段发生变化时,才生成 NotificationMessage。

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: generate-notificationmessage-on-configmap-change
  annotations:
    policies.kyverno.io/title: Generate NotificationMessage on ConfigMap Change
    policies.kyverno.io/category: Operations
    policies.kyverno.io/subject: ConfigMap, NotificationMessage
    policies.kyverno.io/description: >-
      Generates a platform NotificationMessage resource when the watched ConfigMap data changes.
spec:
  background: false
  rules:
    - name: generate-email-notificationmessage
      match:
        any:
          - resources:
              kinds:
                - ConfigMap
              namespaces:
                - app-team-a
              names:
                - app-notification-source
              operations:
                - UPDATE
      preconditions:
        all:
          - key: "{{ request.object.data || `{}` }}"
            operator: NotEquals
            value: "{{ request.oldObject.data || `{}` }}"
      generate:
        apiVersion: aitextensions.alauda.io/v1beta1
        kind: NotificationMessage
        name: "cm-change-{{ request.object.metadata.namespace }}-{{ request.object.metadata.name }}-{{ request.object.metadata.resourceVersion }}"
        namespace: cpaas-system
        synchronize: false
        data:
          metadata:
            labels:
              app.kubernetes.io/managed-by: kyverno
              app.kubernetes.io/part-of: configmap-change-notification
              configmap-change/source-namespace: "{{ request.object.metadata.namespace }}"
              configmap-change/source-name: "{{ request.object.metadata.name }}"
            annotations:
              configmap-change/resource-version: "{{ request.object.metadata.resourceVersion }}"
          spec:
            body:
              labels:
                namespace: "{{ request.object.metadata.namespace }}"
                name: "{{ request.object.metadata.name }}"
                resourceVersion: "{{ request.object.metadata.resourceVersion }}"
                application: "{{ request.object.data.application || '' }}"
                owner: "{{ request.object.data.owner || '' }}"
                severity: "{{ request.object.data.severity || '' }}"
            notifications:
              - name: "configmap-change-email-{{ request.object.metadata.resourceVersion }}"
                ephemeral:
                  methods:
                    - Email
                  template: configmap-change-email-template
                receivers:
                  - destination: platform-operator@example.com

生成规则使用以下关键字段:

  • match.resources:仅监视指定的 ConfigMap,并且仅监视 UPDATE 操作。
  • preconditions:比较新旧 data 字段,因此仅元数据更新不会生成消息。
  • generate.apiVersion、kind、name 和 namespace:定义目标资源的标识。
  • generate.data:定义生成资源的完整内容。
  • synchronize: false:为每次匹配的更新创建单独的目标资源。

生成的 NotificationMessage 使用 spec.body.labels 作为模板变量,使用 ephemeral.methods 选择电子邮件传递方式,使用 ephemeral.template 引用电子邮件模板,并使用 receivers[].destination 指定收件人的电子邮件地址。

验证生成规则

更新受监视的 ConfigMap。

kubectl patch configmap app-notification-source \
  -n app-team-a \
  --type merge \
  -p '{"data":{"severity":"critical"}}'

检查 Kyverno 后台控制器日志。日志显示 Kyverno 创建了生成的目标资源。

kubectl logs -n kyverno deploy/kyverno-background-controller --since=5m

日志输出包含类似于以下内容的生成目标:

created generate target resource target=aitextensions.alauda.io/v1beta1/NotificationMessage/cpaas-system/cm-change-app-team-a-app-notification-source-19377570

对于此示例,还应检查通知 API 日志。成功发送电子邮件时,会显示一个任务、电子邮件发件人和收件人,以及 Done 状态。

kubectl logs -n cpaas-system deploy/courier-api --since=5m

日志输出包含类似于以下内容的条目:

Try to exec {"tasks": 1}
Email begin {"from": ["user@example.com"], "to": ["platform-operator@example.com"]}
Email end {"from": ["user@example.com"], "to": ["platform-operator@example.com"]}
"status":{"conditions":[{"method":"Email","addresses":["platform-operator@example.com"],"type":"Ready","status":"True"}],"phase":"Done"}

NotificationMessage 资源的生命周期很短,会由通知 API 消费。在消息被接受后,普通的 kubectl get 命令可能不会显示生成的消息。

故障排查

如果未生成目标资源,请检查 Kyverno 更新请求。

kubectl get updaterequests -A

如果更新请求失败,请检查详细错误信息。

kubectl describe updaterequest -n kyverno <update-request-name>

常见原因包括:

  • 源资源更新与 match.resources 筛选条件不匹配。
  • ConfigMap 更新未更改 data 字段,因此未满足前置条件。
  • Kyverno 后台控制器没有权限在 cpaas-system 中创建 notificationmessages.aitextensions.alauda.io 资源。
  • 目标集群中没有 notificationmessages.aitextensions.alauda.io/v1beta1 API。
  • 生成的资源名称超过了 Kubernetes DNS 标签长度限制。
  • ephemeral.template 引用的电子邮件模板不存在。
  • 电子邮件通知服务器未配置或不可用。