《使用 Kubebuilder 建立自定義 K8s AdmissionWebhooks》 最先發布在 blog.hdls.me/15708754600…html
Kubebuilder 除了能夠構建 CRD API 及其 Controller 以外,還能構建 AdmissionWebhooks。這篇文章就來詳細分析 Kubebuilder 如何構建 AdmissionWebhooks。node
首先要知道,在 K8s 裏 AdmissionWebhooks 是什麼,目的是什麼。android
先說場景,若是咱們須要在 pod 建立出來以前,對其進行配置修改或者檢查,這部分工做若是放在 ApiServer 裏,須要管理員在 ApiServer 中將其編譯成二進制文件,若是配置修改想作成自定義的形式會很是麻煩。而 Admission controllers 就是爲這種場景而生的工具,以插件的形式附着到 ApiServer 中,AdmissionWebhooks 就是其中一種准入插件。nginx
K8s 的 AdmissionWebhooks 分兩種:MutatingAdmissionWebhook
和 ValidatingAdmissionWebhook
,兩者合起來就是一個特殊類型的 admission controllers
,一個處理資源更改,一個處理驗證。git
在以前一篇文章《[譯]深刻剖析 Kubernetes MutatingAdmissionWebhook》中,給出了很是詳細的 MutatingAdmissionWebhook 的教程,其中主要作了三件事情:github
那麼用 Kubebuilder 構建 AdmissionWebhooks 的話,Kubebuilder 會爲咱們自動生成 Webhook Server,並留下幾個函數讓咱們添加自有邏輯。web
這裏使用一個簡單的場景作一個演示,咱們自定義一個名爲 App 資源,當用戶建立一個 App 實例時,咱們根據用戶的描述建立出一個 Deployment。redis
而後咱們添加一個 MutatingAdmissionWebhook
,當用戶經過 App 建立 Deployment 時,自動添加一個 sidecar 容器到 Pod 中(這裏使用 nginx 做爲 sidecar)。json
本文所用 kubebuilder 版本爲 2.0.1,完整的項目代碼可見:github.com/zwwhdls/Kub…api
第一步是建立出 CRD 及其 Controller,幾行命令就能搞定:
$ export GO111MODULE=on
$ mkdir $GOPATH/src/zww-app
$ cd $GOPATH/src/zww-app
$ kubebuilder init --domain o0w0o.cn --owner "zwwhdls"
$ kubebuilder create api --group app --version v1 --kind App
複製代碼
我這裏作的比較簡單,AppSpec
只定義了一個 deploy 屬性(就是 appsv1.DeploymentSpec
),Controller 中會根據 deploy 屬性生成對應的 Deployment:
type AppSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file
Deploy appsv1.DeploymentSpec `json:"deploy,omitempty"`
}
複製代碼
在完善了 AppSpec
和 Controller 的 Reconcile
函數後,使 Kubebuilder 從新生成代碼,並將 config/crd
下的 CRD yaml 應用到當前集羣:
make
make install
複製代碼
接下來就是用 Kubebuilder 來生成 Webhooks 了:
kubebuilder create webhook --group app --version v1 --kind App
複製代碼
在路徑 api/v1
下生成了一個名爲 app_webhook.go
的文件。能夠看到 Kubebuilder 已經幫你定義了兩個變量:
var _ webhook.Defaulter = &App{}
var _ webhook.Validator = &App{}
複製代碼
這兩個變量分別表示 MutatingWebhookServer 和 ValidatingWebhookServer,在程序啓動的時候,這兩個 Server 會 run 起來。
對於 MutatingWebhookServer,Kubebuilder 預留了 Default()
函數,讓用戶來填寫本身的邏輯:
// Default implements webhook.Defaulter so a webhook will be registered for the type
func (r *App) Default() {
applog.Info("default", "name", r.Name)
// TODO(user): fill in your defaulting logic.
}
複製代碼
對於咱們但願 Webhook 在資源發生什麼樣的變化時觸發,能夠經過這條註釋修改:
// +kubebuilder:webhook:path=/mutate-app-o0w0o-cn-v1-app,mutating=true,failurePolicy=fail,groups=app.o0w0o.cn,resources=apps,verbs=create;update,versions=v1,name=mapp.kb.io
複製代碼
對應的參數爲:
對於 ValidatingWebhookServer,Kubebuilder 的處理與 MutatingWebhookServer 一致,這裏再也不贅述。
方便起見,我只定義了 MutatingWebhookServer 的 Default
函數,爲每一個 App 類型資源的 pod 注入一個 nginx sidecar 容器:
func (r *App) Default() {
applog.Info("default", "name", r.Name)
var cns []core.Container
cns = r.Spec.Deploy.Template.Spec.Containers
container := core.Container{
Name: "sidecar-nginx",
Image: "nginx:1.12.2",
}
cns = append(cns, container)
r.Spec.Deploy.Template.Spec.Containers = cns
}
複製代碼
本文僅分享本地開發測試的調試方案,線上部署方案請參考官方文檔。
首先須要將 MutatingWebhookConfiguration 稍做修改,使得 ApiServer 可以與 Webhook Server 通訊。具體方法以下:
第一步,配置 Server Path;將 service 去掉,換成 url: https://<server_ip>:9443/mutate-app-o0w0o-cn-v1-app
,其中 server_ip
是 Webhook Server 的 ip,若是運行在本地,就是本地的 ip。須要注意的是 url 中的 path 要與 app_webhook.go
中定義的保持一致。
第二步,配置 caBundle;因爲在 Kube 裏,全部與 ApiServer 交互的組件都須要與 ApiServer 進行雙向 TLS 認證,咱們這裏須要先手動簽發自簽名 CA 證書:
$ openssl genrsa -out ca.key 2048
$ openssl req -x509 -new -nodes -key ca.key -subj "/CN=<server_ip>" -days 10000 -out ca.crt
$ openssl genrsa -out server.key 2048
$ cat << EOF >csr.conf
> [ req ]
> default_bits = 2048
> prompt = no
> default_md = sha256
> req_extensions = req_ext
> distinguished_name = dn
>
> [ dn ]
> C = <country>
> ST = <state>
> L = <city>
> O = <organization>
> OU = <organization unit>
> CN = <server_ip>
>
> [ req_ext ]
> subjectAltName = @alt_names
>
> [ alt_names ]
> IP.1 = <server_ip>
>
> [ v3_ext ]
> authorityKeyIdentifier=keyid,issuer:always
> basicConstraints=CA:FALSE
> keyUsage=keyEncipherment,dataEncipherment
> extendedKeyUsage=serverAuth,clientAuth
> subjectAltName=@alt_names
> EOF
$ openssl req -new -key server.key -out server.csr -config csr.conf
$ openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 10000 -extensions v3_ext -extfile csr.conf
複製代碼
證書生成後將 server.key
和 server.crt
拷貝到 Kubebuilder 設置的 webhook server 的私鑰和證書路徑下:
webhook server 的私鑰路徑:$(TMPDIR)/k8s-webhook-server/serving-certs/tls.key
webhook server 的證書路徑:$(TMPDIR)/k8s-webhook-server/serving-certs/tls.crt
注:若是 $(TMPDIR) 爲空,則默認路徑爲 "/tmp/k8s-webhook-server/...",但 android 系統默認路徑爲 "/data/local/tmp/k8s-webhook-server/..."
而 MutatingWebhookConfiguration 中的 caBundle 爲 ca.crt 的 base64 編碼結果。最終 yaml 結果爲:
apiVersion: admissionregistration.k8s.io/v1beta1
kind: MutatingWebhookConfiguration
metadata:
creationTimestamp: null
name: mutating-webhook-configuration
webhooks:
- clientConfig:
caBundle: LS0tLS1CRUdJTiBDRVJ...FLS0tLS0=
url: https://<server_ip>:9443/mutate-app-o0w0o-cn-v1-app
failurePolicy: Fail
name: mapp.kb.io
rules:
...
複製代碼
ValidatingWebhookConfiguration 的修改與 MutatingWebhookConfiguration 相似,只須要注意 server path 與 app_webhook.go
中一致便可。兩個配置文件都修改好以後在集羣中 apply 一下便可。
最後直接在本地運行 CRD Controller 及 Webhook Server:
make run
複製代碼
簡單運行一個 app 試試:
apiVersion: app.o0w0o.cn/v1
kind: App
metadata:
name: app-sample
spec:
deploy:
selector:
matchLabels:
app: app-sample
template:
metadata:
name: sample
labels:
app: app-sample
spec:
containers:
- name: cn
image: daocloud.io/library/redis:4.0.14-alpine
複製代碼
查看是否已經注入了 sidecar 容器:
$ kubectl apply -f config/samples/app_v1_app.yaml
$ kubectl get app
NAME AGE
app-sample 43s
$ kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
app-sample-deploy 0/1 1 0 43s
$ kubectl get po
NAME READY STATUS RESTARTS AGE
app-sample-deploy-5b5cfb9c9b-z8jk5 0/2 ContainerCreating 0 43s
複製代碼