《使用 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
复制代码