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

相关文章
相关标签/搜索