其他分享
首页 > 其他分享> > Kubernetes Admission Controller解析

Kubernetes Admission Controller解析

作者:互联网

本文将对Kubernetes中的Admission Controller机制及其原理进行解析并以Prometheus Operator为例说明该机制在具体应用场景下的实现及使用方式。

原理概述

简单地说,Admission Controller实现了对于Kubernetes集群的准入控制。如上图所示,Admission Controller以插件的形式内置于Kuberntes APIServer,在APIServer对请求的处理链路中发挥作用。一般RESTful请求进入APIServer之后,会经过认证、审计、流量控制、鉴权等一系列通用处理,接着Admission Controller会对请求进行准入控制,主要包含MutatingValidation两类操作,具体的操作都由相应的插件完成。Mutating可以对请求中的资源对象进行修改而Validation则仅进行校验。MutatingValidation之间还有一个名为Object Schema Validation的操作,用于进行一些对于资源对象通用的校验,例如Pod中所有容器的名字都要唯一等等。最后完成与etcd之间的交互。

Kubernetes APIServer中内置了一系列准入控制的插件,利用启动参数中的--enable-admission-plugins可以指定需要启动的准入控制插件:

--enable-admission-plugins=PodNodeSelector,NamespaceLifecycle,PodSecurityPolicy,...,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,...

不同的Admission Controller插件用于对不同类型的请求进行准入控制,通过对请求内容的校验甚至修改来实现保证集群以及应用的安全可用和对应用配置的高度可控的目的。

例如,上述配置中的NamespaceLifecycle插件用于确保在处于Terminating状态的Namespace内创建新的资源对象的请求会被拒绝,而插件PodSecurityPolicy则会根据请求创建的Pod的Security Context以及相关的Pod Security Policy决定是否拒绝该Pod。

但是需要注意的是,类似于NamespaceLifecycle的准入控制插件都是以in-tree的形式存在于APIServer中的,也就是说如果要使用该插件,不仅需要在APIServer的启动参数中做如上指定,而且需要将该插件编译至APIServer的二进制文件中,而且在运行时中动态加载插件也是不可能的。

显然,这种方式是不具备扩展性的,尤其是在Kubernetes Operator生态不断发展,用户对CRD的使用日益频繁的情况下。事实上,上述配置中MutatingAdmissionWebhookValidatingAdmissionWebhook这两个特殊的插件就是用来解决扩展性问题的。这两个插件往往分别位于MutatingValidating的最后,它们并不会对请求进行任何处理而是直接将它们以webhook的形式转发至系统中注册的远程插件,由远程插件执行用户自定义的一些准入控制操作。

Admission Controller Webhook

既然支持准入控制插件的动态安装,那么首先需要解决的就是插件的服务发现问题,自定义准入控制插件该如何向APIServer注册呢?在Kubernetes体系下,解决这类问题最常规的方法就是定义一个新的资源对象,事实上,Kubernetes也是这么做的。自定义的准入控制插件一般不会独立部署,而是往往以Webhook Server的形式嵌入到CRD的Operator中,例如,本文中作为例子的Prometheus Operator,用来对该Operator定义的资源对象进行校验。Operator以Deployment的形式部署在集群中并且配置相应的Service。如果要将该插件注册至APIServer使其能够在准入控制中的Validating阶段生效,则需要定义如下资源对象:

apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingWebhookConfiguration
metadata:
name: prometheus-operator-admission
webhooks:
- name: prometheusrulevalidate.monitoring.coreos.com
clientConfig:
  caBundle: ${CA_PEM_B64}
  service:
    name: prometheus-operator
    namespace: monitor
    path: /admission-prometheusrules/validate
failurePolicy: Fail
rules:
- apiGroups:
  - monitoring.coreos.com
  apiVersions:
  - '*'
  operations:
  - CREATE
  - UPDATE
  resources:
  - prometheusrules

从字面上就非常容易理解上述配置的含义,其中的rules字段描述了该自定义插件需要处理的资源对象的筛选条件。例如这个例子表示仅对monitoring.coreos.com这个API Group下任意版本的PrometheusRules资源对象的创建/更新进行处理。clientConfig字段则表示了对于自定义插件的访问方法,利用service字段就能方便地拼接出一个URL,APIServer内置的ValidatingAdmissionWebhook直接将请求封装并且转发即可。如果要注册在Mutating阶段生效的插件,则需要另外创建一个类型为MutatingWebhookConfiguration的资源对象,格式内容与ValidatingWebhookConfiguration基本一致。一般Operator中内嵌的Admission Webhook Server会包含两个endpoints,用于分别处理MutatingValidating类型的请求。例如Prometheus Operator中,/admission-prometheusrules/mutate用于处理Mutating类型的请求,而 /admission-prometheusrules/validate则用于处理Validating类型的请求。

APIServer中的MutatingAdmissionWebhookValidatingAdmissionWebhook插件则会分别对MutatingWebhookConfigurationValidatingWebhookConfiguration类型的资源对象进行List-Watch。每当处理一个请求时,它们都需要对该请求的目标资源对象与各个注册插件的rules进行匹配。一旦匹配成功则将请求封装至如下结构并利用services中的信息转发至对应插件进行处理:

type AdmissionReview struct {
metav1.TypeMeta
Request *AdmissionRequest
Response *AdmissionResponse
}

APIServer与插件交互的Request和Reponse都是利用的上述结构,对于APIServer发往插件的请求,上述结构的Request字段不为空,如下所示,其中包含了插件需要的所有信息,Object字段更是包含了该RESTful请求对应的资源对象的完整内容:

type AdmissionRequest struct {
   ...
   Kind metav1.GroupVersionKind
   Resource metav1.GroupVersionResource
   Name string
   Namespace string
   Operation Operation
   Object runtime.RawExtension
   ...
}

插件会从AdmissionRequest中解析出,其中包含的需要校验的资源对象。例如,Prometheus Operator会首先判断得到的资源对象是否为自己需要校验的类型,即PrometheusRules,之后再对其中的内容进行校验,根据结果返回AdmissionReview结构并指定其中的Response字段,如下:

type AdmissionResponse struct {
   ...
   Allowed bool
   Result *metav1.Status
   Patch []byte
   PatchType *PatchType
   ...
}

Allowed字段表示校验是否通过,Result字段在校验不通过时表明理由。Patch相关的字段仅在Mutating阶段使用,用于表示对资源对象的修改,此处最经典的案例莫过于Istio利用MutatingWebhookConfiguration自动注入Envoy作为应用的Proxy Sidecar。

上述即为Admission Controller实现原理的全部内容,理解起来并不是十分困难,简而言之,APIServer内置了一堆准入控制插件能够对资源对象的增删改查进行控制甚至修改,从而保证了集群的安全可用以及对于资源对象配置统一灵活的管理,而基于Webhook的远程插件又进一步增加了这种灵活性。至于自定义插件本质上就是一个Webhook Server,分别针对MutatingValidating实现相应的处理函数,为了更直观地理解,可以参考Prometheus Operator中的实现。整个流程的示意图如下:

但是真正要在生产环境中应用Admission Controller这一特性,尤其是为CRD自定义插件,作为在APIServer之外独立运行的程序,APIServer与之交互伴随而来的安全问题是不得不考虑的。

APIServer与准入控制插件的安全交互

Kubernetes构建了一套复杂的机制保证集群的安全,特别是与APIServer相关的交互,各种权限问题是根本绕不过的。上文为了突出Admission Controller的实现原理,我们有意忽略了安全相关的设置。事实上,APIServer在通过MutatingWebhookConfiguration或者ValidatingWebhookConfiguration获取相应的Service之后只能通过HTTPS与之进行交互,交互的端口只能为443,而且APIServer不能跳过对于插件的认证。因此,MutatingWebhookConfiguration或者ValidatingWebhookConfiguration中的clientConfig.caBundle字段是必须填写的。

那么现在的问题就变为如何在部署准入控制插件时自签名一套证书供插件使用而根证书能够写入MutatingWebhookConfiguration或者ValidatingWebhookConfiguration中供APIServer使用。所幸地是,如果以Helm的形式部署应用,Helm允许在应用启动之前或者之后执行一系列Hook操作,这也就为我们提前创建自签名证书提供了可能。整个部署过程总体可以分为以下三个步骤:

  1. 利用helm中的PrestartHook,构建前置Job(在annotation中指定helm.sh/hook: pre-install,pre-upgrade即可)用于构建自签名证书。此处需要使用一个名为kube-webhook-certgen的开源组件。该组件主要包含两个子命令,其中子命令create生成对应的ca以及cert+key并保存至指定的Secret中,具体配置参见Prometheus Operator中的相关设置

  2. 真正执行插件部署,包括插件对应的Deployment,Service以及MutatingWebhookConfiguration或者ValidatingWebhookConfiguration。需要注意的是要将步骤1中生成的Secret中包含的cert和key挂载到插件对应的Pod中,使其能够构建基于HTTPS的Webhook Server供Kubernetes APIServer访问。当然,如果有的插件,类似于Prometheus Operator,本身并不支持HTTPS,则需要以Sidecar的形式部署一个Proxy用作TLS Termination。由该Proxy挂载Secret中的cert和key并与APIServer直接交互,再由它将流量转发至Prometheus Operator真正完成准入控制,具体配置参见Prometheus Operator中的相关设置

  3. 利用helm中的PoststartHook,构建后置Job。该Job同样利用了kube-webhook-certgen,与步骤1不同的是,这次使用的是子命令patch用于将指定Secret中的ca加载到MutatingWebhookConfiguration或者ValidatingWebhookConfiguration中。APIServer监听到配置变更之后就会重新加载MutatingWebhookConfiguration/ValidatingWebhookConfiguration,后续有符合规则的请求就能够通过HTTPS转发到对应的插件进行处理。该Job的具体配置参见Prometheus Operator中的相关设置

上述步骤的示意图如下:

总结

本文主要对Kubernetes Admission Controller的设计原理进行了分析并结合其在Prometheus Operator中的应用说了该机制在实际使用中可能遇到的问题,尤其是安全方面的问题。Admission Controller也是Kubernetes强大扩展性的一种体现,为进一步增强集群的安全可用,资源配置的统一管理控制提供了可能。以Admission Controller为例,Kubernetes的很多高级特性从设计来看并不十分复杂,但是由于Kubernetes复杂的类型系统以及安全策略方面的考虑,会让这些特性使用起来并不十分友好。

 

 

参考文献

标签:插件,Kubernetes,Admission,APIServer,Controller,Operator
来源: https://www.cnblogs.com/YaoDD/p/12747090.html