K8S项目实践(06): 动手实现webhook
简介
Webhook就是一种HTTP回调,用于在某种情况下执行某些动作,Webhook不是K8S独有的,很多场景下都可以进行Webhook,比如在提交完代码后调用一个Webhook自动构建docker镜像
K8S中提供了自定义资源类型和自定义控制器来扩展功能,还提供了动态准入控制,其实就是通过Webhook来实现准入控制,分为两种:验证性质的准入 Webhook (Validating Admission Webhook) 和 修改性质的准入 Webhook (Mutating Admission Webhook)
Admission Webhook有哪些使用场景?如下
- 在资源持久化到ETCD之前进行修改(Mutating Webhook),比如增加init Container或者sidecar Container
- 在资源持久化到ETCD之前进行校验(Validating Webhook),不满足条件的资源直接拒绝并给出相应信息
http://blog.mospan.cn/post/img/webhook/webhook.webp
Webhook可以理解成Java Web开发中的Filter,每个请求都会经过Filter处理,从图中可以看到,先执行的是Mutating Webhook,它可以对资源进行修改,然后执行的是Validating Webhook,它可以拒绝或者接受请求,但是它不能修改请求。
K8S中有已经实现了的Admission Webhook列表,详情参考每个准入控制器的作用是什么?
示例原理
我们以一个简单的Webhook作为例子,该Webhook会在创建Deployment资源的时候检查它是否有相应的标签,如果没有的话,则加上(Mutating Webhook),然后在检验它是否有相应的标签(Validating Webhook),有则创建该Deployment,否则拒绝并给出相应错误提示。
所有代码都在:
git@github.com:mospany/admission-webhook-example.git
验证
编译打包
cd admission-webhook-example/v1
DOCKER_USER=mospany bash ./build
它将生成镜像并上传到docker.io/mospany/admission-webhook-example:v1。
检查是否开启了动态准入控制
查看APIServer是否开启了MutatingAdmissionWebhook和ValidatingAdmissionWebhook
# 获取apiserver pod名字
apiserver_pod_name=`kubectl get --no-headers=true po -n kube-system | grep kube-apiserver | awk '{ print $1 }'`
# 查看api server的启动参数plugin
kubectl get po $apiserver_pod_name -n kube-system -o yaml | grep plugin
如果输出如下,说明已经开启
- --enable-admission-plugins=NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook
否则,需要修改启动参数,请不然直接修改Pod的参数,这样修改不会成功,请修改配置文件/etc/kubernetes/manifests/kube-apiserver.yaml,加上相应的插件参数后保存,APIServer的Pod会监控该文件的变化,然后重新启动。
创建RBAC
由于我们的webhook会对资源进行修改,所以需要单独给一个ServiceAccount,在K8S集群中直接创建即可
kubectl apply -f deployment/rbac.yaml
效果如下:
[root@k8s-master deployment]# kubectl apply -f rbac.yaml
serviceaccount/admission-webhook-example-sa unchanged
clusterrole.rbac.authorization.k8s.io/admission-webhook-example-cr unchanged
clusterrolebinding.rbac.authorization.k8s.io/admission-webhook-example-crb unchanged
[root@k8s-master deployment]#
证书认证
K8S集群默认是HTTPS通信的,所以APiserver调用webhook的过程也是HTTPS的,所以需要进行证书认证,证书认证相当于是给Service的域名进行认证(Service后面会创建),将Service域名放到认证请求server.csr文件中,然后创建一个K8S证书签署请求资源CertificateSigningRequest,APIServer签署该证书后生成server-cert.pem,再将最初创建的私钥server-key.pem和签署好的证书server-cert.pem放到Secret中供Deployment调用,详细过程看脚本
webhook-create-signed-cert.sh
认证很简单,执行该脚本即可,会创建一个名为admission-webhook-example-certs的Secret
./deployment/webhook-create-signed-cert.sh
这一步顺便把Service创建了,因为证书是给该Service的域名颁发的
kubectl apply -f deployment/service.yaml
部署Deployment
kubectl apply -f deployment.yaml
稍等片刻如果有类似如下输出说明Pod已经运行
[root@k8s-master deployment]# kubectl apply -f deployment.yaml
deployment.apps/admission-webhook-example-deployment unchanged
[root@k8s-master deployment]# kubectl get pod -A -o wide | grep web
default admission-webhook-example-deployment-7dd75cffb6-24bhg 1/1 Running 2 (13d ago) 57d 10.244.235.217 k8s-master <none> <none>
[root@k8s-master deployment]# kubectl get pod -A | grep web
default admission-webhook-example-deployment-7dd75cffb6-24bhg 1/1 Running 2 (13d ago) 57d
[root@k8s-master deployment]#
部署ValidatingWebhook
首先包含一个namespaceSelector,表示此webhook只针对有admission-webhook-example标签的namespace生效,当然也可以去掉
namespaceSelector:
matchLabels:
admission-webhook-example: enabled
查看编排文件deployment/validatingwebhook.yaml,里面有一个占位符${CA_BUNDLE}
clientConfig:
service:
name: admission-webhook-example-svc
namespace: default
path: "/validate" # Path是我们自己定义的
caBundle: ${CA_BUNDLE}
这个是什么呢?webhook是APIServer调用的,此时APIServer相当于是一个客服端,webhook是一个服务端,可以对比下平时上网,打开https网站时是谁在验证域名的证书?是内置在浏览器里面的根证书在做验证,所以这里的CA_BUNDLE就类似于APIServer调用webhook的根证书,它去验证webhook证书。
所以先填充这个CA_BUNDLE后再执行
# 填充占位符
cat deployment/validatingwebhook.yaml | ./deployment/webhook-patch-ca-bundle.sh > /tmp/validatingwebhook.yaml
# 部署
kubectl apply -f /tmp/validatingwebhook.yaml
验证
1、给default namespace添加label
kubectl label namespace default admission-webhook-example=enabled
2、部署sleep.yaml
kubectl apply -f deployment/sleep.yaml
FAQ
error: unable to recognize “STDIN”: no matches for kind “CertificateSigningRequest” in version “certificates.k8s.io/v1beta1”
当执行`bash webhook-create-signed-cert.sh`时出现如下错误:
error: unable to recognize “STDIN”: no matches for kind “CertificateSigningRequest” in version “certificates.k8s.io/v1beta1”`
需修改webhook-create-signed-cert.sh相关内容为:
# create server cert/key CSR and send to k8s API
cat <<EOF | kubectl create -f -
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: ${csrName}
spec:
groups:
- system:authenticated
request: $(cat ${tmpdir}/server.csr | base64 | tr -d '\n')
signerName: kubernetes.io/kube-apiserver-client
usages:
- digital signature
- key encipherment
- client auth
EOF
详见:missing required field “signerName” #29
no matches for kind “ValidatingWebhookConfiguration” in version “admissionregistration.k8s.io/v1beta1”
当执行`kubectl apply -f validatingwebhook.yaml`时出现错误:
no matches for kind “ValidatingWebhookConfiguration” in version “admissionregistration.k8s.io/v1beta1”
需改为:
admissionReviewVersions: ["v1","v1beta1"]
sideEffects: None
详见: Pls help me in configuring/using OPA in minikbe
x509: certificate specifies an incompatible key usage
参考资料
作者:mospan
微信关注:墨斯潘園
本文出处:http://mospany.github.io/2023/03/05/k8s-webhook-example/
文章版权归本人所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。