介绍 Kubernetes Admission Controllers

Admission Controllers

在介绍 Kubernetes 的 Admission Controllers 迁移,我们通过回顾一下 kube-apiserver 处理 request 的过程。

上图中每个 API Request 在被保存到 etcd 前,kube-apiserver 对它们进行了以下处理:

  • API HTTP Handler
  • Authentication/Authorization
  • Mutating Admission
  • Object Schema Validation
  • Validating Admission

kube-apiserver 和其他 http server 一样在真正处理请求时,需要先运行一些代码中预先定义的 filter 函数,即上面图片中的 API HTTP Handler 过程。kube-apiserver 注册的 filter 在逻辑在 staging/src/k8s.io/apiserver/pkg/server/config.go 文件的 DefaultBuildHandlerChain 函数中实现。filter 函数主要定义在 k8s.io/apiserver/pkg/server/filters 包。

从 DefaultBuildHandlerChain 的代码中可以看出,该函数注册了 WithAuthorization 和 WithAuthentication 函数,这两个函数会分别通过 Authorizer 接口和 AuthenticateRequest 接口分别进行鉴权和认证。

请求通过处理链后 kube-apiserver 真正处理逻辑就开始了,"/",“version”,“apis”,“/healthz” 等特殊 URL 的请求将被 router 到特定的函数进行处理,而针对资源类(如 pod 等)请求将进入后续的 Mutating Admission、Object Schema Validation、Validating Admission 阶段进行处理,最后保存到 etcd 中。

上述流程中,完成 Mutating Admission 和 Validating Admission 阶段的代码逻辑在 Kubernetes 中称为 Admission Controllers ,按照其功能可以大致分为两类:

  • Mutating Admission:修改请求的对象
  • Validating Admission:验证请求的对象

Admission Controllers 可能属于 Mutating Admission 或者 Validating Admission 中的一种又或者同时属于这两种类型。

kube-apiserver 处理 request 的请求并非完全如上图一样,step by step 按照顺序执行。该图来自 kubernetes blogs 仅描述了 kube-apiserver 工作的大体流程。

目前 Kubernetes 中定义了大量 Admission Controllers,代码在 kubernetes 项目的 plugin/pkg/admission 目录下。

用户可以通过参数 –enable-admission-plugins–disable-admission-plugins 开启或者禁用特定的 Admission Controllers。 通过执行以下命令可以快速查看,默认开启的 Admission Controllers:

1
kube-apiserver -h | grep enable-admission-plugins

官方文档花了很大篇幅介绍每个 Admission Controllers 的作用,有兴趣的读者可以直接阅读相关章节(what does each admission controller do)。

Dynamic Admission Control

Kubernetes 提供了两个特殊准入控制器 ValidatingAdmissionWebhook 和 MutatingAdmissionWebhook。它们提供了一种基于 HTTP 回调的机制,使用户可以在 kube-apiserver 处理流程中实现对资源的修改和校验。通常我们将 ValidatingAdmissionWebhook 和 MutatingAdmissionWebhook 提供的能力称为“动态准入”。

ValidatingAdmissionWebhook 和 MutatingAdmissionWebhook 的代码逻辑定义在 staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook 包中,用户启用它们之前需要确保 kube-apiserver 开启了相关插件,并且启用了 admissionregistration.k8s.io/v1 API。

WebHook Server

Dynamic Admission Control 基于 web hook 工作,用户需要实现一个 Admission Webhook Server。Server 用于处理由 kube-apiserver 发送的 AdmissionReview 请求,并将处理结果发回 kube-apiserver。

Kubernetes 官方给出了以下两个 WebHook Server 的 demo:

依赖 kubernetes-sigs/controller-runtime 包提供的接口,我们可以非常快速的开发 Webhook Server。用于只需要将实现 kubernetes-sigs/controller-runtime 中定义的 Handler 接口并将它注册到对应的 url 中即可。

1
2
3
4
5
6
7
8
// Handler can handle an AdmissionRequest.
type Handler interface {
	// Handle yields a response to an AdmissionRequest.
	//
	// The supplied context is extracted from the received http.Request, allowing wrapping
	// http.Handlers to inject values into and control cancelation of downstream request processing.
	Handle(context.Context, Request) Response
}

通常情况下,我们将 Webhook Server 部署在 kubernetes 集群中并且创建对应的 Service ,kube-apiserver 通过 ClusterIP 访问他们,但需要注意的是我们需要在一定程度上保证 Webhook Server 的高可用。

证书配置

kube-apiserver 和 Webhook Server 基于 HTTPS 通讯,因此双方需要配置相关证书。

当 Webhook Server 基于 [kubernetes-sigs/controller-runtime] 开发时,我们可以指定一个 serving-certs 目录,目录中包含证书文件 tls.crt 和 tls.key。

需要注意的是:服务端证书文件中的 SAN 配置需要和 kube-apiserver 访问 Webhook Server 使用的地址匹配,通常在 kubernetes 集群中部署 Webhook Server 并通过 Service 访问时,需要将ClusterIP 或者 Service Name 添加到证书的 SAN 中。

需要注意,当 kube-apiserver 使用 systemctl 部署在服务器上时,无法解析 Service Name

配置好 Webhook 的服务端证书后,需要通过 –admission-control-config-file 在 kube-apiserver 配置客户端证书,该配置指定了 ValidatingAdmissionWebhook 和 MutatingAdmissionWebhook 访问服务端时使用的客户端证书,内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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>"

上述配置中,kubeConfigFile 指向证书文件,它的格式和 kubeconfig 的文件格式一致,可以基于证书、token 等多种方式实现认证:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
apiVersion: v1
kind: Config
users:
# Webhook Server 地址为 https://kube-webhook.svc.cluster.local ,这里 "*" 是通配符。
- name: '*.svc.cluster.local'
  user:
    client-certificate-data: "<pem encoded certificate>"
    client-key-data: "<pem encoded key>"
# Webhook Server 地址为 https://127.0.0.1:9443
- name: '127.0.0.1:9443'
  user:
    password: "<password>"
    username: "<name>"
# '*' 表示默认值,即所有不符合前面匹配的 url 地址,使用下面的 token 认证。
- name: '*'
  user:
    token: "<token>"

webhooks 配置

ValidatingAdmissionWebhook 和 MutatingAdmissionWebhook 之所以被称为“动态准入控制”的原因是:用户可以在不重启 kube-apisever 的前提下(前提是你已经配置好了 HTTPS 的客户端证书),使自定义的 webhook 生效。这主要通过在 kubernetes 中配置 ValidatingWebhookConfiguration 或者 MutatingWebhookConfiguration 实现,不同配置决定了 webhook 起作用的位置不同,即 Validating 和 Mutating 的差别。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
  name: pod-toleration-mutating-webhook
webhooks:
  - name: pod-toleration
    clientConfig:
      caBundle: <ca base64>
      service:
        name: pod-toleration-mutating-webhook
        namespace: kube-system
        path: "/mutate-pod-toleration"
    rules:
      - operations: ["CREATE"]
        apiGroups: [""]
        apiVersions: ["v1"]
        resources: ["pods"]
    failurePolicy: Ignore
    namespaceSelector:
      matchLabels:
        pod-toleration-injection: enabled
    sideEffects: None
    admissionReviewVersions: ["v1", "v1beta1"]
    timeoutSeconds: 5

上述定义了一个为 Pod 注入 toleration 的 MutatingWebhook,其中最关键是:webhooks.clientConfig,它决定了 kube-apiserver 如何连接 Webhook Server 。用户可以通过 service 或 url 的方式定义 Webhook Server 的服务器地址、端口、CA 证书、以及 URL。

MutatingWebhookConfiguration 还决定了 kube-apiserver 调用 webhook 的事件类型,用户可以通过多种方式设置 kube-apiserver 什么情况下会调用 webhook:

  • rules:这是最常用的方式,基于资源的 GVK 操作(CREATE、DELETE、UPDATE) 筛选事件。
  • objectSelector:基于资源的 Labels 筛选事件。
  • namespaceSelector:基于资源所处的 Namespace 进行筛选。

需要注意,由于升级引发资源的版本变化,如 Deployment 的版本从 extensions/v1beta1 变为 apps/v1 时,默认情况下用户只需要在 rules 中指定 apps/v1 版本即可,具体可以查看官方文档关于 Matching requests: matchPolicy 的描述。

配置 manifest 的字段说明可以参考以下地址:

其他内容

Side effects

kubernetes 在 1.13 版本提供了服务端 Dry-run 功能,当用户使用 kubectl apply 时可以添加 –dry-run=server 参数,当 kube-apiserver 处理这类请求时同样会访问 webhook 并执行完正常请求除持久化到 etcd 以外的所有流程。

对于一些调用 kubenretes client 修改其中资源的 Webhook 来说, dry-run=server 请求对 kubernetes 集群会产生某些副作用。

为了杜绝这些副作用,kubernetes 在 MutatingWebhookConfiguration 和 ValidatingWebhookConfiguration 中提供了 sideEffects 配置,该配置支持以下参数:

  • None:明确声明该 Webhook 对集群不存在副作用
  • NoneOnDryRun:该 Webhook 对集群存在副作用,当 Server 端能够从 AdmissionReview 的参数中判断请求是否是 dry-run 类型,并作出相应的逻辑处理。
  • Some:该 Webhook 对集群存在副作用,此时 dry-run 类型的请求不会被发送到 Webhook Server,kube-apiserver 会直接拒绝请求。
  • Unknown:作用同 Some ,默认值。

参考:https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#side-effects

幂等性

根据官方文档的描述,通过 reinvocationPolicy 配置我们可以触发 Webhook 的重新调用,以避免内置 Admission 覆盖 MutatingWebhook 的修改。 这种情况下,要注意的重要因素有:

  • 不能保证重新调用 Webhook 的次数恰好是一。
  • 如果重新调用 Webhook 导致对象的进一步修改,则不能保证再次调用 Webhook(进步修改意思是:Webhook Server 接受/返回的对象结构不一致)。
  • Webhook 可能会重新排序,以最大程度地减少额外调用的次数。

为了满足上述要求,MutatingWebhook 的代码应该总能保证其“幂等性”。

参考:https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/extensible-admission-controllers/#reinvocation-policy

监控

参考:https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#monitoring-admission-webhooks

最佳实践

参考:https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#best-practices-and-warnings

参考