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復雜的類型系統以及安全策略方面的考慮,會讓這些特性使用起來並不十分友好。

 

 

參考文獻


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM