动态准入控制

除了内置的 admission 插件, 准入插件可以作为扩展独立开发,并以运行时所配置的 Webhook 的形式运行。 此页面描述了如何构建、配置、使用和监视准入 Webhook。

什么是准入 Webhook?

准入 Webhook 是一种用于接收准入请求并对其进行处理的 HTTP 回调机制。 可以定义两种类型的准入 webhook,即 验证性质的准入 Webhook修改性质的准入 Webhook。 修改性质的准入 Webhook 会先被调用。它们可以更改发送到 API 服务器的对象以执行自定义的设置默认值操作。

在完成了所有对象修改并且 API 服务器也验证了所传入的对象之后, 验证性质的 Webhook 会被调用,并通过拒绝请求的方式来强制实施自定义的策略。

说明: 如果准入 Webhook 需要保证它们所看到的是对象的最终状态以实施某种策略。 则应使用验证性质的准入 Webhook,因为对象被修改性质 Webhook 看到之后仍然可能被修改。

尝试准入 Webhook

准入 Webhook 本质上是集群控制平面的一部分。你应该非常谨慎地编写和部署它们。 如果你打算编写或者部署生产级准入 webhook,请阅读用户指南以获取相关说明。 在下文中,我们将介绍如何快速试验准入 Webhook。

先决条件

  • 确保 Kubernetes 集群版本至少为 v1.16(以便使用 admissionregistration.k8s.io/v1 API) 或者 v1.9 (以便使用 admissionregistration.k8s.io/v1beta1 API)。

  • 确保启用 MutatingAdmissionWebhook 和 ValidatingAdmissionWebhook 控制器。 这里 是一组推荐的 admission 控制器,通常可以启用。

  • 确保启用了 admissionregistration.k8s.io/v1beta1 API。

编写一个准入 Webhook 服务器

请参阅 Kubernetes e2e 测试中的 admission webhook 服务器 的实现。webhook 处理由 apiserver 发送的 AdmissionReview 请求,并且将其决定作为 AdmissionReview 对象以相同版本发送回去。

有关发送到 webhook 的数据的详细信息,请参阅 webhook 请求

要获取来自 webhook 的预期数据,请参阅 webhook 响应

示例准入 Webhook 服务器置 ClientAuth 字段为,默认为 NoClientCert 。这意味着 webhook 服务器不会验证客户端的身份,认为其是 apiservers。 如果你需要双向 TLS 或其他方式来验证客户端,请参阅如何对 apiservers 进行身份认证

部署准入 Webhook 服务

e2e 测试中的 webhook 服务器通过 deployment API 部署在 Kubernetes 集群中。该测试还将创建一个 service 作为 webhook 服务器的前端。参见相关代码

你也可以在集群外部署 webhook。这样做需要相应地更新你的 webhook 配置。

即时配置准入 Webhook

你可以通过 ValidatingWebhookConfiguration 或者 MutatingWebhookConfiguration 动态配置哪些资源要被哪些准入 Webhook 处理。

以下是一个 ValidatingWebhookConfiguration 示例,mutating webhook 配置与此类似。有关每个配置字段的详细信息,请参阅 webhook 配置 部分。

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  name: "pod-policy.example.com"
webhooks:
- name: "pod-policy.example.com"
  rules:
  - apiGroups:   [""]
    apiVersions: ["v1"]
    operations:  ["CREATE"]
    resources:   ["pods"]
    scope:       "Namespaced"
  clientConfig:
    service:
      namespace: "example-namespace"
      name: "example-service"
    caBundle: "Ci0tLS0tQk...<base64-encoded PEM bundle containing the CA that signed the webhook's serving certificate>...tLS0K"
  admissionReviewVersions: ["v1", "v1beta1"]
  sideEffects: None
  timeoutSeconds: 5

# 1.16 中被淘汰,推荐使用 admissionregistration.k8s.io/v1
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingWebhookConfiguration
metadata:
  name: "pod-policy.example.com"
webhooks:
- name: "pod-policy.example.com"
  rules:
  - apiGroups:   [""]
    apiVersions: ["v1"]
    operations:  ["CREATE"]
    resources:   ["pods"]
    scope:       "Namespaced"
  clientConfig:
    service:
      namespace: "example-namespace"
      name: "example-service"
    caBundle: "Ci0tLS0tQk...<base64-encoded PEM bundle containing the CA that signed the webhook's serving certificate>...tLS0K"
  admissionReviewVersions: ["v1beta1"]
  timeoutSeconds: 5

scope 字段指定是仅集群范围的资源(Cluster)还是名字空间范围的资源资源(Namespaced)将与此规则匹配。* 表示没有范围限制。

说明: 当使用 clientConfig.service 时,服务器证书必须对 <svc_name>.<svc_namespace>.svc 有效。
说明: 对于使用 admissionregistration.k8s.io/v1 创建的 webhook 而言,其 webhook 调用的默认超时是 10 秒; 对于使用 admissionregistration.k8s.io/v1beta1 创建的 webhook 而言,其默认超时是 30 秒。 从 kubernetes 1.14 开始,可以设置超时。建议对 webhooks 设置较短的超时时间。 如果 webhook 调用超时,则根据 webhook 的失败策略处理请求。

当 apiserver 收到与 rules 相匹配的请求时,apiserver 按照 clientConfig 中指定的方式向 webhook 发送一个 admissionReview 请求。

创建 webhook 配置后,系统将花费几秒钟使新配置生效。

对 apiservers 进行身份认证

如果你的 webhook 需要身份验证,则可以将 apiserver 配置为使用基本身份验证、持有者令牌或证书来向 webhook 提供身份证明。完成此配置需要三个步骤。

  • 启动 apiserver 时,通过 --admission-control-config-file 参数指定准入控制配置文件的位置。

  • 在准入控制配置文件中,指定 MutatingAdmissionWebhook 控制器和 ValidatingAdmissionWebhook 控制器应该读取凭据的位置。 凭证存储在 kubeConfig 文件中(是​​的,与 kubectl 使用的模式相同),因此字段名称为 kubeConfigFile。 以下是一个准入控制配置文件示例:

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: ValidatingAdmissionWebhook
  configuration:
    apiVersion: apiserver.config.k8s.io/v1
    kind: WebhookAdmissionConfiguration
    kubeConfigFile: "<path-to-kubeconfig-file>"
- name: MutatingAdmissionWebhook
  configuration:
    apiVersion: apiserver.config.k8s.io/v1
    kind: WebhookAdmissionConfiguration
    kubeConfigFile: "<path-to-kubeconfig-file>"

# 1.17 中被淘汰,推荐使用 apiserver.config.k8s.io/v1
apiVersion: apiserver.k8s.io/v1alpha1
kind: AdmissionConfiguration
plugins:
- name: ValidatingAdmissionWebhook
  configuration:
    # 1.17 中被淘汰,推荐使用 apiserver.config.k8s.io/v1,kind = WebhookAdmissionConfiguration
    apiVersion: apiserver.config.k8s.io/v1alpha1
    kind: WebhookAdmission
    kubeConfigFile: "<path-to-kubeconfig-file>"
- name: MutatingAdmissionWebhook
  configuration:
    # 1.17 中被淘汰,推荐使用 apiserver.config.k8s.io/v1,kind = WebhookAdmissionConfiguration
    apiVersion: apiserver.config.k8s.io/v1alpha1
    kind: WebhookAdmission
    kubeConfigFile: "<path-to-kubeconfig-file>"

有关 AdmissionConfiguration 的更多信息,请参见 AdmissionConfiguration schema。 有关每个配置字段的详细信息,请参见 webhook 配置部分。

  • 在 kubeConfig 文件中,提供证书凭据:

    apiVersion: v1
    kind: Config
    users:
    # 名称应设置为服务的 DNS 名称或配置了 Webhook 的 URL 的主机名(包括端口)。
    # 如果将非 443 端口用于服务,则在配置 1.16+ API 服务器时,该端口必须包含在名称中。
    #
    # 对于配置在默认端口(443)上与服务对话的 Webhook,请指定服务的 DNS 名称:
    # - name: webhook1.ns1.svc
    #   user: ...
    #
    # 对于配置在非默认端口(例如 8443)上与服务对话的 Webhook,请在 1.16+ 中指定服务的 DNS 名称和端口:
    # - name: webhook1.ns1.svc:8443
    #   user: ...
    # 并可以选择仅使用服务的 DNS 名称来创建第二节,以与 1.15 API 服务器版本兼容:
    # - name: webhook1.ns1.svc
    #   user: ...
    #
    # 对于配置为使用 URL 的 webhook,请匹配在 webhook 的 URL 中指定的主机(和端口)。
    # 带有 `url: https://www.example.com` 的 webhook:
    # - name: www.example.com
    #   user: ...
    #
    # 带有 `url: https://www.example.com:443` 的 webhook:
    # - name: www.example.com:443
    #   user: ...
    #
    # 带有 `url: https://www.example.com:8443` 的 webhook:
    # - name: www.example.com:8443
    #   user: ...
    #
    - name: 'webhook1.ns1.svc'
      user:
        client-certificate-data: "<pem encoded certificate>"
        client-key-data: "<pem encoded key>"
    # `name` 支持使用 * 通配符匹配前缀段。
    - name: '*.webhook-company.org'
      user:
        password: "<password>"
        username: "<name>"
    # '*' 是默认匹配项。
    - name: '*'
      user:
        token: "<token>"
    

当然,你需要设置 webhook 服务器来处理这些身份验证。

请求

向 Webhook 发送 POST 请求时,请设置 Content-Type: application/json 并对 admission.k8s.io API 组中的 AdmissionReview 对象进行序列化,将所得到的 JSON 作为请求的主体。

Webhook 可以在配置中的 admissionReviewVersions 字段指定可接受的 AdmissionReview 对象版本:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
...
webhooks:
- name: my-webhook.example.com
  admissionReviewVersions: ["v1", "v1beta1"]
  ...

创建 admissionregistration.k8s.io/v1 webhook 配置时,admissionReviewVersions 是必填字段。 Webhook 必须支持至少一个当前和以前的 apiserver 都可以解析的 AdmissionReview 版本。

# v1.16 中被淘汰,推荐使用 admissionregistration.k8s.io/v1
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingWebhookConfiguration
...
webhooks:
- name: my-webhook.example.com
  admissionReviewVersions: ["v1beta1"]
  ...

如果未指定 admissionReviewVersions,则创建 admissionregistration.k8s.io/v1beta1 Webhook 配置时的默认值为 v1beta1

API 服务器将发送的是 admissionReviewVersions 列表中所支持的第一个 AdmissionReview 版本。如果 API 服务器不支持列表中的任何版本,则不允许创建配置。

如果 API 服务器遇到以前创建的 Webhook 配置,并且不支持该 API 服务器知道如何发送的任何 AdmissionReview 版本,则调用 Webhook 的尝试将失败,并依据失败策略进行处理。

此示例显示了 AdmissionReview 对象中包含的数据,该数据用于请求更新 apps/v1 Deploymentscale 子资源:

{
  "apiVersion": "admission.k8s.io/v1",
  "kind": "AdmissionReview",
  "request": {
    # 唯一标识此准入回调的随机 uid
    "uid": "705ab4f5-6393-11e8-b7cc-42010a800002",

    # 传入完全正确的 group/version/kind 对象
    "kind": {"group":"autoscaling","version":"v1","kind":"Scale"},
    # 修改 resource 的完全正确的的 group/version/kind
    "resource": {"group":"apps","version":"v1","resource":"deployments"},
    # subResource(如果请求是针对 subResource 的)
    "subResource": "scale",

    # 在对 API 服务器的原始请求中,传入对象的标准 group/version/kind
    # 仅当 webhook 指定 `matchPolicy: Equivalent` 且将对 API 服务器的原始请求转换为 webhook 注册的版本时,这才与 `kind` 不同。
    "requestKind": {"group":"autoscaling","version":"v1","kind":"Scale"},
    # 在对 API 服务器的原始请求中正在修改的资源的标准 group/version/kind
    # 仅当 webhook 指定了 `matchPolicy:Equivalent` 并且将对 API 服务器的原始请求转换为 webhook 注册的版本时,这才与 `resource` 不同。
    "requestResource": {"group":"apps","version":"v1","resource":"deployments"},
    # subResource(如果请求是针对 subResource 的)
    # 仅当 webhook 指定了 `matchPolicy:Equivalent` 并且将对 API 服务器的原始请求转换为该 webhook 注册的版本时,这才与 `subResource` 不同。
    "requestSubResource": "scale",

    # 被修改资源的名称
    "name": "my-deployment",
    # 如果资源是属于名字空间(或者是名字空间对象),则这是被修改的资源的名字空间
    "namespace": "my-namespace",

    # 操作可以是 CREATE、UPDATE、DELETE 或 CONNECT
    "operation": "UPDATE",

    "userInfo": {
      # 向 API 服务器发出请求的经过身份验证的用户的用户名
      "username": "admin",
      # 向 API 服务器发出请求的经过身份验证的用户的 UID
      "uid": "014fbff9a07c",
      # 向 API 服务器发出请求的经过身份验证的用户的组成员身份
      "groups": ["system:authenticated","my-admin-group"],
      # 向 API 服务器发出请求的用户相关的任意附加信息
      # 该字段由 API 服务器身份验证层填充,并且如果 webhook 执行了任何 SubjectAccessReview 检查,则应将其包括在内。
      "extra": {
        "some-key":["some-value1", "some-value2"]
      }
    },

    # object 是被接纳的新对象。
    # 对于 DELETE 操作,它为 null。
    "object": {"apiVersion":"autoscaling/v1","kind":"Scale",...},
    # oldObject 是现有对象。
    # 对于 CREATE 和 CONNECT 操作,它为 null。
    "oldObject": {"apiVersion":"autoscaling/v1","kind":"Scale",...},
    # options 包含要接受的操作的选项,例如 meta.k8s.io/v CreateOptions、UpdateOptions 或 DeleteOptions。
    # 对于 CONNECT 操作,它为 null。
    "options": {"apiVersion":"meta.k8s.io/v1","kind":"UpdateOptions",...},

    # dryRun 表示 API 请求正在以 `dryrun` 模式运行,并且将不会保留。
    # 带有副作用的 Webhook 应该避免在 dryRun 为 true 时激活这些副作用。
    # 有关更多详细信息,请参见 http://k8s.io/docs/reference/using-api/api-concepts/#make-a-dry-run-request
    "dryRun": false
  }
}

{
  # v1.16 中被废弃,推荐使用 admission.k8s.io/v1
  "apiVersion": "admission.k8s.io/v1beta1",
  "kind": "AdmissionReview",
  "request": {
    # 唯一标识此准入回调的随机 uid
    "uid": "705ab4f5-6393-11e8-b7cc-42010a800002",

    # 传入完全正确的 group/version/kind 对象
    "kind": {"group":"autoscaling","version":"v1","kind":"Scale"},
    # 修改 resource 的完全正确的的 group/version/kind
    "resource": {"group":"apps","version":"v1","resource":"deployments"},
    # subResource(如果请求是针对 subResource 的)
    "subResource": "scale",

    # 在对 API 服务器的原始请求中,传入对象的标准 group/version/kind。
    # 仅当 Webhook 指定了 `matchPolicy:Equivalent` 并且将对 API 服务器的原始请求转换为该 Webhook 注册的版本时,这与 `kind` 不同。
    # 仅由 v1.15+ API 服务器发送。
    "requestKind": {"group":"autoscaling","version":"v1","kind":"Scale"},
    # 在对 API 服务器的原始请求中正在修改的资源的标准 group/version/kind
    # 仅当 webhook 指定了 `matchPolicy:Equivalent` 并且将对 API 服务器的原始请求转换为 webhook 注册的版本时,这才与 `resource` 不同。
    # 仅由 v1.15+ API 服务器发送。
    "requestResource": {"group":"apps","version":"v1","resource":"deployments"},
    # subResource(如果请求是针对 subResource 的)
    # 仅当 webhook 指定了 `matchPolicy:Equivalent` 并且将对 API 服务器的原始请求转换为该 webhook 注册的版本时,这才与 `subResource` 不同。
    # 仅由 v1.15+ API 服务器发送。
    "requestSubResource": "scale",

    # 被修改资源的名称
    "name": "my-deployment",
    # 如果资源是属于名字空间(或者是名字空间对象),则这是被修改的资源的名字空间
    "namespace": "my-namespace",

    # 操作可以是 CREATE、UPDATE、DELETE 或 CONNECT
    "operation": "UPDATE",

    "userInfo": {
      # 向 API 服务器发出请求的经过身份验证的用户的用户名
      "username": "admin",
      # 向 API 服务器发出请求的经过身份验证的用户的 UID
      "uid": "014fbff9a07c",
      # 向 API 服务器发出请求的经过身份验证的用户的组成员身份
      "groups": ["system:authenticated","my-admin-group"],
      # 向 API 服务器发出请求的用户相关的任意附加信息
      # 该字段由 API 服务器身份验证层填充,并且如果 webhook 执行了任何 SubjectAccessReview 检查,则应将其包括在内。
      "extra": {
        "some-key":["some-value1", "some-value2"]
      }
    },

    # object 是被接纳的新对象。
    # 对于 DELETE 操作,它为 null。
    "object": {"apiVersion":"autoscaling/v1","kind":"Scale",...},
    # oldObject 是现有对象。
    # 对于 CREATE 和 CONNECT 操作(对于 v1.15.0 之前版本的 API 服务器中的 DELETE 操作),它为 null。
    "oldObject": {"apiVersion":"autoscaling/v1","kind":"Scale",...},
    # options 包含要接受的操作的选项,例如 meta.k8s.io/v CreateOptions、UpdateOptions 或 DeleteOptions。
    # 对于 CONNECT 操作,它为 null。
    # 仅由 v1.15+ API 服务器发送。
    "options": {"apiVersion":"meta.k8s.io/v1","kind":"UpdateOptions",...},

    # dryRun 表示 API 请求正在以 `dryrun` 模式运行,并且将不会保留。
    # 带有副作用的 Webhook 应该避免在 dryRun 为 true 时激活这些副作用。
    # 有关更多详细信息,请参见 http://k8s.io/docs/reference/using-api/api-concepts/#make-a-dry-run-request
    "dryRun": false
  }
}

响应

Webhook 使用 HTTP 200 状态码、Content-Type: application/json 和一个包含 AdmissionReview 对象的 JSON 序列化格式来发送响应。该 AdmissionReview 对象与发送的版本相同,且其中包含的 response 字段已被有效填充。

response 至少必须包含以下字段:

  • uid,从发送到 webhook 的 request.uid 中复制而来
  • allowed,设置为 truefalse

Webhook 允许请求的最简单响应示例:

{
  "apiVersion": "admission.k8s.io/v1",
  "kind": "AdmissionReview",
  "response": {
    "uid": "<value from request.uid>",
    "allowed": true
  }
}

{
  "apiVersion": "admission.k8s.io/v1beta1",
  "kind": "AdmissionReview",
  "response": {
    "uid": "<value from request.uid>",
    "allowed": true
  }
}

Webhook 禁止请求的最简单响应示例:

{
  "apiVersion": "admission.k8s.io/v1",
  "kind": "AdmissionReview",
  "response": {
    "uid": "<value from request.uid>",
    "allowed": false
  }
}

{
  "apiVersion": "admission.k8s.io/v1beta1",
  "kind": "AdmissionReview",
  "response": {
    "uid": "<value from request.uid>",
    "allowed": false
  }
}

当拒绝请求时,Webhook 可以使用 status 字段自定义 http 响应码和返回给用户的消息。 有关状态类型的详细信息,请参见 API 文档。 禁止请求的响应示例,它定制了向用户显示的 HTTP 状态码和消息:

{
  "apiVersion": "admission.k8s.io/v1",
  "kind": "AdmissionReview",
  "response": {
    "uid": "<value from request.uid>",
    "allowed": false,
    "status": {
      "code": 403,
      "message": "You cannot do this because it is Tuesday and your name starts with A"
    }
  }
}

{
  "apiVersion": "admission.k8s.io/v1beta1",
  "kind": "AdmissionReview",
  "response": {
    "uid": "<value from request.uid>",
    "allowed": false,
    "status": {
      "code": 403,
      "message": "You cannot do this because it is Tuesday and your name starts with A"
    }
  }
}

当允许请求时,mutating准入 Webhook 也可以选择修改传入的对象。 这是通过在响应中使用 patchpatchType 字段来完成的。 当前唯一支持的 patchTypeJSONPatch。 有关更多详细信息,请参见 JSON patch。 对于 patchType: JSONPatchpatch 字段包含一个以 base64 编码的 JSON patch 操作数组。

例如,设置 spec.replicas 的单个补丁操作将是 [{"op": "add", "path": "/spec/replicas", "value": 3}]

如果以 Base64 形式编码,结果将是 W3sib3AiOiAiYWRkIiwgInBhdGgiOiAiL3NwZWMvcmVwbGljYXMiLCAidmFsdWUiOiAzfV0=

因此,添加该标签的 webhook 响应为:

{
  "apiVersion": "admission.k8s.io/v1",
  "kind": "AdmissionReview",
  "response": {
    "uid": "<value from request.uid>",
    "allowed": true,
    "patchType": "JSONPatch",
    "patch": "W3sib3AiOiAiYWRkIiwgInBhdGgiOiAiL3NwZWMvcmVwbGljYXMiLCAidmFsdWUiOiAzfV0="
  }
}

{
  "apiVersion": "admission.k8s.io/v1beta1",
  "kind": "AdmissionReview",
  "response": {
    "uid": "<value from request.uid>",
    "allowed": true,
    "patchType": "JSONPatch",
    "patch": "W3sib3AiOiAiYWRkIiwgInBhdGgiOiAiL3NwZWMvcmVwbGljYXMiLCAidmFsdWUiOiAzfV0="
  }
}

Webhook 配置

要注册准入 Webhook,请创建 MutatingWebhookConfigurationValidatingWebhookConfiguration API 对象。

每种配置可以包含一个或多个 Webhook。如果在单个配置中指定了多个 Webhook,则应为每个 webhook 赋予一个唯一的名称。 这在 admissionregistration.k8s.io/v1 中是必需的,但是在使用 admissionregistration.k8s.io/v1beta1 时强烈建议使用, 以使生成的审核日志和指标更易于与活动配置相匹配。

每个 Webhook 定义以下内容。

匹配请求-规则

每个 webhook 必须指定用于确定是否应将对 apiserver 的请求发送到 webhook 的规则列表。 每个规则都指定一个或多个 operations、apiGroups、apiVersions 和 resources 以及资源的 scope:

  • operations 列出一个或多个要匹配的操作。 可以是 CREATEUPDATEDELETECONNECT* 以匹配所有内容。
  • apiGroups 列出了一个或多个要匹配的 API 组。"" 是核心 API 组。"*" 匹配所有 API 组。
  • apiVersions 列出了一个或多个要匹配的 API 版本。"*" 匹配所有 API 版本。
  • resources 列出了一个或多个要匹配的资源。
    • "*" 匹配所有资源,但不包括子资源。
    • "*/*" 匹配所有资源,包括子资源。
    • "pods/*" 匹配 pod 的所有子资源。
    • "*/status" 匹配所有 status 子资源。
  • scope 指定要匹配的范围。有效值为 "Cluster""Namespaced""*"。 子资源匹配其父资源的范围。在 Kubernetes v1.14+ 版本中才被支持。 默认值为 "*",对应 1.14 版本之前的行为。
    • "Cluster" 表示只有集群作用域的资源才能匹配此规则(API 对象 Namespace 是集群作用域的)。
    • "Namespaced" 意味着仅具有名字空间的资源才符合此规则。
    • "*" 表示没有范围限制。

如果传入请求与任何 Webhook 规则的指定操作、组、版本、资源和范围匹配,则该请求将发送到 Webhook。

以下是可用于指定应拦截哪些资源的规则的其他示例。

匹配针对 apps/v1apps/v1beta1 组中 deploymentsreplicasets 资源的 CREATEUPDATE 请求:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
...
webhooks:
- name: my-webhook.example.com
  rules:
  - operations: ["CREATE", "UPDATE"]
    apiGroups: ["apps"]
    apiVersions: ["v1", "v1beta1"]
    resources: ["deployments", "replicasets"]
    scope: "Namespaced"
  ...

# v1.16 中被废弃,推荐使用 admissionregistration.k8s.io/v1
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingWebhookConfiguration
...
webhooks:
- name: my-webhook.example.com
  rules:
  - operations: ["CREATE", "UPDATE"]
    apiGroups: ["apps"]
    apiVersions: ["v1", "v1beta1"]
    resources: ["deployments", "replicasets"]
    scope: "Namespaced"
  ...

匹配所有 API 组和版本中的所有资源(但不包括子资源)的创建请求:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
...
webhooks:
- name: my-webhook.example.com
  rules:
  - operations: ["CREATE"]
    apiGroups: ["*"]
    apiVersions: ["*"]
    resources: ["*"]
    scope: "*"
  ...

# v1.16 中被废弃,推荐使用 admissionregistration.k8s.io/v1
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingWebhookConfiguration
...
webhooks:
- name: my-webhook.example.com
  rules:
  - operations: ["CREATE"]
    apiGroups: ["*"]
    apiVersions: ["*"]
    resources: ["*"]
    scope: "*"
  ...

匹配所有 API 组和版本中所有 status 子资源的更新请求:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
...
webhooks:
- name: my-webhook.example.com
  rules:
  - operations: ["CREATE"]
    apiGroups: ["*"]
    apiVersions: ["*"]
    resources: ["*"]
    scope: "*"
  ...

# v1.16 中被废弃,推荐使用 admissionregistration.k8s.io/v1
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingWebhookConfiguration
...
webhooks:
- name: my-webhook.example.com
  rules:
  - operations: ["CREATE"]
    apiGroups: ["*"]
    apiVersions: ["*"]
    resources: ["*"]
    scope: "*"
  ...

匹配请求:objectSelector

在版本 v1.15+ 中, 通过指定 objectSelector,Webhook 能够根据 可能发送的对象的标签来限制哪些请求被拦截。 如果指定,则将对 objectSelector 和可能发送到 Webhook 的 object 和 oldObject 进行评估。如果两个对象之一与选择器匹配,则认为该请求已匹配。

空对象(对于创建操作而言为 oldObject,对于删除操作而言为 newObject), 或不能带标签的对象(例如 DeploymentRollbackPodProxyOptions 对象) 被认为不匹配。

仅当选择使用 webhook 时才使用对象选择器,因为最终用户可以通过设置标签来 跳过准入 Webhook。

这个例子展示了一个 mutating webhook,它将匹配带有标签 foo:bar 的任何资源的 CREATE 的操作:

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
...
webhooks:
- name: my-webhook.example.com
  objectSelector:
    matchLabels:
      foo: bar
  rules:
  - operations: ["CREATE"]
    apiGroups: ["*"]
    apiVersions: ["*"]
    resources: ["*"]
    scope: "*"
  ...

# v1.16 中被废弃,推荐使用 admissionregistration.k8s.io/v1
apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
...
webhooks:
- name: my-webhook.example.com
  objectSelector:
    matchLabels:
      foo: bar
  rules:
  - operations: ["CREATE"]
    apiGroups: ["*"]
    apiVersions: ["*"]
    resources: ["*"]
    scope: "*"
  ...

有关标签选择器的更多示例,请参见标签

匹配请求:namespaceSelector

通过指定 namespaceSelector,Webhook 可以根据具有名字空间的资源所处的 名字空间的标签来选择拦截哪些资源的操作。

namespaceSelector 根据名字空间的标签是否匹配选择器,决定是否针对具名字空间的资源 (或 Namespace 对象)的请求运行 webhook。 如果对象是除 Namespace 以外的集群范围的资源,则 namespaceSelector 标签无效。

本例给出的修改性质的 Webhook 将匹配到对名字空间中具名字空间的资源的 CREATE 请求, 前提是这些资源不含值为 "0" 或 "1" 的 "runlevel" 标签:

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
...
webhooks:
- name: my-webhook.example.com
  namespaceSelector:
    matchExpressions:
    - key: runlevel
      operator: NotIn
      values: ["0","1"]
  rules:
  - operations: ["CREATE"]
    apiGroups: ["*"]
    apiVersions: ["*"]
    resources: ["*"]
    scope: "Namespaced"
  ...

# v1.16 中被废弃,推荐使用 admissionregistration.k8s.io/v1
apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
...
webhooks:
- name: my-webhook.example.com
  namespaceSelector:
    matchExpressions:
    - key: runlevel
      operator: NotIn
      values: ["0","1"]
  rules:
  - operations: ["CREATE"]
    apiGroups: ["*"]
    apiVersions: ["*"]
    resources: ["*"]
    scope: "Namespaced"
  ...

此示例显示了一个验证性质的 Webhook,它将匹配到对某名字空间中的任何具名字空间的资源的 CREATE 请求,前提是该名字空间具有值为 "prod" 或 "staging" 的 "environment" 标签:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
...
webhooks:
- name: my-webhook.example.com
  namespaceSelector:
    matchExpressions:
    - key: environment
      operator: In
      values: ["prod","staging"]
  rules:
  - operations: ["CREATE"]
    apiGroups: ["*"]
    apiVersions: ["*"]
    resources: ["*"]
    scope: "Namespaced"
  ...

# v1.16 中被废弃,推荐使用 admissionregistration.k8s.io/v1
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingWebhookConfiguration
...
webhooks:
- name: my-webhook.example.com
  namespaceSelector:
    matchExpressions:
    - key: environment
      operator: In
      values: ["prod","staging"]
  rules:
  - operations: ["CREATE"]
    apiGroups: ["*"]
    apiVersions: ["*"]
    resources: ["*"]
    scope: "Namespaced"
  ...

有关标签选择器的更多示例,请参见 标签

匹配请求:matchPolicy

API 服务器可以通过多个 API 组或版本来提供对象。 例如,Kubernetes API 服务器允许通过 extensions/v1beta1apps/v1beta1apps/v1beta2apps/v1 API 创建和修改 Deployment 对象。

例如,如果一个 webhook 仅为某些 API 组/版本指定了规则(例如 apiGroups:["apps"], apiVersions:["v1","v1beta1"]),而修改资源的请求 是通过另一个 API 组/版本(例如 extensions/v1beta1)发出的, 该请求将不会被发送到 Webhook。

在 v1.15+ 中,matchPolicy 允许 webhook 定义如何使用其 rules 匹配传入的请求。 允许的值为 ExactEquivalent

  • Exact 表示仅当请求与指定规则完全匹配时才应拦截该请求。
  • Equivalent 表示如果某个请求意在修改 rules 中列出的资源, 即使该请求是通过其他 API 组或版本发起,也应拦截该请求。

在上面给出的示例中,仅为 apps/v1 注册的 webhook 可以使用 matchPolicy

  • matchPolicy: Exact 表示不会将 extensions/v1beta1 请求发送到 Webhook
  • matchPolicy:Equivalent 表示将 extensions/v1beta1 请求发送到 webhook (将对象转换为 webhook 指定的版本:apps/v1

建议指定 Equivalent,确保升级后启用 API 服务器中资源的新版本时, Webhook 继续拦截他们期望的资源。

当 API 服务器停止提供某资源时,该资源不再被视为等同于该资源的其他仍在提供服务的版本。 例如,extensions/v1beta1 中的 Deployment 已被废弃,计划在 v1.16 中默认停止使用。 在这种情况下,带有 apiGroups:["extensions"], apiVersions:["v1beta1"], resources: ["deployments"] 规则的 Webhook 将不再拦截通过 apps/v1 API 来创建 Deployment 的请求。 ["deployments"] 规则将不再拦截通过 apps/v1 API 创建的部署。

此示例显示了一个验证性质的 Webhook,该 Webhook 拦截对 Deployment 的修改(无论 API 组或版本是什么), 始终会发送一个 apps/v1 版本的 Deployment 对象:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
...
webhooks:
- name: my-webhook.example.com
  matchPolicy: Equivalent
  rules:
  - operations: ["CREATE","UPDATE","DELETE"]
    apiGroups: ["apps"]
    apiVersions: ["v1"]
    resources: ["deployments"]
    scope: "Namespaced"
  ...

使用 admissionregistration.k8s.io/v1 创建的 admission webhhok 默认为 Equivalent

# v1.16 中被废弃,推荐使用 admissionregistration.k8s.io/v1
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingWebhookConfiguration
...
webhooks:
- name: my-webhook.example.com
  matchPolicy: Equivalent
  rules:
  - operations: ["CREATE","UPDATE","DELETE"]
    apiGroups: ["apps"]
    apiVersions: ["v1"]
    resources: ["deployments"]
    scope: "Namespaced"
  ...

使用 admissionregistration.k8s.io/v1beta1 创建的准入 Webhook 默认为 Exact

调用 Webhook

API 服务器确定请求应发送到 webhook 后,它需要知道如何调用 webhook。 此信息在 webhook 配置的 clientConfig 节中指定。

Webhook 可以通过 URL 或服务引用来调用,并且可以选择包含自定义 CA 包,以用于验证 TLS 连接。

URL

url 以标准 URL 形式给出 webhook 的位置(scheme://host:port/path)。

host 不应引用集群中运行的服务;通过指定 service 字段来使用服务引用。 主机可以通过某些 apiserver 中的外部 DNS 进行解析。 (例如,kube-apiserver 无法解析集群内 DNS,因为这将违反分层规则)。host 也可以是 IP 地址。

请注意,将 localhost127.0.0.1 用作 host 是有风险的, 除非你非常小心地在所有运行 apiserver 的、可能需要对此 webhook 进行调用的主机上运行。这样的安装可能不具有可移植性,即很难在新集群中启用。

scheme 必须为 "https";URL 必须以 "https://" 开头。

使用用户或基本身份验证(例如:"user:password@")是不允许的。 使用片段("#...")和查询参数("?...")也是不允许的。

这是配置为调用 URL 的修改性质的 Webhook 的示例 (并且期望使用系统信任根证书来验证 TLS 证书,因此不指定 caBundle):

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
...
webhooks:
- name: my-webhook.example.com
  clientConfig:
    url: "https://my-webhook.example.com:9443/my-webhook-path"
  ...

# v1.16 中被废弃,推荐使用 admissionregistration.k8s.io/v1
apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
...
webhooks:
- name: my-webhook.example.com
  clientConfig:
    url: "https://my-webhook.example.com:9443/my-webhook-path"
  ...

服务引用

clientConfig 内部的 Service 是对该 Webhook 服务的引用。 如果 Webhook 在集群中运行,则应使用 service 而不是 url。 服务的 namespacename 是必需的。 port 是可选的,默认值为 443。path 是可选的,默认为 "/"。

这是一个 mutating Webhook 的示例,该 mutating Webhook 配置为在子路径 "/my-path" 端口 "1234" 上调用服务,并使用自定义 CA 包针对 ServerName my-service-name.my-service-namespace.svc 验证 TLS 连接:

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
...
webhooks:
- name: my-webhook.example.com
  clientConfig:
    caBundle: "Ci0tLS0tQk...<base64-encoded PEM bundle containing the CA that signed the webhook's serving certificate>...tLS0K"
    service:
      namespace: my-service-namespace
      name: my-service-name
      path: /my-path
      port: 1234
  ...

# v1.16 中被废弃,推荐使用 admissionregistration.k8s.io/v1
apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
...
webhooks:
- name: my-webhook.example.com
  clientConfig:
    caBundle: "Ci0tLS0tQk...<base64-encoded PEM bundle containing the CA that signed the webhook's serving certificate>...tLS0K"
    service:
      namespace: my-service-namespace
      name: my-service-name
      path: /my-path
      port: 1234
  ...

副作用

Webhook 通常仅对发送给他们的 AdmissionReview 内容进行操作。 但是,某些 Webhook 在处理 admission 请求时会进行带外更改。

进行带外更改的(产生“副作用”的) Webhook 必须具有协调机制(如控制器), 该机制定期确定事物的实际状态,并调整由准入 Webhook 修改的带外数据以反映现实情况。 这是因为对准入 Webhook 的调用不能保证所准入的对象将原样保留,或根本不保留。 以后,webhook 可以修改对象的内容,在写入存储时可能会发生冲突,或者 服务器可以在持久保存对象之前关闭电源。

此外,处理 dryRun: true admission 请求时,具有副作用的 Webhook 必须避免产生副作用。 一个 Webhook 必须明确指出在使用 dryRun 运行时不会有副作用, 否则 dry-run 请求将不会发送到该 Webhook,而 API 请求将会失败。

Webhook 使用 webhook 配置中的 sideEffects 字段显示它们是否有副作用:

  • Unknown:有关调用 Webhook 的副作用的信息是不可知的。 如果带有 dryRun:true 的请求将触发对该 Webhook 的调用,则该请求将失败,并且不会调用该 Webhook。
  • None:调用 webhook 没有副作用。
  • Some:调用 webhook 可能会有副作用。 如果请求具有 dry-run 属性将触发对此 Webhook 的调用, 则该请求将会失败,并且不会调用该 Webhook。
  • NoneOnDryRun:调用 webhook 可能会有副作用,但是如果将带有 dryRun: true 属性的请求发送到 webhook,则 webhook 将抑制副作用(该 webhook 可识别 dryRun)。

允许值:

  • admissionregistration.k8s.io/v1beta1 中,sideEffects 可以设置为 UnknownNoneSome 或者 NoneOnDryRun,并且默认值为 Unknown
  • admissionregistration.k8s.io/v1 中, sideEffects 必须设置为 None 或者 NoneOnDryRun

这是一个 validating webhook 的示例,表明它对 dryRun: true 请求没有副作用:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
...
webhooks:
- name: my-webhook.example.com
  sideEffects: NoneOnDryRun
  ...

# v1.16 中被废弃,推荐使用 admissionregistration.k8s.io/v1
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingWebhookConfiguration
...
webhooks:
- name: my-webhook.example.com
  sideEffects: NoneOnDryRun
  ...

超时

由于 Webhook 会增加 API 请求的延迟,因此应尽快完成自身的操作。 timeoutSeconds 用来配置在将调用视为失败之前,允许 API 服务器等待 Webhook 响应的时间长度。

如果超时在 Webhook 响应之前被触发,则基于失败策略,将忽略 Webhook 调用或拒绝 API 调用。

超时值必须设置在 1 到 30 秒之间。

这是一个自定义超时设置为 2 秒的 validating Webhook 的示例:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
...
webhooks:
- name: my-webhook.example.com
  timeoutSeconds: 2
  ...

使用 admissionregistration.k8s.io/v1 创建的准入 Webhook 默认超时为 10 秒。

# v1.16 中被废弃,推荐使用 admissionregistration.k8s.io/v1
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingWebhookConfiguration
...
webhooks:
- name: my-webhook.example.com
  timeoutSeconds: 2
  ...

使用 admissionregistration.k8s.io/v1beta1 创建的准入 Webhook 默认超时为 30 秒。

再调用策略

修改性质的准入插件(包括 Webhook)的任何一种排序方式都不会适用于所有情况。 (参见 https://issue.k8s.io/64333 示例)。 修改性质的 Webhook 可以向对象中添加新的子结构(例如向 pod 中添加 container), 已经运行的其他修改插件可能会对这些新结构有影响 (就像在所有容器上设置 imagePullPolicy 一样)。

在 v1.15+ 中,允许修改性质的准入插件感应到其他插件所做的更改, 如果修改性质的 Webhook 修改了一个对象,则会重新运行内置的修改性质的准入插件, 并且修改性质的 Webhook 可以指定 reinvocationPolicy 来控制是否也重新调用它们。

可以将 reinvocationPolicy 设置为 NeverIfNeeded。 默认为 Never

  • Never: 在一次准入测试中,不得多次调用 Webhook。
  • IfNeeded: 如果在最初的 Webhook 调用之后被其他对象的插件修改了被接纳的对象, 则可以作为准入测试的一部分再次调用该 webhook。

要注意的重要因素有:

  • 不能保证附加调用的次数恰好是一。
  • 如果其他调用导致对该对象的进一步修改,则不能保证再次调用 Webhook。
  • 使用此选项的 Webhook 可能会重新排序,以最大程度地减少额外调用的次数。
  • 要在确保所有修改都完成后验证对象,请改用验证性质的 Webhook (推荐用于有副作用的 Webhook)。

这是一个修改性质的 Webhook 的示例,该 Webhook 在以后的准入插件修改对象时被重新调用:

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
...
webhooks:
- name: my-webhook.example.com
  reinvocationPolicy: IfNeeded
  ...

# v1.16 中被废弃,推荐使用 admissionregistration.k8s.io/v1
apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
...
webhooks:
- name: my-webhook.example.com
  reinvocationPolicy: IfNeeded
  ...

修改性质的 Webhook 必须具有幂等性,并且能够成功处理 已被接纳并可能被修改的对象的修改性质的 Webhook。 对于所有修改性质的准入 Webhook 都是如此,因为它们可以在对象中进行的 任何更改可能已经存在于用户提供的对象中,但是对于选择重新调用的 webhook 来说是必不可少的。

失败策略

failurePolicy 定义了如何处理准入 webhook 中无法识别的错误和超时错误。允许的值为 IgnoreFail

  • Ignore 表示调用 webhook 的错误将被忽略并且允许 API 请求继续。
  • Fail 表示调用 webhook 的错误导致准入失败并且 API 请求被拒绝。

这是一个修改性质的 webhook,配置为在调用准入 Webhook 遇到错误时拒绝 API 请求:

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
...
webhooks:
- name: my-webhook.example.com
  failurePolicy: Fail
  ...

使用 admissionregistration.k8s.io/v1beta1 创建的准入 Webhook 将 failurePolicy 默认设置为 Ignore

# v1.16 中被废弃,推荐使用 admissionregistration.k8s.io/v1
apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
...
webhooks:
- name: my-webhook.example.com
  failurePolicy: Fail
  ...

使用 admissionregistration.k8s.io/v1beta1 创建的准入 Webhook 将 failurePolicy 默认设置为 Ignore

监控 Admission Webhook

API 服务器提供了监视准入 Webhook 行为的方法。这些监视机制可帮助集群管理员 回答以下问题:

  1. 哪个修改性质的 webhook 改变了 API 请求中的对象?
  2. 修改性质的 Webhook 对对象做了哪些更改?
  3. 哪些 webhook 经常拒绝 API 请求?是什么原因拒绝?

Mutating Webhook 审计注解

有时,了解 API 请求中的哪个修改性质的 Webhook 使对象改变以及该 Webhook 应用了哪些更改很有用。

在 v1.16+ 中,kube-apiserver 针对每个修改性质的 Webhook 调用执行 审计操作。 每个调用都会生成一个审计注解,记述请求对象是否发生改变, 可选地还可以根据 webhook 的准入响应生成一个注解,记述所应用的修补。 针对给定请求的给定执行阶段,注解被添加到审计事件中, 然后根据特定策略进行预处理并写入后端。

事件的审计级别决定了要记录哪些注解:

Metadata 或更高审计级别上,将使用 JSON 负载记录带有键名 mutation.webhook.admission.k8s.io/round_{round idx}_index_{order idx} 的注解, 该注解表示针对给定请求调用了 Webhook,以及该 Webhook 是否更改了对象。

例如,对于正在被重新调用的某 Webhook,所记录的注解如下。 Webhook 在 mutating Webhook 链中排在第三个位置,并且在调用期间未改变请求对象。

# 审计事件相关记录
{
    "kind": "Event",
    "apiVersion": "audit.k8s.io/v1",
    "annotations": {
        "mutation.webhook.admission.k8s.io/round_1_index_2": "{\"configuration\":\"my-mutating-webhook-configuration.example.com\",\"webhook\":\"my-webhook.example.com\",\"mutated\": false}"
        # 其他注解
        ...
    }
    # 其他字段
    ...
}
# 反序列化的注解值
{
    "configuration": "my-mutating-webhook-configuration.example.com",
    "webhook": "my-webhook.example.com",
    "mutated": false
}

对于在第一轮中调用的 Webhook,所记录的注解如下。 Webhook 在 mutating Webhook 链中排在第一位,并在调用期间改变了请求对象。

# 审计事件相关记录
{
    "kind": "Event",
    "apiVersion": "audit.k8s.io/v1",
    "annotations": {
        "mutation.webhook.admission.k8s.io/round_0_index_0": "{\"configuration\":\"my-mutating-webhook-configuration.example.com\",\"webhook\":\"my-webhook-always-mutate.example.com\",\"mutated\": true}"
        # 其他注解
        ...
    }
    # 其他字段
    ...
}
# 反序列化的注解值
{
    "configuration": "my-mutating-webhook-configuration.example.com",
    "webhook": "my-webhook-always-mutate.example.com",
    "mutated": true
}

Request 或更高审计级别上,将使用 JSON 负载记录带有键名为 patch.webhook.admission.k8s.io/round_{round idx}_index_{order idx} 的注解, 该注解表明针对给定请求调用了 Webhook 以及应用于请求对象之上的修改。

例如,以下是针对正在被重新调用的某 Webhook 所记录的注解。 Webhook 在修改性质的 Webhook 链中排在第四,并在其响应中包含一个 JSON 补丁, 该补丁已被应用于请求对象。

# 审计事件相关记录
{
    "kind": "Event",
    "apiVersion": "audit.k8s.io/v1",
    "annotations": {
        "patch.webhook.admission.k8s.io/round_1_index_3": "{\"configuration\":\"my-other-mutating-webhook-configuration.example.com\",\"webhook\":\"my-webhook-always-mutate.example.com\",\"patch\":[{\"op\":\"add\",\"path\":\"/data/mutation-stage\",\"value\":\"yes\"}],\"patchType\":\"JSONPatch\"}"
        # 其他注解
        ...
    }
    # 其他字段
    ...
}
# 反序列化的注解值
{
    "configuration": "my-other-mutating-webhook-configuration.example.com",
    "webhook": "my-webhook-always-mutate.example.com",
    "patchType": "JSONPatch",
    "patch": [
        {
            "op": "add",
            "path": "/data/mutation-stage",
            "value": "yes"
        }
    ]
}

准入 Webhook 度量值

Kube-apiserver 从 /metrics 端点公开 Prometheus 指标,这些指标可用于监控和诊断 apiserver 状态。以下指标记录了与准入 Webhook 相关的状态。

apiserver 准入 Webhook 拒绝次数

有时,了解哪些准入 Webhook 经常拒绝 API 请求以及拒绝的原因是很有用的。

在 v1.16+ 中,kube-apiserver 提供了 Prometheus 计数器度量值,记录 准入 Webhook 的拒绝次数。 度量值的标签给出了 Webhook 拒绝该请求的原因:

  • name:拒绝请求 Webhook 的名称。
  • operation:请求的操作类型可以是 CREATEUPDATEDELETECONNECT 其中之一。
  • type:Admission webhook 类型,可以是 admitvalidating 其中之一。
  • error_type:标识在 webhook 调用期间是否发生了错误并且导致了拒绝。其值可以是以下之一:
    • calling_webhook_error:发生了来自准入 Webhook 的无法识别的错误或超时错误, 并且 webhook 的 失败策略 设置为 Fail
    • no_error:未发生错误。Webhook 在准入响应中以 allowed: false 值拒绝了请求。 度量标签 rejection_code 记录了在准入响应中设置的 .status.code
    • apiserver_internal_error:apiserver 发生内部错误。
  • rejection_code:当 Webhook 拒绝请求时,在准入响应中设置的 HTTP 状态码。

拒绝计数指标示例:

# HELP apiserver_admission_webhook_rejection_count [ALPHA] Admission webhook rejection count, identified by name and broken out for each admission type (validating or admit) and operation. Additional labels specify an error type (calling_webhook_error or apiserver_internal_error if an error occurred; no_error otherwise) and optionally a non-zero rejection code if the webhook rejects the request with an HTTP status code (honored by the apiserver when the code is greater or equal to 400). Codes greater than 600 are truncated to 600, to keep the metrics cardinality bounded.
# TYPE apiserver_admission_webhook_rejection_count counter
apiserver_admission_webhook_rejection_count{error_type="calling_webhook_error",name="always-timeout-webhook.example.com",operation="CREATE",rejection_code="0",type="validating"} 1
apiserver_admission_webhook_rejection_count{error_type="calling_webhook_error",name="invalid-admission-response-webhook.example.com",operation="CREATE",rejection_code="0",type="validating"} 1
apiserver_admission_webhook_rejection_count{error_type="no_error",name="deny-unwanted-configmap-data.example.com",operation="CREATE",rejection_code="400",type="validating"} 13

最佳实践和警告

幂等性

幂等的修改性质的准入 Webhook 能够成功处理已经被它接纳甚或修改的对象。 即使多次执行该准入测试,也不会产生与初次执行结果相异的结果。

幂等 mutating admission Webhook 的示例:

  1. 对于 CREATE Pod 请求,将 Pod 的字段 .spec.securityContext.runAsNonRoot 设置为 true,以实施安全最佳实践。
  2. 对于 CREATE Pod 请求,如果未设置容器的字段 .spec.containers[].resources.limits,设置默认资源限制值。
  3. 对于 CREATE pod 请求,如果 Pod 中不存在名为 foo-sidecar 的边车容器, 向 Pod 注入一个 foo-sidecar 容器。

在上述情况下,可以安全地重新调用 Webhook,或接受已经设置了字段的对象。

非幂等 mutating admission Webhook 的示例:

  1. 对于 CREATE pod 请求,注入名称为 foo-sidecar 并带有当前时间戳的 边车容器(例如 foo-sidecar-19700101-000000)。
  2. 对于 CREATE/UPDATE pod 请求,如果容器已设置标签 "env" 则拒绝, 否则将 "env": "prod" 标签添加到容器。
  3. 对于 CREATE pod 请求,盲目地添加一个名为 foo-sidecar 的边车容器, 而未查看 Pod 中是否已经有 foo-sidecar 容器。

在上述第一种情况下,重新调用该 Webhook 可能导致同一个 Sidecar 容器 多次注入到 Pod 中,而且每次使用不同的容器名称。 类似地,如果 Sidecar 已存在于用户提供的 Pod 中,则 Webhook 可能注入重复的容器。

在上述第二种情况下,重新调用 Webhook 将导致 Webhook 自身输出失败。

在上述第三种情况下,重新调用 Webhook 将导致 Pod 规范中的容器重复, 从而使请求无效并被 API 服务器拒绝。

拦截对象的所有版本

建议通过将 .webhooks[].matchPolicy 设置为 Equivalent, 以确保准入 Webhooks 始终拦截对象的所有版本。 建议准入 Webhooks 应该更偏向注册资源的稳定版本。 如果无法拦截对象的所有版本,可能会导致准入策略未再某些版本的请求上执行。 有关示例,请参见匹配请求:matchPolicy

可用性

建议准入 webhook 尽快完成执行(时长通常是毫秒级),因为它们会增加 API 请求的延迟。 建议对 Webhook 使用较小的超时值。有关更多详细信息,请参见超时

建议 Admission Webhook 应该采用某种形式的负载均衡机制,以提供高可用性和高性能。 如果集群中正在运行 Webhook,则可以在服务后面运行多个 Webhook 后端,以利用该服务支持的负载均衡。

确保看到对象的最终状态

如果某准入 Webhook 需要保证自己能够看到对象的最终状态以实施策略, 则应该使用一个验证性质的 webhook, 因为可以通过 mutating Webhook 看到对象后对其进行修改。

例如,一个修改性质的准入Webhook 被配置为在每个 CREATE Pod 请求中 注入一个名称为 "foo-sidecar" 的 sidecar 容器。

如果必须存在边车容器,则还应配置一个验证性质的准入 Webhook 以拦截 CREATE Pod 请求,并验证要创建的对象中是否存在具有预期配置的名称为 "foo-sidecar" 的容器。

避免自托管的 Webhooks 中出现死锁

如果集群内的 Webhook 配置能够拦截启动其自己的 Pod 所需的资源, 则该 Webhook 可能导致其自身部署时发生死锁。

例如,某修改性质的准入 Webhook 配置为仅当 Pod 中设置了某个标签 (例如 "env": "prod")时,才接受 CREATE Pod 请求。 Webhook 服务器在未设置 "env" 标签的 Deployment 中运行。当运行 Webhook 服务器的 容器的节点运行不正常时,Webhook 部署尝试将容器重新调度到另一个节点。 但是,由于未设置 "env" 标签,因此请求将被现有的 Webhook 服务器拒绝,并且调度迁移不会发生。

建议使用 namespaceSelector 排除 Webhook 所在的名字空间。

副作用

建议准入 Webhook 应尽可能避免副作用,这意味着该准入 webhook 仅对发送给他们的 AdmissionReview 的内容起作用,并且不要进行额外更改。 如果 Webhook 没有任何副作用,则 .webhooks[].sideEffects 字段应设置为 None

如果在准入执行期间存在副作用,则应在处理 dryRuntrueAdmissionReview 对象时避免产生副作用,并且其 .webhooks[].sideEffects 字段应设置为 NoneOnDryRun。更多详细信息,请参见副作用

避免对 kube-system 名字空间进行操作

kube-system 名字空间包含由 Kubernetes 系统创建的对象, 例如用于控制平面组件的服务账号,诸如 kube-dns 之类的 Pod 等。 意外更改或拒绝 kube-system 名字空间中的请求可能会导致控制平面组件 停止运行或者导致未知行为发生。 如果你的准入 Webhook 不想修改 Kubernetes 控制平面的行为,请使用 namespaceSelector 避免 拦截 kube-system 名字空间。

最后修改 December 04, 2020 at 6:22 PM PST : [zh] Fix links in zh localization (4) (abab517ff)