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請求essay-header以及證書,並不能獲取請求的body。所以authn/authz只能對客戶端進行認證和鑑權,不可以對請求的對象進行任何操作,因爲這裏根本還獲取不到對象。

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

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

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

1)當API Server接收到對象後首先根據http的路徑可以知道對象的版本號,然後將request body反序列化成versioned object.

2)versioned object轉化爲internal object,即沒有版本的內部類型,這種資源類型是所有versioned類型的超集。只有轉化爲internal後才能適配所有的客戶端versioned object的校驗。

3)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,對細節源代碼感興趣的讀者可以自行參考

https://github.com/kubernetes/kubernetes/blob/v1.10.0-beta.1/test/images/webhook/main.go,這裏面利用golang 標準庫實現的一個基本的http server,並註冊多個路由,同時服務於多種resource的准入控制。重點關注一下資源對象的decode過程,這是k8s apimachinery的高級功能。利用了apimachinery的scheme的能力,使用之前必須要將api註冊到scheme中,代碼詳見:

(https://github.com/kubernetes/kubernetes/blob/v1.10.0-beta.1/test/images/webhook/scheme.go)。一個典型的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: v1kind: Namespacemetadata: name: e2e-tests-webhook-gbgt6spec: finalizers: - kubernetes---apiVersion: extensions/v1beta1kind: Deploymentmetadata: labels: app: sample-webhook webhook: "true" name: sample-webhook-deployment namespace: e2e-tests-webhook-gbgt6spec: 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: v1kind: Servicemetadata: labels: test: webhook name: e2e-test-webhook namespace: e2e-tests-webhook-gbgt6spec: 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/v1beta1kind: MutatingWebhookConfigurationmetadata: name: e2e-test-mutating-webhook-podwebhooks:- 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: v1kind: Podmetadata: name: webhook-to-be-mutated namespace: e2e-tests-webhook-gbgt6spec: 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: v1kind: Podmetadata: 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-286ed488dc10spec: 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。

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

●webhook可動態擴展Admission能力,滿足自定義客戶的需求

●不需要重啓API Server,可通過創建webhook configuration熱加載webhook admission。

參考:

1)https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/

2)https://www.slideshare.net/sttts/kubecon-eu-2018-sig-api-machinery-deep-dive

查看原文 >>
相關文章