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 介紹