Kyverno变量

作者: 王勇1024 | 来源:发表于2023-01-07 13:09 被阅读0次

    用于重用和智能决策的数据驱动策略

    变量通过启用对策略定义、准入审查请求和外部数据源(如 ConfigMaps、Kubernetes API Server 甚至 OCI image registries)中的数据的引用,使策略更加智能和可重用。

    变量存储为 JSON,Kyverno 支持使用 JMESPath(发音为“James path”)来选择和转换 JSON 数据。使用 JMESPath,以 {{key1.key2.key3}} 的格式引用来自数据源的值。例如,要在 kubectl apply 操作(如命名空间)期间引用传入资源的名称,您可以将其写为变量引用:{{request.object.metadata.name}}。在处理规则之前,策略引擎将使用变量值替换任何格式为 {{ <JMESPath> }} 的值。有关专门探讨 JMESPath 在 Kyverno 中的使用的页面,请参见此处

    注意:目前在 match 或 exclude 语句或 patchesJson6902.path 中不允许使用变量。

    预定义变量

    Kyverno 自动创建一些有用的变量并在规则中使它们可用:

    1. serviceAccountName: “userName”,它是 service account 的最后一部分(即没有前缀 system:serviceaccount:<namespace>:)。例如,在处理来自 system:serviceaccount:nirmata:user1 的请求时,Kyverno 会将值 user1 存储在变量 serviceAccountName 中。

    2. serviceAccountNamespace: serviceAccount 的 “namespace” 部分。例如,在处理来自 system:serviceaccount:nirmata:user1 的请求时,Kyverno 会将 nirmata 存储在变量 serviceAccountNamespace 中。

    3. request.roles: 存储在数组中的、给定账户可能具有的 role 列表。如["foo:dave"]。

    4. request.clusterRoles: 存储在数组中的 cluster role 列表。如 ["dave-admin","system:basic-user","system:discovery","system:public-info-viewer"]

    5. images: a map of container image information, if available. See Variables from container images for more information.

    注意:request.roles 或 request.clusterRoles 之一将被替换为变量,但不能同时替换两者。

    策略定义中的变量

    Kyverno 策略定义可以以“快捷方式”的形式引用策略定义中的其他字段。这可能是一种分析和比较值的有用方法,而无需显式定义它们。

    为了让 Kyverno 在清单中引用这些现有值,它使用符号 $(./../key_1/key_2)。这可能看起来很熟悉,因为它本质上与 Linux/Unix 系统引用相对路径的方式相同。 例如,可以看下面的策略清单片段。

    validationFailureAction: enforce
    rules:
    - name: check-tcpSocket
      match:
        any:
        - resources:
            kinds:
            - Pod
      validate:
        message: "Port number for the livenessProbe must be less than that of the readinessProbe."
        pattern:
          spec:
            ^(containers):
            - livenessProbe:
                tcpSocket:
                  port: "$(./../../../readinessProbe/tcpSocket/port)"
              readinessProbe:
                tcpSocket:
                  port: "3000"
    

    在上面的示例中,Pod Spec 中所有 container 的 readinessProbe.tcpSocket.port 字段必须为 3000,livenessProbe.tcpSocket.port 字段值也必须是相同的值。查找表达式可以被认为是一个 cd 返回三个级别并向下进入 readinessProbe 对象。

    可以在查找清单变量中使用运算符,因此可以修改前面的代码片段。

    - livenessProbe:
        tcpSocket:
          port: "$(<./../../../readinessProbe/tcpSocket/port)"
      readinessProbe:
        tcpSocket:
          port: "3000"
    

    在这个示例中,livenessProbe.tcpSocket.port 的字段值必须小于 readinessProbe.tcpSocket.port 的值。

    更多信息请参考Operators章节。

    转义变量

    在某些情况下,您希望编写一个包含变量的规则,以供另一个程序或流程执行操作,而不是供 Kyverno 使用。例如,使用 $() 表示法中的变量,从 Kyverno 1.5.0 开始,这些变量可以使用前导反斜杠 () 进行转义,并且 Kyverno 不会尝试替换值。用 JMESPath 表示法编写的变量也可以使用相同的语法进行转义,例如 {{ request.object.metadata.name }}。

    下面的策略中,OTEL_RESOURCE_ATTRIBUTES 的值包含另一个环境变量的引用,例如,$(POD_NAMESPACE)。

    apiVersion: kyverno.io/v1
    kind: Policy
    metadata:
      name: add-otel-resource-env
    spec:
      background: false
      rules:
      - name: imbue-pod-spec
        match:
          any:
          - resources:
              kinds:
              - v1/Pod
        mutate:
          patchStrategicMerge:
            spec:
              containers:
              - (name): "?*"
                env:
                - name: NODE_NAME
                  value: "mutated_name"
                - name: POD_IP_ADDRESS
                  valueFrom:
                    fieldRef:
                      fieldPath: status.podIP
                - name: POD_NAME
                  valueFrom:
                    fieldRef:
                      fieldPath: metadata.name
                - name: POD_NAMESPACE
                  valueFrom:
                    fieldRef:
                      fieldPath: metadata.namespace
                - name: POD_SERVICE_ACCOUNT
                  valueFrom:
                    fieldRef:
                      fieldPath: spec.serviceAccountName
                - name: OTEL_RESOURCE_ATTRIBUTES
                  value: >-
                    k8s.namespace.name=\$(POD_NAMESPACE),
                    k8s.node.name=\$(NODE_NAME),
                    k8s.pod.name=\$(POD_NAME),
                    k8s.pod.primary_ip_address=\$(POD_IP_ADDRESS),
                    k8s.pod.service_account.name=\$(POD_SERVICE_ACCOUNT),
                    rule_applied=$(./../../../../../../../../name)                
    

    使用如下 Pod 定义,可以对此进行测试。

    apiVersion: v1
    kind: Pod
    metadata:
      name: test-env-vars
    spec:
      containers:
      - name: test-container
        image: busybox
        command: ["sh", "-c"]
        args:
        - while true; do
          echo -en '\n';
          printenv OTEL_RESOURCE_ATTRIBUTES;
          sleep 10;
          done;
        env:
        - name: NODE_NAME
          value: "node_name"
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: POD_IP_ADDRESS
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
      restartPolicy: Never
    

    结果是 OTEL_RESOURCE_ATTRIBUTES 环境变量会被添加到 Pod 中,如下所示:

    - name: OTEL_RESOURCE_ATTRIBUTES
          value: k8s.namespace.name=$(POD_NAMESPACE), k8s.node.name=$(NODE_NAME), k8s.pod.name=$(POD_NAME),
            k8s.pod.primary_ip_address=$(POD_IP_ADDRESS), k8s.pod.service_account.name=$(POD_SERVICE_ACCOUNT),
            rule_applied=imbue-pod-spec
    

    AdmissionReview 请求变量

    Kyverno 以 webhook 的方式在 Kubernetes 中运行。当 Kubernetes API Server 收到新的请求时(如创建 Pod),API Server将该信息发送给已注册的、用于监听 Pod 创建事件的 webhook。传入的数据会以 AdmissionReview 对象传给 webhook。有四种常用于获取 AdmissionReview 请求中数据属性的方式:

    • {{request.operation}}:正在执行的 API 操作的类型(CREATE、UPDATE、DELETE 或 CONNECT)。

    • {{request.object}}:正在被创建或修改的新对象。对于 DELETE 请求,该对象是null。

    • {{request.oldObject}}:被修改的旧对象。对于 CREATE 和 CONNECT 请求,该对象是null。

    • {{request.userInfo}} :包含有关谁/什么提交了请求的信息,其中包括组和用户名键。

    • {{request.namespace}}:被操作对象的命名空间。

    以下是查找此数据的一些示例:

    • 引用资源名称(字符串类型)

    {{request.object.metadata.name}}

    • 引用元数据(对象类型)

    {{request.object.metadata}}

    • 引用正在创建的新命名空间的名称

    {{request.object.name}}

    • 引用提交请求的用户名

    {{request.userInfo.username}}

    AdmissionReview 中的变量也可以和用户定义的字符串相结合,用于 messages 或其它字段。

    1. 从多个变量构建一个名称(字符串类型)

    "ns-owner-{{request.namespace}}-{{request.userInfo.username}}-binding"

    让我们看一个如何在 Kyverno 政策中使用此 AdmissionReview 数据的示例。

    在下面的 ClusterPolicy 中,我们想知道哪个账户创建了一个给定的 Pod 资源。我们可以使用 AdmissionReview 包含的信息,特别是 username,将这个信息写入 label 中。如下所示:

    apiVersion: kyverno.io/v1
    kind: ClusterPolicy
    metadata:
      name: who-created-this
    spec:
      background: false
      rules:
      - name: who-created-this
        match:
          any:
          - resources:
              kinds:
              - Pod
        mutate:
          patchStrategicMerge:
            metadata:
              labels:
                created-by: "{{request.userInfo.username}}"
    

    这个示例会修改传入的 Pod,并将 kubeconfig 中授权的用户信息写入 created-by 标签中。

    创建一个简单的 Pod 资源:

    kubectl run busybox --image busybox:1.28
    

    查看新创建的 Pod busybox:

    kubectl get po busybox --show-labels
    
    NAME       READY   STATUS    RESTARTS   AGE   LABELS
    busybox   0/1     Pending   0          25m   created-by=kubernetes-admin,run=busybox
    

    在输出信息中,我们可以看到 Pod 上有一个标签 created-by=kubernetes-admin,kubernetes-admin 就是创建该 Pod 的用户。

    来自容器镜像的变量

    Kyverno 能从 AdmissionReview 请求中提取镜像数据,并能够在规则上下文中用 map 类型的变量 images 引用这些数据。例如:

    {
      "containers": {
        "tomcat": {
          "registry": "https://ghcr.io",
          "name": "tomcat",
          "tag": "9"
        }
      },
      "initContainers": {
        "vault": {
          "registry": "https://ghcr.io",
          "name": "vault",
          "tag": "v3"
        }
      }
    }
    

    如果一个 AdmissionReview 请求中定义了 containers 或 initContainers,就可以用下面的方式通过 images 引用这些数据。

    引用 tomcat 容器中镜像的属性:

    • 引用 registry URL

    {{images.containers.tomcat.registry}}

    • 引用镜像名称

    {{images.containers.tomcat.name}}

    • 引用镜像tag

    {{images.containers.tomcat.tag}}

    • 引用摘要

    {{images.containers.tomcat.digest}}

    引用 vault initContainer 中镜像的属性:

    • 引用 registry URL

    {{images.initContainers.vault.registry}}

    • 引用镜像名称

    {{images.initContainers.vault.name}}

    • 引用镜像tag

    {{images.initContainers.vault.tag}}

    • 引用摘要

    {{images.initContainers.vault.digest}}

    这种相同的模式和镜像变量编排也适用于临时容器。

    Kyverno 默认将一个空 registry 设置为 docker.io,并将一个空 tag 设置为 latest。

    注意

    请注意,对于 JMESPath 中某些字符必须进行转义(如容器名中的-),可以通过使用双引号和双转义字符 \ 来完成转义,例如 {{images.containers."my-container".tag}}。更多信息请参考 JMESPath 格式化

    您还可以获取所有容器的镜像属性以进行进一步处理。例如,{{ images.containers.*.name }} 创建了一个包含所有容器名称的列表。

    内联变量

    可以在 context 中定义变量以供 Kyverno 规则使用。这可以像静态值、另一个变量或嵌套对象一样简单。也可以使用相同的变量名重新定义变量,将使用最后设置的值。下面设置了一个值为 foo 的 context 变量。

        context:
        # 唯一的变量名称
        # 如果用户重新声明一个名称相同的变量,该变量会被重分配
        - name: foodata
          variable:
            # value 定义了变量的值,它可能包含 jmespath 变量或者任何可以用 JSON 对象表示的 YAML 对象。
            # value、default 和 jmespath 是可选的,但必须定义 value 或 jmespath。
            value: "foo"
    

    这个片段给 context 变量设置的值为 request.object.metadata.name。如果未定义 value 字段,jmesPath 的内容将作用于整个上下文。

    context:
    - name: objName
      variable:
        jmesPath: request.object.metadata.name
    

    变量可以引用其他变量,如下所示。

    context:
    - name: jpExpression
      variable:
        value: name
    - name: objName
      variable:
        value:
          name: "{{ request.object.metadata.name }}"
        jmesPath: "{{ jpExpression }}"
    

    来自外部数据源的变量

    一些策略决策需要访问由其他 Kubernetes 控制器或外部应用程序管理的集群资源和数据。 对于这些类型的策略,Kyverno 允许对 Kubernetes API Server进行 HTTP 调用并使用 ConfigMap。

    从外部来源获取的数据存储在按规则处理的上下文中,用于由策略引擎评估变量。一旦来自外部源的数据存储在上下文中,就可以像任何其他变量数据一样引用它。

    要了解有关 ConfigMap 查找和 API Server 调用的更多信息,请参考 外部数据源

    嵌套查找

    也可以将 JMESPath 表达式嵌套在另一个表达式中,例如,当混合来自 ConfigMap 和 AdmissionReview 的数据时。通过在另一个中包含一个 JMESPath 表达式,Kyverno 将首先替换内部表达式,然后再构建外部表达式,如下例所示。

    apiVersion: kyverno.io/v1
    kind: ClusterPolicy
    metadata:
      name: resource-annotater
    spec:
      background: false
      rules:
      - name: add-resource-annotations
        context:
        - name: LabelsCM
          configMap:
            name: resource-annotater-reference
            namespace: default
        match:
          any:
          - resources:
              kinds:
              - Pod
        mutate:
          patchStrategicMerge:
            metadata:
              annotations:
                foo: "{{LabelsCM.data.{{ request.object.metadata.labels.app }}}}"
    

    在这个示例中,首先以 {{request.object.metadata.labels.app}} 的形式将 AdmissionReview 中的数据收集在内部表达式中,而外部表达式是从名为 LabelsCM 的 ConfigMap 上下文构建的。

    评估顺序

    Kyverno 策略可以在以下位置使用变量:

    • 规则上下文(context)

    • 规则前置条件(preconditions)

    • 规则定义

      • Validation patterns

      • Validation deny rules

      • Mutate strategic merge patches (patchesStrategicMerge)

      • Generate resource data definitions

    match 和 exclude 元素中不支持使用变量。这样规则就可以快速匹配,而无需加载和处理数据。patchesJson6902.path 中也不支持使用变量。

    由于变量可以嵌套,因此了解变量的计算顺序很重要。 在准入控制期间,引擎处理规则的方式如下:

    1. 匹配规则集是通过从请求信息创建散列来确定的,以根据规则和资源类型检索所有匹配规则。

    2. 每个匹配的规则都经过进一步处理,以全面评估匹配和检索条件。

    3. 然后评估规则上下文并从数据源加载变量。

    4. 然后检查前置条件。

    5. 处理规则体。

    这种排序使得在定义 context 时可以使用请求数据,并在前置条件中使用上下文变量。在 context 本身中,每个变量都按照定义的顺序进行评估。因此,如果需要,变量可以引用先前的变量,但尝试使用后续定义将导致错误。

    JMESPath 自定义函数

    除了 JMESPath 提供的内置功能列表之外,Kyverno 通过添加其他几个功能来增强这些功能,这使得制定 Kyverno 策略更加容易。

    通用

    base64_decode(string) string
    base64_encode(string) string
    compare(string, string) integer
    equal_fold(string, string) bool
    label_match(object, object) bool (object arguments must be enclosed in backticks; ex. `{{request.object.spec.template.metadata.labels}}`)
    parse_json(string) any (decodes a valid JSON encoded string to the appropriate type. Opposite of `to_string` function)
    parse_yaml(string) any
    path_canonicalize(string) string
    pattern_match(pattern string, string|number) bool ('*' matches zero or more alphanumeric characters, '?' matches a single alphanumeric character)
    regex_match(string, string|number) bool
    regex_replace_all(regex string, src string|number, replace string|number) string (converts all parameters to string)
    regex_replace_all_literal(regex string, src string|number, replace string|number) string (converts all parameters to string)
    replace(str string, old string, new string, n float64) string
    replace_all(str string, old string, new string) string
    semver_compare(string, string) bool (Use operators [>, <, etc] with string inputs for comparison logic)
    split(str string, sep string) []string
    time_since(<layout>, <time1>, <time2>) string (all inputs as string)
    to_upper(string) string
    to_lower(string) string
    trim(str string, cutset string) string
    truncate(str string, length float64) string (length argument must be enclosed in backticks; ex. "{{request.object.metadata.name | truncate(@, `9`)}}")
    

    计算

    add(number, number) number
    add(quantity|number, quantity|number) quantity (returns a quantity if any of the parameters is a quantity)
    add(duration|number, duration|number) duration (returns a duration if any of the parameters is a duration)
    subtract(number, number) number
    subtract(quantity|number, quantity|number) quantity (returns a quantity if any of the parameters is a quantity)
    subtract(duration|number, duration|number) duration (returns a duration if any of the parameters is a duration)
    multiply(number, number) number
    multiply(quantity|number, quantity|number) quantity (returns a quantity if any of the parameters is a quantity)
    multiply(duration|number, duration|number) duration (returns a duration if any of the parameters is a duration)
    divide(quantity|number, quantity|number) quantity|number (returns a quantity if exactly one of the parameters is a quantity, else a number; the divisor must be non-zero)
    divide(duration|number, duration|number) duration|number (returns a duration if exactly one of the parameters is a duration, else a number; the divisor must be non-zero)
    modulo(number, number) number
    modulo(quantity|number, quantity|number) quantity (returns a quantity if any of the parameters is a quantity; the divisor must be non-zero)
    modulo(duration|number, duration|number) duration (returns a duration if any of the parameters is a duration; the divisor must be non-zero)
    

    注意

    JMESPath 算术函数适用于标量(例如,10)、资源数量(例如,10Mi)和持续时间(例如,10h)。如果输入是标量,则必须将其括在反引号中,以便将参数视为数字。资源数量和持续时间用单引号括起来,被视为字符串。

    特殊变量 {{@}} 可用于引用给定字段中的当前值,这对于源值很有用。

    要查找其中一些函数的实际示例,请参阅 Kyverno 策略库。有关每个自定义过滤器的更完整信息以及示例,请参阅此处的 JMESPath 页面。

    相关文章

      网友评论

        本文标题:Kyverno变量

        本文链接:https://www.haomeiwen.com/subject/dapscdtx.html