使用 Kubebuilder 建立自定義 K8s AdmissionWebhooks

《使用 Kubebuilder 建立自定義 K8s AdmissionWebhooks》 最先發布在 blog.hdls.me/15708754600…html

Kubebuilder 除了能夠構建 CRD API 及其 Controller 以外,還能構建 AdmissionWebhooks。這篇文章就來詳細分析 Kubebuilder 如何構建 AdmissionWebhooks。node

K8s 的 AdmissionWebhooks

首先要知道,在 K8s 裏 AdmissionWebhooks 是什麼,目的是什麼。android

先說場景,若是咱們須要在 pod 建立出來以前,對其進行配置修改或者檢查,這部分工做若是放在 ApiServer 裏,須要管理員在 ApiServer 中將其編譯成二進制文件,若是配置修改想作成自定義的形式會很是麻煩。而 Admission controllers 就是爲這種場景而生的工具,以插件的形式附着到 ApiServer 中,AdmissionWebhooks 就是其中一種准入插件。nginx

K8s 的 AdmissionWebhooks 分兩種:MutatingAdmissionWebhookValidatingAdmissionWebhook,兩者合起來就是一個特殊類型的 admission controllers,一個處理資源更改,一個處理驗證。git

在以前一篇文章《[譯]深刻剖析 Kubernetes MutatingAdmissionWebhook》中,給出了很是詳細的 MutatingAdmissionWebhook 的教程,其中主要作了三件事情:github

  1. MutatingWebhookConfiguration:MutatingAdmissionWebhook 向 ApiServer 註冊的配置;
  2. MutatingAdmissionWebhook 自己:一種插件形式的 admission controller,須要向 ApiServer 註冊本身;
  3. Webhook Admission Server:一個附着到 k8s ApiServer 的 http server,接收 ApiServer 的請求。

那麼用 Kubebuilder 構建 AdmissionWebhooks 的話,Kubebuilder 會爲咱們自動生成 Webhook Server,並留下幾個函數讓咱們添加自有邏輯。web

建立自定義 AdmissionWebhooks

這裏使用一個簡單的場景作一個演示,咱們自定義一個名爲 App 資源,當用戶建立一個 App 實例時,咱們根據用戶的描述建立出一個 Deployment。redis

而後咱們添加一個 MutatingAdmissionWebhook,當用戶經過 App 建立 Deployment 時,自動添加一個 sidecar 容器到 Pod 中(這裏使用 nginx 做爲 sidecar)。json

本文所用 kubebuilder 版本爲 2.0.1,完整的項目代碼可見:github.com/zwwhdls/Kub…api

初始化 API 及 Controller

第一步是建立出 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
複製代碼

建立 Webhook Server

接下來就是用 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
複製代碼

對應的參數爲:

  • failurePolicy:表示 ApiServer 沒法與 webhook server 通訊時的失敗策略,取值爲 "ignore" 或 "fail";
  • groups:表示這個 webhook 在哪一個 Api Group 下會收到請求;
  • mutating:這個參數是個 bool 型,表示是不是 mutating 類型;
  • name:webhook 的名字,須要與 configuration 中對應;
  • path:webhook 的 path;
  • resources:表示這個 webhook 在哪一個資源發生變化時會收到請求;
  • verbs:表示這個 webhook 在資源發生哪一種變化時會收到請求,取值爲 「create「, "update", "delete", "connect", 或 "*" (即全部);
  • versions:表示這個 webhook 在資源的哪一個 version 發生變化時會收到請求;

對於 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
}
複製代碼

運行 Webhook Server

本文僅分享本地開發測試的調試方案,線上部署方案請參考官方文檔

首先須要將 MutatingWebhookConfiguration 稍做修改,使得 ApiServer 可以與 Webhook Server 通訊。具體方法以下:

配置 Server Path

第一步,配置 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.keyserver.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
複製代碼

相關文章
相關標籤/搜索