K8s AdmissionWebhook
AdmissionWebhook
什么是AdmissionWebhook,就要先了解k8s中的admission controller, 按照官方的解释是:
admission controller是拦截(经过身份验证)API Server请求的网关,并且可以修改请求对象或拒绝请求
简而言之,它可以认为是拦截器,类似web框架中的middleware。
k8s默认提供很多内置的admission controller,通过kube-apiserver启动命令参数可以 查看到支持的admission controller plugin有哪些。
1 | kube-apiserver --help | grep enable-admission-plugins |
这里enable的admission-plugins如下
1 | --enable-admission-plugins=PersistentVolumeClaimResize,NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota,NodeRestriction,ValidatingAdmissionWebhook,MutatingAdmissionWebhook |
这里不对每个plugin详细说明,网上都可以搜到相关资料。 总体来说,admission-plugins分为三大类:
- 修改类型(mutating)
- 验证类型(validating)
- 既是修改又是验证类型(mutating&validating)
这些admission plugin构成一个顺序链,先后顺序决定谁先调用,但不会影响使用。这里关心的plugin有两个:
一. MutatingAdmissionWebhook, ValidatingAdmissionWebhook
- MutatingAdmissionWebhook: 做修改操作的AdmissionWebhook
- ValidatingAdmissionWebhook: 做验证操作的AdmissionWebhook
引用kubernetes官方博客的一张图来说明MutatingAdmissionWebhook和ValidatingAdmissionWebhook所处的位置:
解释下这个过程:
- api请求到达K8s API Server
- 请求要先经过认证
- kubectl调用需要kubeconfig
- 直接调用K8s api需要证书+bearToken
- client-go调用也需要kubeconfig
- 执行一连串的admission controller,包括MutatingAdmissionWebhook和ValidatingAdmissionWebhook, 先串行-执行MutatingAdmission的Webhook list
- 对请求对象的schema进行校验
- 并行执行ValidatingAdmission的Webhook list
- 最后写入etcd
二. Initializers vs AdmissionWebhook区别
二者都能实现动态可扩展载入admission controller, Initializers是串行执行,在高并发场景容易导致对象停留在uninitialized状态,影响继续调度。 Alpha Initializers特性在k8s 1.14版本被移除了,详情见https://github.com/kubernetes/apimachinery/issues/60。 相比Initializers,官方更推荐AdmissionWebhook;MutatingAdmissionWebhook是串行执行,ValidatingAdmissionWebhook是并行执行,性能更好。
AdmissionWebhook应用场景
Istio相信大家都有听过,istio就是采用AdmissionWebhook实现sidecar容器自动注入。我目前用到的应用场景有两个,当然肯定还有其它应用场景。
- 自动打标签
比如启动一个应用,应用包括deployment、service、ingress; 怎么快速过滤出哪些资源属于应用? 在k8s中,pod、service、ingress 都是独立的资源,通过给这些资源打上label,是最快速的方式。
- 自动注入sidecar容器
应用启动后,应用的监控、日志如何处理?借助sidecar容器注入到其pod中
收集应用日志的sidecar容器可以像下图所示,应用监控的sidecar容器类似
AdmissionWebhook demo
demo 地址: https://github.com/luckymrwang/webhook-demo 实现的功能有:
- 针对
admission-webhook-example=enabled
标签的命名空间生效 - 自动打标签 (pod、deplpoyment、service、ingress自动打上app.kubernetes.io/name=not_available的标签)
- sidecar自动注入(pod自动带上nginx sidecar container)
克隆demo项目
1 | # git clone https://github.com/luckymrwang/webhook-demo |
创建sidecar部署的应用空间 sidecar-injector
1 | # kubectl create ns sidecar-injector |
利用脚本(istio团队提供的)生成CertificateSigningRequest,再生成secret(给后面的webhook-api使用)
1 | # ./deployment/webhook-create-signed-cert.sh \ |
利用脚本生成mutatingwebhook和validatingwebhook yaml,变量CA_BUNDLE自动从kubeconfig中获取
1 | # cat ./deployment/validatingwebhook.yaml | ./deployment/webhook-patch-ca-bundle.sh > ./deployment/validatingwebhook-ca-bundle.yaml |
编译镜像
1 | # docker build --rm -t test/admission-webhook-example:v1 -f docker/Dockerfile . |
部署webhook-api
1 | # kubectl apply -f ./deployment/mutatingwebhook-ca-bundle.yaml -n sidecar-injector |
创建应用空间 injection
并打 label,只对admission-webhook-example标签的命名空间生效
1 | # kubectl create ns injection |
这里sidecar是nginx, sidecar依赖的configmap
1 | # kubectl apply -f ./deployment/nginxconfigmap.yaml -n injection |
部署一个busybox,sidecar是nginx
1 | # kubectl apply -f ./deployment/sleep.yaml -n injection |
pod自动打上app.kubernetes.io/name标签, pod中有两个container
1 | # kubectl get pod sleep-5588cb5f94-5dl8f --show-labels -n injection |
service自动打上app.kubernetes.io/name标签
1 | # kubectl get svc sleep --show-labels -n injection |
ingress自动打上app.kubernetes.io/name标签
1 | # kubectl get ingresses.extensions sleep --show-labels -n injection |
开发调试
如果K8s装在远端Server上,在本地如何调试?
1、在goland中启动webhook api, 监听在本地6443
2、利用ssh反向代理
1 | ssh -R 6443:127.0.0.1:6443 root@<k8s-master-ip> |
监听在K8s Server上的6443,会重定向到本地的6443
3、利用K8s headless service指向k8s-master-ip的6443
1 | [root@node66 ~]# cat /data/deployment/service.yaml |
执行上述操作步骤后,在本地就可以设置断点调试了
发散思维
AdmissionWebhook多集群应用
如果有多个k8s集群,那是不是要在每个集群都启动一个Webhook API呢?所以就有了下面的架构,所有集群共享 一个Webhook API。
cluster c1、c2中的Webhook配置会指向各自集群内部的service,这个service其实是headless service, 它指向的是cluster A的service(需要暴露给其它集群能够访问,nodePort也可以),这样所有集群就共享一个Webhook API了。
总结
AdmissionWebhook可以像拦截器一样拦截K8s api请求,要实现修改功能用MutatingAdmissionWebhook, 实现验证功能用ValidatingAdmissionWebhook。
写一个AdmissionWebhook API的要点:
1、AdmissionReview结构体 request请求信息通过AdmissionReview的Request字段可以获取到; response通过AdmissionReview的Response字段设置返回
1 | // AdmissionReview describes an admission review request/response. |
2、mutating是通过json patch方式实现的
对应的结构体定义如下:
1 | type patchOperation struct { |
参考链接
- http://www.iceyao.com.cn/2019/06/05/%E7%8E%A9%E8%BD%ACK8s-AdmissionWebhook/
- https://github.com/morvencao/kube-mutating-webhook-tutorial
- https://github.com/banzaicloud/admission-webhook-example
- https://banzaicloud.com/blog/k8s-admission-webhooks/
- https://kubernetes.io/blog/2019/03/21/a-guide-to-kubernetes-admission-controllers/
- JSON Patch