之前写了一个简单的webhook 的tls server,并且写了一个简单的validate的api 用于实现资源的拦截。之前有提到webhook有两类,第二类就是mutatingadmissionwebhook,即对资源进行修改,仔细想想不会太意外,比如istio本质上不就是mutatingadmissionwebhook么,在启动的时候直接sidecar方式插入一个container,然后通过该container进行各类服务收集/治理,那练手的话可以自己插入一个redis 容器,模拟istio的sidecar容器创建。
思路
同样整理一下思路先:
- 和validateadmission 不一样,mutateadmission 需要的是对资源的修改,所以需要先定义一个修改操作的结构体,即所谓的patch:
- 由于需要插入的是一个sidecar容器,所以需要先定义一个sidecar的容器资源
- 然后webhook拦截api-server的请求,获取admissionreview,获取创建的资源信息
- 然后对admissionreview 进行patch 操作,将sidecar的容器注入进去。
- 然后回调api-server
理完思路写代码,先看下patch的结构体
// 定义patch对象 type patchOperation struct { Op string `json:"op"` Path string `json:"path"` Value interface{} `json:"value,omitempty"` }
op是操作的类型,比如add,del, path是修改的资源对象的路径,value是修改资源对象的值。
然后将需要插入的sidecar 容器解析出来
// 需要插入的sidecar 容器 type sideConfig struct { Containers []corev1.Container `yaml:"containers"` } func loadSideCarConfig() (*sideConfig, error) { data, err := ioutil.ReadFile("/config/sidecar.yaml") if err != nil { return nil, err } var cfg sideConfig if err := yaml.Unmarshal(data, &cfg); err != nil { return nil, err } return &cfg, nil }
现在有了具体的sidecar后,就只需要将插入sidecar 容器这个动作封装成patch ,以json序列化一下然后返回给api-server。
// 返回具体的admissionresponse return &admissionv1.AdmissionResponse{ Allowed: allowed, Patch: patchBytes, PatchType: func() *admissionv1.PatchType { pt := admissionv1.PatchTypeJSONPatch return &pt }(), }
之后进行镜像的打包和上传
docker build -t registry.cn-shanghai.aliyuncs.com/zyxpaomian/webhook:v1.8 . docker push registry.cn-shanghai.aliyuncs.com/zyxpaomian/webhook:v1.8
定义一个admissionwebhook, 只正对deployment和service的创建做拦截
apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration metadata: name: admission-registry-mutate webhooks: - name: zyx.test.admission-registry-mutate clientConfig: service: namespace: default name: admission-registry path: "/v1/api/mutate" caBundle: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURuakNDQW9hZ0F3SUJBZ0lVT2RVeWVCenVVcFdIenFvMGczOE5XeE1SNm9jd0RRWUpLb1pJaHZjTkFRRUwKQlFBd1p6RUxNQWtHQTFVRUJoTUNRMDR4RVRBUEJnTlZCQWdUQ0ZOb1lXNW5hR0ZwTVJFd0R3WURWUVFIRXdoVAphR0Z1WjJoaGFURU1NQW9HQTFVRUNoTURhemh6TVE4d0RRWURWUVFMRXdaVGVYTjBaVzB4RXpBUkJnTlZCQU1UCkNtdDFZbVZ5Ym1WMFpYTXdIaGNOTWpFd05USTBNRGMwT1RBd1doY05Nall3TlRJek1EYzBPVEF3V2pCbk1Rc3cKQ1FZRFZRUUdFd0pEVGpFUk1BOEdBMVVFQ0JNSVUyaGhibWRvWVdreEVUQVBCZ05WQkFjVENGTm9ZVzVuYUdGcApNUXd3Q2dZRFZRUUtFd05yT0hNeER6QU5CZ05WQkFzVEJsTjVjM1JsYlRFVE1CRUdBMVVFQXhNS2EzVmlaWEp1ClpYUmxjekNDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFLTWJLT0Q5WGMzMDhlWlQKK0J2MGYzR3QxTGtzWk1jUXNUUG5rQXpWeVRQSDdFSkpORkVoVzI0UE8yNzgvaTNWcEdPNU12MVZCcW1rcGwrSgorZ1dwZW5TSDNIbTdsOTZmckt4TmRGWG94Y3VLaWNPOHhvVUVxWCt0cVIvR0toUGYrcjBCWXFUR1o1aVVCZGdVCjU0STJIQjREc1Exc0lCY1JBUms5WVJ5ZjRLYXRZVVEzdGRuNFRyNmFhUXg5OE9Gd1hUNXpiNktINVQ2MTNKdk8KUExpV0JNWUxUZG5WZ256ZzVXeDUreDkvV0FtaTJSK0JjTllLUjY0RWgzeWJZam5NeGtGL1JEMTZPSTlFOTJNRApEaVB6U09oemplQ3RIY1NRcStpTllIQzhzSUJlRFlxL256bTFDR0hTL1NpMDdZVFBjZTBXeE5RelRpMlM2dlZXCkdrWGVPaWNDQXdFQUFhTkNNRUF3RGdZRFZSMFBBUUgvQkFRREFnRUdNQThHQTFVZEV3RUIvd1FGTUFNQkFmOHcKSFFZRFZSME9CQllFRlB0S2U2TXFoTjlLaEJ5S3Ntdy9sR3pZd25iL01BMEdDU3FHU0liM0RRRUJDd1VBQTRJQgpBUUFqNnVyVTVhS0I5MWZuQTF2dE1DVjZRVHFmenMrd1dFcHZVbElXOHdZYXVydDZPWUc1YkxFSHoyRUJJRXplCjMvcGM4ZUkvamJSU0JNZDJGbk9Sa0pVTmh5MDBBZE05MTRFUFo5Y0FIOFpGUVI1UTZCUmw1Qnl6Mk5jOHN3dGMKMmtraW0vWUJ4TkFFYnFLQjBhTmw0TW5LbTc3TzgzSXpwREN2SzBWdlRCVXJVYnlzd1l6d0lCaEZxSTl5ZUo2Zgo1U1JpemdrUHVYRG1qSlZiN092dzNTNkxRT1o4VmdJRS9wZGJvRTBsTHpmZ2pROWVDVFM0ZmRXSE9oNGp1TXJMCklqeG1pMHhOVVVkTXdPeFNvQlJKa3J3eU1Fa1FoN2swYmZROFdsRkFNenVST29RVmVraU1BNkhLeDdQOFd1UmUKYzhKRkVOVmk5VEVMTXkwWHpJNWhoOVlwCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K rules: - operations: [ "CREATE" ] apiGroups: ["apps", ""] apiVersions: ["v1"] resources: ["deployments","services"] admissionReviewVersions: [ "v1" ] sideEffects: None
进行测试, 和之前一样编写一个deployment,里面只有一个nginx的container:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment labels: app: nginx spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: docker.io/nginx:latest ports: - containerPort: 80
然后进行apply:
可以看到这个deployment 下面有2个container 在初始化,这第二个container 就是mutatingadmission 拦截后注入的sidecar 容器。