更改资源

本页介绍了如何使用 Policy Controller 修改资源。这对于设置默认值等操作非常有用。例如,您可能希望为特定命名空间中的所有资源注入标签,或者将 Pod 的 imagePullPolicy 默认设置为 Always(如果尚未设置)。

启用变更

控制台

如需启用变更,请完成以下步骤:

  1. 在 Google Cloud 控制台中,转到安全状况管理部分下的 GKE Enterprise 政策页面。

    前往“政策”

  2. 设置标签页下的集群表中,选择修改配置列中的修改
  3. 展开修改 Policy Controller 配置菜单。
  4. 选中启用变更 webhook复选框。
  5. 选择保存更改

gcloud Policy Controller

如需启用变更,请运行以下命令:

gcloud container fleet policycontroller update \
    --memberships=MEMBERSHIP_NAME \
    --mutation

MEMBERSHIP_NAME 替换为要用于启用变更的已注册集群的成员资格名称。您可以指定多个成员资格(以英文逗号分隔)。

gcloud ConfigManagement

如需启用变更,必须在 config-management 资源中将 spec.policyController.mutation.enabled 设置为 true

apiVersion: configmanagement.gke.io/v1
kind: ConfigManagement
metadata:
  name: config-management
spec:
  policyController:
    enabled: true
     mutation:
      enabled: true 

如果您使用的是 gcloud CLI 命令,则必须使用 Alpha 版本来启用变更,如以下示例所示:

  # apply-spec.yaml

  applySpecVersion: 1
  spec:
    policyController:
      enabled: true
      mutationEnabled: true

创建 apply-spec.yaml 文件后,请运行以下命令以应用配置:

  gcloud alpha container fleet config-management apply \
      --membership=MEMBERSHIP_NAME \
      --config=CONFIG_YAML_PATH \
      --project=PROJECT_ID

替换以下内容:

  • MEMBERSHIP_NAME:具有您要使用的政策控制器设置的已注册集群的成员资格名称
  • CONFIG_YAML_PATHapply-spec.yaml 文件的路径
  • PROJECT_ID:您的项目 ID

定义

  • 变更器:帮助配置 Policy Controller 的变更行为的 Kubernetes 资源。
  • 系统:多个变更器的排列方式

变更示例

以下示例展示了一个变更器,该变更器将所有 Pod 中的所有容器的 imagePullPolicy 设置为 Always

# set-image-pull-policy.yaml

apiVersion: mutations.gatekeeper.sh/v1alpha1
kind: Assign
metadata:
  name: always-pull-image
spec:
  applyTo:
  - groups: [""]
    kinds: ["Pod"]
    versions: ["v1"]
  location: "spec.containers[name: *].imagePullPolicy"
  parameters:
    assign:
      value: "Always"

此示例中存在标准的 Kubernetes 元数据字段(apiVersionkindmetadata.name),但 spec 用于配置变更器的行为。

spec.applyTo 将变更器绑定到指定的资源。因为我们要更改对象中的特定字段,所以会隐式定义该对象的架构。例如,如果当前变更器已应用于 Namespace 资源,则毫无意义。因此,此字段是必填字段,因此 Policy Controller 知道此变更器与哪些架构相关。此列表中缺少的 GroupVersionKinds 不会发生变化。

spec.location 表示要修改哪个字段。在本例中,我们使用 glob (*) 指明需要修改容器列表中的所有条目。请注意,location 字段可能需要遍历的列表种类只有映射类型列表,并且必须指定映射的键字段。映射类型列表是一种 Kubernetes 构造。如需了解详情,请参阅 Kubernetes 的文档

spec.parameters.assign.value 是分配给 location 的值。此字段是非类型化的,可以采用任何值,但请注意,Kubernetes 仍会验证所请求的更改操作,因此,对于要修改的对象,插入架构错误的值会导致请求被拒绝。

为作业使用变更器

如果要配置作业或 CronJob,您必须分别指定版本和组,如以下示例所示:

applyTo:
- groups: ["batch"]
  kind: ["Job"]
  versions: ["v1"]

为作业使用变更器时,除非修改了现有作业,否则它们不会更改。修改作业会触发对变更网络钩子的请求,并导致其变更。

执行流程

要了解 Kubernetes 变更网络钩子,最重要的概念可能是其调用政策,因为一个变更器的输出可能会改变另一个变更器的行为方式。例如,如果您添加了新的边车,则用于为所有容器设置映像拉取政策的变更器现在会包含一个新容器。

实际上,这意味着对于任何给定请求,Policy Controller 的变更网络钩子都可能会被调用多次。

为了缩短延迟时间,Policy Controller 会重新调用自身来避免额外的 HTTP 请求。这意味着 Sidecar 注入和映像拉取政策会产生预期结果。

Policy Controller 的变更例程将继续重新调用,直到资源“收敛”,这意味着额外的迭代不会产生进一步的影响。

位置语法

位置语法使用点 (.) 访问函数遍历字段。对于键控列表,用户可以使用语法 [<key>: <value>] 引用列表中的各个对象,也可以使用 [<key>: *] 引用列表中的所有对象。

值和字段可以用单引号 (') 或双引号 (") 括起来。如果其包含特殊字符(如点或空格),则必须执行此操作。

在带英文引号的值中,可通过在前缀前添加 \ 来转义特殊字符。"Use \" to escape and \\\" to escape" 变为 Use " to escape and \" to escape

下面是 v1/Pod 资源的部分示例:

  • spec.priority 应用 spec.priority
  • spec.containers[name: "foo"].volumeMounts[mountPath: "/my/mount"].readOnly 引用 foo 容器的 /my/mount 装载的 readOnly 字段。
  • spec.containers[name: *].volumeMounts[mountPath: "/my/mount"].readOnly 引用所有容器的 /my/mount 装载的 readOnly 字段。

如果您引用资源上当前不存在的位置,系统会默认创建该位置。此行为可通过路径测试进行配置。

路径测试

如何执行默认操作,避免修改已经存在的值?也许我们想将所有容器的 /secure-mount 设置为只读,但如果 /secure-mount 尚不存在,我们不想创建它。我们可以通过路径测试执行这两项操作。

这是一个说明如何避免改变已经设置的 imagePullPolicy 的示例:

# set-image-pull-policy.yaml

apiVersion: mutations.gatekeeper.sh/v1alpha1
kind: Assign
metadata:
  name: always-pull-image
spec:
  applyTo:
  - groups: [""]
    kinds: ["Pod"]
    versions: ["v1"]
  location: "spec.containers[name: *].imagePullPolicy"
  parameters:
    assign:
      value: "Always"
    pathTests:
    - subPath: "spec.containers[name: *].imagePullPolicy"
      condition: "MustNotExist"

下面是避免创建空 sidecar 容器(如果尚不存在)的另一个示例:

# set-image-pull-policy.yaml

apiVersion: mutations.gatekeeper.sh/v1alpha1
kind: Assign
metadata:
  name: always-pull-image
spec:
  applyTo:
  - groups: [""]
    kinds: ["Pod"]
    versions: ["v1"]
  location: 'spec.containers[name: "sidecar"].imagePullPolicy'
  parameters:
    assign:
      value: "Always"
    pathTests:
    - subPath: 'spec.containers[name: "sidecar"]'
      condition: "MustExist"

如有必要,可以指定多个路径测试。

subPath 必须location 或者是其前缀。

condition 的唯一有效值是 MustExistMustNotExist

匹配

使用变更器时,也可以使用与限制条件相同的条件进行匹配。

变更器

目前有两种变更器:AssignAssignMetadata

分配

Assign 可以更改资源 metadata 字段以外的任何值。由于所有 GroupVersionKinds 都具有唯一的架构,因此必须绑定到一组特定的 GroupVersionKinds

它具有以下架构:

apiVersion: mutations.gatekeeper.sh/v1alpha1
kind: Assign
metadata:
  name: always-pull-image
spec:
  applyTo:
  - groups: [""]
    kinds: ["Pod"]
    versions: ["v1"]
  match:
    kinds: # redundant because of `applyTo`, but left in for consistency
      - apiGroups: ["*"]
        kinds: ["*"]
    namespaces: ["my-namespace"]
    scope: "Namespaced" # or "Cluster"
    excludedNamespaces: ["some-other-ns"]
    labelSelector:
      matchLabels:
        mutate: "yes"
      matchExpressions:
      - key: "my-label"
        operator: "In" # or, "NotIn", "Exists", or "DoesNotExist"
        values: ["my-value"]
    namespaceSelector:
      matchLabels:
        mutate: "yes"
      matchExpressions:
      - key: "my-label"
        operator: "In" # or, "NotIn", "Exists", or "DoesNotExist"
        values: ["my-value"]
  location: "spec.containers[name: *].imagePullPolicy"
  parameters:
    pathTests:
    - subPath: 'spec.containers[name: "sidecar"]' # must be a prefix of `location`
      condition: "MustExist" # or "MustNotExist"
    - subPath: "spec.containers[name: *].imagePullPolicy"
      condition: "MustNotExist"
    assign:
      value: "Always" # any type can go here, not just a string

AssignMetadata

AssignMetadata 可以添加新的元数据标签。而不能更改现有元数据标签的值。否则,可能会编写一个将无限期递归的变更器系统,从而导致请求超时。

由于所有资源共享相同的 metadata 架构,因此不需要指定 AssignMetadata 适用的资源。

此外,由于 AssignMetadata 无法执行相同的操作,因此其架构稍微简单一些。

apiVersion: mutations.gatekeeper.sh/v1alpha1
kind: AssignMetadata
metadata:
  name: set-team-name
spec:
  match:
    kinds:
      - apiGroups: ["*"]
        kinds: ["*"]
    namespaces: ["my-namespace"]
    scope: "Namespaced" # or "Cluster"
    excludedNamespaces: ["some-other-ns"]
    labelSelector:
      matchLabels:
        mutate: "yes"
      matchExpressions:
      - key: "my-label"
        operator: "In" # or, "NotIn", "Exists", or "DoesNotExist"
        values: ["my-value"]
    namespaceSelector:
      matchLabels:
        mutate: "yes"
      matchExpressions:
      - key: "my-label"
        operator: "In" # or, "NotIn", "Exists", or "DoesNotExist"
        values: ["my-value"]
  location: "metadata.labels.team" # must start with `metadata.labels`
  parameters:
    assign:
      value: "Always" # any type can go here, not just a string

最佳实践

Kubernetes 注意事项

Kubernetes 文档列出了关于使用变更网络钩子的一些重要注意事项。由于 Policy Controller 作为 Kubernetes 准入网络钩子运行,因此建议在此处适用。

Policy Controller 的变更语法旨在让您更轻松地满足有关变更网络钩子(包括幂等性)的运营问题

编写变更器

原子性

最佳做法是使每个变更器尽可能自给自足。由于 Kubernetes 具有最终一致性,因此一个变更器不应依赖第二个变更器才能正常工作。例如,添加边车时,请添加整个边车,而不是使用多个变更器来构建这些边车。

验证

强制执行某个条件时,最好设置变更器的匹配限制条件。这有助于确保在审核中检测到违规请求并拒绝预先存在的违规行为。

紧急恢复

变更是作为 Kubernetes 变更网络钩子实现的。可以按照与验证网络钩子相同的方式停止它,但相关资源是名为 gatekeeper-mutating-webhook-configurationMutatingWebhookConfiguration

后续步骤

  • 如需了解 Gatekeeper 变更说明和示例,请参阅开源文档