使用 Kyverno Generate 规则

使用 Kyverno generate 规则,在匹配到资源被创建或更新时自动创建 Kubernetes 资源。generate 规则定义了:

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

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

有关上游 Kyverno 参考信息,请参阅官方 Kyverno generate rules 文档

Generate 的工作方式

Kyverno generate 规则是 ClusterPolicyPolicy 的一部分。

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 提供,并在创建后很快被消费。

前提条件

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

此示例不需要 notification rule、notification policy、notification receiver 或 notification sender 资源。收件人电子邮件地址直接在生成的 NotificationMessage 中指定。

准备目标资源依赖项

生成的 NotificationMessage 会引用一个电子邮件模板。在应用 Kyverno policy 之前,请先创建该模板。

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 通过其后台 controller 创建生成的资源。请授予该 controller 在目标命名空间中创建 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 安装在不同的命名空间中,或者使用了不同的后台 controller ServiceAccount 名称,请在应用 YAML 之前更新 subjects 部分。

创建 Generate Policy

创建一个 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

generate 规则使用以下关键字段:

  • match.resources:仅监视指定的 ConfigMap 和 UPDATE 操作。
  • preconditions:比较新的和旧的 data 字段,以避免仅元数据更新时生成消息。
  • generate.apiVersionkindnamenamespace:定义目标资源标识。
  • generate.data:定义完整的生成资源主体。
  • synchronize: false:为每个匹配的更新创建一个单独的目标资源。

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

验证 Generate 规则

更新被监视的 ConfigMap。

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

检查 Kyverno 后台 controller 日志。日志会显示 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 update requests。

kubectl get updaterequests -A

如果某个 update request 失败,请查看其详细错误信息。

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

常见原因包括:

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