天天看點

Kubernetes 準入控制 Admission Controller 介紹

1.什麼是Admission Controller

Admission Controller(準入控制)是 Kubernetes API Server 用于攔截請求的一種手段。Admission可以做到對請求的資源對象進行校驗,修改。service mesh最近很火的項目Istio天生支援Kubernetes,利用的就是admission對服務執行個體自動注入sidecar。

假如對 Kubernetes 有一定的了解的話,應該會知道在 Kubernetes 中還有 authn/authz,為什麼還會引入 admission 這種機制?

1)authn/authz 是 Kubernetes 的認證鑒權,運作在 filter 中,隻 能擷取 http 請求 header 以及證書,并不能擷取請求的 body。是以 authn/authz 隻能對用戶端進行認證和鑒權,不可以對請求的對象進行任何操作,因為這裡根本還擷取不到對象。

2)Admission 運作在 API Server 的增删改查 handler 中,可以自然地操作 API resource。

下面将對 Admission Controller 工作流做一番詳解。

API Server 接收到用戶端請求後首先進行認證鑒權,認證鑒權通過後才會進行後續的endpoint handler處理。

  • 當API Server 接收到對象後首先根據 http 的路徑可以知道對象的版本号,然後将 request body 反序列化成 versioned object.
  • versioned object 轉化為 internal object,即沒有版本的内部類型,這種資源類型是所有 versioned 類型的超集。隻有轉化為 internal 後才能适配所有的用戶端 versioned object 的校驗。
  • Admission Controller 具體的 admit 操作,可以通過這裡修改資源對象,例如為 Pod 挂載一個預設的 Service Account 等。

    4)API Server internal object validation,校驗某個資源對象資料和格式是否合法,例如:Service Name 的字元個數不能超過63等。

    5)Admission Controller validate,可以自定義任何的對象校驗規則。

    6)internal object 轉化為 versioned object,并且持久化存儲到 etcd。

注:以上 versioned object 和 internal object 直接的轉換關系會在《深度剖析Kubernetes API Server三部曲 - part 2》詳細解釋,歡迎持續關注。

2.如何使用 admission controller

Kubernetes 1.10之前的版本可以使用

--admission-control

打開 Admission Controller。同時

--admission-control

的順序決定 Admission 運作的先後。其實這種方式對于使用者來講其實是挺複雜的,因為這要求使用者對所有的 Admission Controllers 需要完全了解。

如果使用Kubernetes 1.10之後的版本,

--admission-control

已經廢棄,建議使用

--enable-admission-plugins --disable-admission-plugins

指定需要打開或者關閉的 Admission Controller。 同時使用者指定的順序并不影響實際 Admission Controllers 的執行順序,對使用者來講非常友好。

值得一提的是,有些 Admission Controller 可能會使用 Alpha 版本的 API,這時必須首先使能其使用的 API 版本。否則 Admission Controller 不能工作,可能會影響系統功能。

2.1 webhook admission

目前 Kubernetes 中已經有非常多的 Admission 插件, 但是并不能保證滿足所有開發者的需求。 衆所周知,Kbernetes 之是以受到推崇,它的可擴充能力功不可沒。Admission 也提供了一種 webhook 的擴充機制。

  • MutatingAdmissionWebhook:在對象持久化之前進行修改
  • ValidatingAdmissionWebhook:在對象持久化之前進行

可能有讀者接觸過另外一種動态可擴充的機制 Initializers,不過至今還是 Apha 特性,社群讨論有可能會把它移除。是以選擇動态 Admission 首選 webhook。

2.2 如何使用webhook admission

Webhook Admission 屬于同步調用,需要使用者部署自己的 webhook server,建立自定義的配置資源對象: ValidatingWebhookConfiguration 或 MutatingWebhookConfiguration。

  • 開發webhook server

這裡我推薦參考社群 e2e 測試用的 server,對細節源代碼感興趣的讀者可以自行參考

github.com/kubernetes/…

,這裡面利用 golang 标準庫實作的一個基本的 http server,并注冊多個路由,同時服務于多種 resource 的準入控制。重點關注一下資源對象的 decode 過程,這是k8s apimachinery 的進階功能。利用了 apimachinery 的 scheme 的能力,使用之前必須要将 api 注冊到 scheme 中,代碼詳見:

。一個典型的 webhook 修改資源對象(Pod)的樣例代碼如下所示。

func mutatePods(ar v1beta1.AdmissionReview) *v1beta1.AdmissionResponse {
 glog.V(2).Info("mutating pods")
 podResource := metav1.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}
 if ar.Request.Resource != podResource {
 glog.Errorf("expect resource to be %s", podResource)
 return nil
 }

 raw := ar.Request.Object.Raw
 pod := corev1.Pod{}
 deserializer := codecs.UniversalDeserializer()
// pod的解碼,利用apimachinery
 if _, _, err := deserializer.Decode(raw, nil, &pod); err != nil {
 glog.Error(err)
 return toAdmissionResponse(err)
 }
 reviewResponse := v1beta1.AdmissionResponse{}
 reviewResponse.Allowed = true if pod.Name == "webhook-to-be-mutated" {
 reviewResponse.Patch = []byte(addInitContainerPatch)
 pt := v1beta1.PatchTypeJSONPatch
 reviewResponse.PatchType = &pt
 }
 return &reviewResponse
 }
複制代碼           
  • 部署webhook server
# kubectl create –f webhook-server.yaml 複制代碼           
apiVersion: v1
kind: Namespace
metadata:
 name: e2e-tests-webhook-gbgt6
spec:
 finalizers:
 - kubernetes
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
 labels:
 app: sample-webhook
 webhook: "true"
 name: sample-webhook-deployment
 namespace: e2e-tests-webhook-gbgt6
spec:
 replicas: 1
 selector:
 matchLabels:
 app: sample-webhook
 webhook: "true"
 template:
 metadata:
 labels:
 app: sample-webhook
 webhook: "true"
 spec:
 containers:
 - args:
 - --tls-cert-file=/webhook.local.config/certificates/tls.crt
 - --tls-private-key-file=/webhook.local.config/certificates/tls.key
 - --alsologtostderr
 - -v=4
 - 2>&1
 image: gcr.io/kubernetes-e2e-test-images/k8s-sample-admission-webhook-amd64:1.10v2
 imagePullPolicy: IfNotPresent
 name: sample-webhook
 volumeMounts:
 - mountPath: /webhook.local.config/certificates
 name: webhook-certs
 readOnly: true
 volumes:
 - name: webhook-certs
 secret:
 defaultMode: 420
 secretName: sample-webhook-secret
---
apiVersion: v1
kind: Service
metadata:
 labels:
 test: webhook
 name: e2e-test-webhook
 namespace: e2e-tests-webhook-gbgt6
spec:
 ports:
 - port: 443
 protocol: TCP
 targetPort: 443
 selector:
 webhook: "true"
 sessionAffinity: None
 type: ClusterIP
複制代碼           

建立 webhook server Deployment 以及 Service,供 API Server 調用。

  • 建立MutatingWebhookConfiguration
# kubectl create –f webhook-config.yaml 複制代碼           
apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
metadata:
 name: e2e-test-mutating-webhook-pod
webhooks:
- clientConfig:
 caBundle: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMyRENDQWNDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFkTVJzd0dRWURWUVFERXhKbE1tVXQKYzJWeWRtVnlMV05sY25RdFkyRXdIaGNOTVRnd056RTVNRGMwT1RJeFdoY05Namd3TnpFMk1EYzBPVEl4V2pBZApNUnN3R1FZRFZRUURFeEpsTW1VdGMyVnlkbVZ5TFdObGNuUXRZMkV3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBCkE0SUJEd0F3Z2dFS0FvSUJBUURFVVFEWVN6SGl3SUFHU1dHSWRBSmVBbnMrNFhaYjlZc3VuQlBVTkJPdHZqeFoKV3NSbUxydE0zVU9lcEszeGsvMzZCSS96RkdXdUNpMlJ0TWUxSWtEa2tVMzNEZE83K0ExVyt2NVZNVnFqL0lDTApsc29USml3TFhTcGowTHNwSUNVdGtqT1dlRjVhK3lJVHgyR01TMG9ZbWtuaHB0RXMrc2tKQjFMWm1uVTBaWFpzClRKak9Lb05ueHdVaTl4QnRUTXBQRWw2cVhmb3dCWlpvYjlkUzNtNzFLbjJCdU5Ec0s3YnVRcGJvdk9XdUQyNDAKdzNLQVJnT04xcjA4Vm4zd1I1MHVXS09tSkVsLzRUZ2JnSTRkaG85WHNIWUhUdnk4R3JRMXhYZE43ZEhSTlpHNQo5aDhmOUUzdjg1VWxwSEVWQThqUHB4RE5SSm9qRXVGQk9raFJEZEY1QWdNQkFBR2pJekFoTUE0R0ExVWREd0VCCi93UUVBd0lDcERBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCQVFDWWl4VUsKYkhsRUpCK2t4THdqdktySDQ1OVVsNUJjb0VXZE1BNnArUC8yWXVZa2NuWC9GRVNjUFRxUS9vdkF3ejU1ZG1FUwpJTjVZOWd2ZlJxdWhZcEdWOHVFSWpzVkczTjdKQm1wM0NyclEyd3FYeHV3cndkVXV1dDltQSt2RkQ4Q2FQSE8xCmVad1J6NEkzTktFQ0xHMHJXQWxseEVvUm9tQ2UvaWZIUnRNRklTRk5sSnZVNlhIbzFDVWNFQ2FwOG9hYXN2cFcKT2JBQjVqQzc5WWJXN2lWVm54cjZGMnRvOG9oSEdNSEpXR1pwSTNKbVpNbGVOK01kVm5ySFdXSXBkOG9iS2E3TgpqSlZTczgzRmlDMzd4d2dqMUQyaTNHUnh5bHNKZEdJWTl4WVpQVmNNUTh6Z2FMMUpJUk1BdVZYbHczUkRzSDR0Cms5WmFybGY1NG9BOUN0Nk8KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
 service:
 name: e2e-test-webhook
 namespace: e2e-tests-webhook-gbgt6
 path: /mutating-pods
 failurePolicy: Ignore
 name: adding-init-container.k8s.io
 namespaceSelector: {}
 rules:
 - apiGroups:
 - ""
 apiVersions:
 - v1
 operations:
 - CREATE
 resources:
 - pods
 - 
複制代碼           

rules 表示對于 core/v1/pods 資源對象建立的時候調用 mutating webhook。server 的位址及路徑通過 clientConfig 指明。

/mutating-pods

是指調用 webhook server 執行 mutatePods,為 pod 增加 init initContainers。

func mutatePods(ar v1beta1.AdmissionReview) *v1beta1.AdmissionResponse {
 glog.V(2).Info("mutating pods")
 podResource := metav1.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}
 if ar.Request.Resource != podResource {
 glog.Errorf("expect resource to be %s", podResource)
 return nil
 }

 raw := ar.Request.Object.Raw
 pod := corev1.Pod{}
 deserializer := codecs.UniversalDeserializer()
 if _, _, err := deserializer.Decode(raw, nil, &pod); err != nil {
 glog.Error(err)
 return toAdmissionResponse(err)
 }
 reviewResponse := v1beta1.AdmissionResponse{}
 reviewResponse.Allowed = true if pod.Name == "webhook-to-be-mutated" {
 reviewResponse.Patch = []byte(addInitContainerPatch)
 pt := v1beta1.PatchTypeJSONPatch
 reviewResponse.PatchType = &pt
 }
 return &reviewResponse
}
複制代碼           

建立Pod

kubectl create –f pod.yaml
複制代碼           
apiVersion: v1
kind: Pod
metadata:
 name: webhook-to-be-mutated
 namespace: e2e-tests-webhook-gbgt6
spec:
 containers:
 - image: k8s.gcr.io/pause:3.1
 name: example
複制代碼           

查詢Pod

# kubectl get pod webhook-to-be-mutated –n e2e-tests-webhook-gbgt6 -oyaml 複制代碼           
apiVersion: v1
kind: Pod
metadata:
 creationTimestamp: 2018-07-19T07:49:37Z
 name: webhook-to-be-mutated
 namespace: e2e-tests-webhook-gbgt6
 resourceVersion: "806"
 selfLink: /api/v1/namespaces/e2e-tests-webhook-gbgt6/pods/webhook-to-be-mutated
 uid: 48d2e91d-8b28-11e8-b16d-286ed488dc10
spec:
 containers:
 - image: k8s.gcr.io/pause:3.1
 imagePullPolicy: IfNotPresent
 name: example
 resources: {}
 terminationMessagePath: /dev/termination-log
 terminationMessagePolicy: File
 volumeMounts:
 - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
 name: default-token-jhqlb
 readOnly: true
 dnsPolicy: ClusterFirst
 initContainers:
 - image: webhook-added-image
 imagePullPolicy: Always
 name: webhook-added-init-container
 resources: {}
 terminationMessagePath: /dev/termination-log
 terminationMessagePolicy: File
 nodeName: 127.0.0.1
 priority: 0
 restartPolicy: Always
 schedulerName: default-scheduler
 securityContext: {}
 serviceAccount: default
 serviceAccountName: default
 terminationGracePeriodSeconds: 30
 tolerations:
 - effect: NoExecute
 key: node.kubernetes.io/not-ready
 operator: Exists
 tolerationSeconds: 300
 - effect: NoExecute
 key: node.kubernetes.io/unreachable
 operator: Exists
 tolerationSeconds: 300
 volumes:
 - name: default-token-jhqlb
 secret:
 defaultMode: 420
 secretName: default-token-jhqlb
複制代碼           

可以看出,建立成功的pod已經多了一個名字為

webhook-added-init-container的initContainers

3.總結

最後我們來總結下 webhook Admission 的優勢

  • webhook 可動态擴充 Admission 能力,滿足自定義客戶的需求
  • 不需要重新開機 API Server,可通過建立 webhook configuration 熱加載 webhook admission。

本文轉自掘金-

Kubernetes 準入控制 Admission Controller 介紹

繼續閱讀