此文档中的信息可能已过时

此文档的更新日期比原文晚,因此其中的信息可能已过时。如果能阅读英文,请查看英文版本以获取最新信息: Mutating Admission Policy

变更性准入策略

特性状态: Kubernetes v1.32 [alpha]

本页概要介绍 MutatingAdmissionPolicy(变更性准入策略)

什么是 MutatingAdmissionPolicy?

变更性准入策略(Mutating Admission Policy)提供了一种声明式的、进程内的方案, 可以用来替代变更性准入 Webhook。

变更性准入策略使用通用表达语言(Common Expression Language,CEL)来声明对资源的变更。 变更操作可以通过使用服务器端应用合并策略所合并的应用配置来定义, 也可以使用 JSON 补丁来定义。

变更性准入策略的可配置能力很强,策略的编写者可以根据集群管理员的需要,定义参数化的策略以及限定到某类资源的策略。

策略包含哪些资源

策略通常由三种资源组成:

  • MutatingAdmissionPolicy 描述策略的抽象逻辑(可以理解为:“此策略将特定标签设置为特定值”)。
  • 为 MutatingAdmissionPolicy 提供信息的一个参数资源(parameter resource), 有了参数之后,策略成为一条具体的语句 (假想:“将 owner 标签设置为类似 company.example.com 的值”)。 参数资源引用 Kubernetes API 中可用的 Kubernetes 某种资源。被引用的资源可以是内置类别或类似 CustomResourceDefinition(CRD)这种扩展资源。 例如,你可以使用 ConfigMap 作为参数。

  • MutatingAdmissionPolicyBinding 将上述两种资源(MutatingAdmissionPolicy 和参数)关联在一起, 并提供作用域限定。如果你只想为 Pods 设置 owner 标签,而不想为其他 API 类别设置标签, 则绑定就是你用来限定此变更的地方。

你必须定义至少一个 MutatingAdmissionPolicy 和一个相应的 MutatingAdmissionPolicyBinding, 才能使策略生效。

如果 MutatingAdmissionPolicy 不需要通过参数进行配置,在 MutatingAdmissionPolicy 中不指定 spec.paramKind 即可。

开始使用 MutatingAdmissionPolicy

变更性准入策略是集群控制平面的一部分。你在编写和部署这些策略时要非常谨慎。 下文描述如何快速试用变更性准入策略。

创建 MutatingAdmissionPolicy

以下是一个 MutatingAdmissionPolicy 的示例。 此策略会将变更新建的 Pod,为其添加一个边车容器(如果以前没有边车容器的话)。

apiVersion: admissionregistration.k8s.io/v1alpha1
kind: MutatingAdmissionPolicy
metadata:
  name: "sidecar-policy.example.com"
spec:
  paramKind:
    kind: Sidecar
    apiVersion: mutations.example.com/v1
  matchConstraints:
    resourceRules:
    - apiGroups:   ["apps"]
      apiVersions: ["v1"]
      operations:  ["CREATE"]
      resources:   ["pods"]
  matchConditions:
    - name: does-not-already-have-sidecar
      expression: "!object.spec.initContainers.exists(ic, ic.name == \"mesh-proxy\")"
  failurePolicy: Fail
  reinvocationPolicy: IfNeeded
  mutations:
    - patchType: "ApplyConfiguration"
      applyConfiguration:
        expression: >
          Object{
            spec: Object.spec{
              initContainers: [
                Object.spec.initContainers{
                  name: "mesh-proxy",
                  image: "mesh/proxy:v1.0.0",
                  args: ["proxy", "sidecar"],
                  restartPolicy: "Always"
                }
              ]
            }
          }          

.spec.mutations 字段由一系列表达式组成,这些表达式求值后将形成资源补丁。 所生成的补丁可以是应用配置JSON 补丁。 你不能将 mutations 设置为空列表。在对所有表达式求值后,API 服务器将所得到的变更应用到正在通过准入阶段的资源。

要配置变更准入策略以便用于某个集群中,需要先创建绑定。 只有存在 spec.policyName 字段值与某策略的 spec.name 相匹配的绑定时,该策略才会生效。

一旦创建了绑定和策略,策略的 spec.matchConditions 相匹配的所有资源请求都会触发已定义的所有变更集合。

在上面的示例中,创建 Pod 将触发添加 mesh-proxy initContainer 这一变更:

apiVersion: v1
kind: Pod
metadata:
  name: myapp
  namespace: default
spec:
  ...
  initContainers:
  - name: mesh-proxy
    image: mesh/proxy:v1.0.0
    args: ["proxy", "sidecar"]
    restartPolicy: Always
  - name: myapp-initializer
    image: example/initializer:v1.0.0
  ...

参数资源

使用参数资源,我们可以将策略配置与其定义分隔开。策略可以定义 paramKind,划定参数资源的 GVK, 随后的策略绑定操作会通过 paramRef 按名称(通过 policyName)将某个策略绑定到特定参数资源。

有关细节参阅参数资源

ApplyConfiguration

MutatingAdmissionPolicy 表达式始终是 CEL 格式的。 每个应用配置 expression 必须求值为(使用 Object() 初始化声明的)CEL 对象。

这些应用配置不能修改原子结构、映射或数组,因为这类修改可能导致意外删除未包含在应用配置中的值。

CEL 表达式可以访问以 CEL 变量组织起来的 API 请求内容及一些其他有用变量:

  • Object - 资源对象的 CEL 类型。
  • Object.<fieldName> - 对象字段的 CEL 类型(例如 Object.spec
  • Object.<fieldName1>.<fieldName2>...<fieldNameN> - 嵌套字段的 CEL 类型(例如 Object.spec.containers

CEL 表达式可以访问以 CEL 变量组织起来的 API 请求内容及一些其他有用变量:

  • object - 来自传入请求的对象。对于 DELETE 请求,取值为 null。
  • oldObject - 现有对象。对于 CREATE 请求,取值为 null。
  • request - API 请求的属性。
  • params - 正被评估的策略绑定所引用到的参数资源。仅在策略设置了 paramKind 时填充。
  • namespaceObject - 传入对象所属的命名空间对象。对于集群范围的资源,取值为 null。
  • variables - 组合变量的映射,包含从变量名称到其惰性评估值的映射。 例如,名为 foo 的变量可以以 variables.foo 的方式访问。
  • authorizer - 一个 CEL 鉴权器。可用于对请求的主体(用户或服务账户)进行鉴权检查。 参阅 https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz
  • authorizer.requestResource - 从 authorizer 构建并使用请求资源配置的 CEL ResourceCheck。

apiVersionkindmetadata.namemetadata.generateNamemetadata.labels 始终可以从对象的根进行访问。其他元数据属性不可访问。

JSONPatch

相同的变更可以被写成以下的 JSON 补丁

apiVersion: admissionregistration.k8s.io/v1alpha1
kind: MutatingAdmissionPolicy
metadata:
  name: "sidecar-policy.example.com"
spec:
  paramKind:
    kind: Sidecar
    apiVersion: mutations.example.com/v1
  matchConstraints:
    resourceRules:
    - apiGroups:   [""]
      apiVersions: ["v1"]
      operations:  ["CREATE"]
      resources:   ["pods"]
  matchConditions:
    - name: does-not-already-have-sidecar
      expression: "!object.spec.initContainers.exists(ic, ic.name == \"mesh-proxy\")"
  failurePolicy: Fail
  reinvocationPolicy: IfNeeded
  mutations:
    - patchType: "JSONPatch"
      jsonPatch:
        expression: >
          [
            JSONPatch{
              op: "add", path: "/spec/initContainers/-",
              value: Object.spec.initContainers{
                name: "mesh-proxy",
                image: "mesh-proxy/v1.0.0",
                restartPolicy: "Always"
              }
            }
          ]          

表达式将通过 CEL 求值,以创建 JSON 补丁。 参阅 https://github.com/google/cel-spec

每个被求值的 expression 必须返回 JSONPatch 值形成的数组。 JSONPatch 类型表示 JSON 补丁中的一个操作。

例如,下面的 CEL 表达式返回 JSON 补丁,用于有条件地修改某个值:

  [
    JSONPatch{op: "test", path: "/spec/example", value: "Red"},
    JSONPatch{op: "replace", path: "/spec/example", value: "Green"}
  ]

要为补丁操作 value 定义 JSON 对象,可以使用 CEL Object 类型。例如:

  [
    JSONPatch{
      op: "add",
      path: "/spec/selector",
      value: Object.spec.selector{matchLabels: {"environment": "test"}}
    }
  ]

要使用包含 '/' 和 '~' 的字符串作为 JSONPatch 路径键,可以使用 jsonpatch.escapeKey()。例如:

  [
    JSONPatch{
      op: "add",
      path: "/metadata/labels/" + jsonpatch.escapeKey("example.com/environment"),
      value: "test"
    },
  ]

CEL 表达式可以访问创建 JSON 补丁和对象所需的类型:

  • JSONPatch - JSON 补丁操作的 CEL 类型。JSONPatch 具有 opfrompathvalue 字段。 有关细节参阅 JSON 补丁value 字段可以设置为字符串、整数、数组、映射或对象。 如果设置,pathfrom 字段的值必须为 JSON 指针字符串, 可以在指针中使用 jsonpatch.escapeKey() CEL 函数来转义包含 /~ 的路径键。
  • Object - 资源对象的 CEL 类型。
  • Object.<fieldName> - 对象字段的 CEL 类型(例如 Object.spec
  • Object.<fieldName1>.<fieldName2>...<fieldNameN> - 嵌套字段的 CEL 类型(例如 Object.spec.containers

CEL 表达式可以访问以 CEL 变量组织的 API 请求的内容及一些其他有用变量:

  • object - 来自传入请求的对象。对于 DELETE 请求,取值为 null。
  • oldObject - 现有对象。对于 CREATE 请求,取值为 null。
  • request - API 请求的属性。
  • params - 正被评估的策略绑定所引用的参数资源。仅在策略具有 paramKind 时填充。
  • namespaceObject - 传入对象所属的命名空间对象。对于集群范围的资源,取值为 null。
  • variables - 组合变量的映射,包含从变量名称到其惰性评估值的映射。 例如,名为 foo 的变量可以以 variables.foo 的形式访问。
  • authorizer - 一个 CEL 鉴权组件。可用于对请求的主体(用户或服务账户)执行鉴权检查。 参阅 https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz
  • authorizer.requestResource - 从 authorizer 构建并以请求资源配置的 CEL ResourceCheck。

CEL 表达式可以访问 Kubernetes CEL 函数库以及:

  • jsonpatch.escapeKey - 执行 JSONPatch 键转义。~/ 分别被转义为 ~0~1

只有格式为 [a-zA-Z_.-/][a-zA-Z0-9_.-/]* 的属性名称是可访问的。

最后修改 January 02, 2025 at 11:37 AM PST: [zh] Add mutating-admission-policy.md (ecfdc47eea)