zoukankan      html  css  js  c++  java
  • admission webhook 初探(编译篇)

    编译开源的 admission webhook

    本文参考的代码 admission-webhook-examplek8s-admission-webhook 文档

    admission webhook 的作用我简单的总结下,当用户的请求到达 k8s apiserver 后,apiserver 根据 MutatingWebhookConfigurationValidatingWebhookConfiguration 的配置,先调用 MutatingWebhookConfiguration 去修改用户请求的配置文件,最后会调用 ValidatingWebhookConfiguration 来验证这个修改后的配置文件是否合法。

    我们可以利用 mutating 的机制,将一些特殊的配置自动加上,而不用用户来操心。同时也可以在 validating 中编写代码设置自己的规则,看请求是否合法。

    因为已经研究过怎么编写 webhook 代码/怎么部署 webhook,要使用好 webhook 需要注意几个点:

    • webhook 的本质是个 http server,因此,需要用代码实现这么一个 server,供 apiserver 调用
    • mutating webhook 和 validating webhook 分别实现了不同的功能,这两者没有直接联系,本质上说,如果修改/验证都需要,我们需要编写两个 http server
    • 编写好的 webhook,建议使用 k8s deployment 部署,同时使用 service 来暴露能力
    • apiserver 是否调用 webhook,是由 k8s MutatingWebhookConfiguration/ValidatingWebhookConfiguration 来定义的
    • 需要注意 apiserver 和 webhook 通信时的身份认证和权限控制问题

    编译示例 webhook

    https://github.com/cnych/admission-webhook-example 项目中将 mutating、validating 的功能都放到一个 http server 中了,这当然也是可以的。但是 k8s MutatingWebhookConfiguration/ValidatingWebhookConfiguration 肯定是需要都部署的。

    [root@localhost lihao04]# cd /root/lihao04
    [root@localhost lihao04]# git clone https://github.com/cnych/admission-webhook-example.git
    [root@localhost lihao04]# cd admission-webhook-example/
    [root@localhost github.com]# export GOPATH=/root/lihao04/go/
    [root@localhost github.com]# mkdir -p /root/lihao04/go/src/github.com/cnych/
    [root@localhost github.com]# ln -s /root/lihao04/admission-webhook-example /root/lihao04/go/src/github.com/cnych/
    [root@localhost github.com]# export http_proxy=http://jarvis:lihao-root@10.191.67.135:3128/
    [root@localhost github.com]# export https_proxy=http://jarvis:lihao-root@10.191.67.135:3128/
    [root@localhost github.com]# cd /root/lihao04/go/src/github.com/cnych/admission-webhook-example
    [root@localhost admission-webhook-example]# dep ensure -v
    ...
    
    [root@localhost admission-webhook-example]# export GO111MODULE=on
    [root@localhost admission-webhook-example]# CGO_ENABLED=0 GOOS=linux go build -mod=vendor -a -installsuffix cgo -o admission-webhook-example
    go: creating new go.mod: module github.com/cnych/admission-webhook-example
    go: copying requirements from Gopkg.lock
    go: converting Gopkg.lock: stat github.com/json-iterator/go@f2b4162afba35581b6d4a50d3b8f34e33c144682: repo version lookup disabled by -mod=vendor
    go: converting Gopkg.lock: stat golang.org/x/text@v0.3.0: repo version lookup disabled by -mod=vendor
    go: converting Gopkg.lock: stat github.com/google/gofuzz@24818f796faf91cd76ec7bddd72458fbced7a6c1: repo version lookup disabled by -mod=vendor
    go: converting Gopkg.lock: stat k8s.io/api@12444147eb1150aa5c80d2aae532cbc5b7be73d0: repo version lookup disabled by -mod=vendor
    go: converting Gopkg.lock: stat github.com/opencontainers/go-digest@v1.0.0-rc1: repo version lookup disabled by -mod=vendor
    go: converting Gopkg.lock: stat github.com/golang/glog@23def4e6c14b4da8ac2ed8007337bc5eb5007998: repo version lookup disabled by -mod=vendor
    go: converting Gopkg.lock: stat github.com/modern-go/reflect2@1df9eeb2bb81f327b96228865c5687bc2194af3f: repo version lookup disabled by -mod=vendor
    go: converting Gopkg.lock: stat k8s.io/apimachinery@e386b2658ed20923da8cc9250e552f082899a1ee: repo version lookup disabled by -mod=vendor
    go: converting Gopkg.lock: stat k8s.io/apiserver@88d4601515c27f180f7efc8705e4cc18dc19100d: repo version lookup disabled by -mod=vendor
    go: converting Gopkg.lock: stat golang.org/x/net@892bf7b0c6e2f93b51166bf3882e50277fa5afc6: repo version lookup disabled by -mod=vendor
    go: converting Gopkg.lock: stat gopkg.in/yaml.v2@v2.1.1: repo version lookup disabled by -mod=vendor
    go: converting Gopkg.lock: stat github.com/gogo/protobuf@v1.0.0: repo version lookup disabled by -mod=vendor
    go: converting Gopkg.lock: stat github.com/spf13/pflag@v1.0.0: repo version lookup disabled by -mod=vendor
    go: converting Gopkg.lock: stat github.com/docker/distribution@34c706e759240975178df82495f147559cc0edc1: repo version lookup disabled by -mod=vendor
    go: converting Gopkg.lock: stat github.com/modern-go/concurrent@bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94: repo version lookup disabled by -mod=vendor
    go: converting Gopkg.lock: stat k8s.io/apiextensions-apiserver@f584b16eb23bd2a3fd292a027d698d95db427c5d: repo version lookup disabled by -mod=vendor
    go: converting Gopkg.lock: stat github.com/ghodss/yaml@v1.0.0: repo version lookup disabled by -mod=vendor
    go: converting Gopkg.lock: stat k8s.io/kubernetes@efe960cdc41ee7b18d408128dfb80babb5bc746a: repo version lookup disabled by -mod=vendor
    go: converting Gopkg.lock: stat gopkg.in/inf.v0@v0.9.0: repo version lookup disabled by -mod=vendor
    
    

    制作镜像

    将编译后的代码打包进 image,这样就可以使用 k8s deployment 来部署

    Dockerfile

    注意修改 FROM 的

    FROM alpine:3.10.3
    
    ADD admission-webhook-example /admission-webhook-example
    
    ENTRYPOINT ["./admission-webhook-example"]
    

    制作命令

    [root@localhost admission-webhook-example]# docker build --no-cache -t docker-registry.lihao04.virtual/jarvis-image/admission-webhook-example:v1 .
    Sending build context to Docker daemon     43MB
    Step 1/3 : FROM docker-registry.lihao04.virtual/jarvis-image/alpine:3.10.3
    3.10.3: Pulling from jarvis-image/alpine
    89d9c30c1d48: Already exists
    Digest: sha256:e4355b66995c96b4b468159fc5c7e3540fcef961189ca13fee877798649f531a
    Status: Downloaded newer image for docker-registry.lihao04.virtual/jarvis-image/alpine:3.10.3
     ---> 965ea09ff2eb
    Step 2/3 : ADD admission-webhook-example /admission-webhook-example
     ---> 2c65bd92673c
    Step 3/3 : ENTRYPOINT ["./admission-webhook-example"]
     ---> Running in 41ac87194f86
    Removing intermediate container 41ac87194f86
     ---> 7c57f014ab9a
    Successfully built 7c57f014ab9a
    Successfully tagged docker-registry.lihao04.virtual/admission-webhook-example:v1
    

    k8s 的配置

    启用 MutatingAdmissionWebhook 和 ValidatingAdmissionWebhook

    MutatingAdmissionWebhookValidatingAdmissionWebhook 默认是不启用的,apiserver 想调用 webhook,还得 enable 相关的能力

    [root@master ~]# kubectl get pods kube-apiserver-master.lihao04.virtual -n kube-system -o yaml|grep enable
        - --enable-admission-plugins=NodeRestriction
        - --enable-bootstrap-token-auth=true
      enableServiceLinks: true
    

    因为 enable-admission-plugins 缺失 feature,我们要 enable

    # 修改 /etc/kubernetes/manifests/kube-apiserver.yaml
    - --enable-admission-plugins=NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook
    

    修改配置文件后立刻生效

    [root@master manifests]# kubectl get pods kube-apiserver-master.lihao04.virtual -n kube-system -o yaml|grep enable
        - --enable-admission-plugins=NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook
        - --enable-bootstrap-token-auth=true
      enableServiceLinks: true
    
    # 确实重启过
    [root@master manifests]# kubectl get pod -n kube-system|grep api
    kube-apiserver-master.lihao04.virtual            1/1     Running            0          54s
    

    部署 webhook

    prepare

    这个 server 是供 apiserver 调用的,它的运行代码正是我们编译的 admission-webhook-example,他使用的镜像正是我们构建的 docker-registry.lihao04.virtual/admission-webhook-example:v1

    部署前,需要先创建 CertificateSigningRequest、secret 等,这个是给 apiserver 与 webhook 通信做身份认证的

    [root@master lihao04]# cd /root/lihao04/admission-webhook-example/deployment
    [root@master lihao04]# ./webhook-create-signed-cert.sh
    

    部署 webhook server

    [root@master deployment]# cat deployment.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: admission-webhook-example-deployment
      labels:
        app: admission-webhook-example
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: admission-webhook-example
      template:
        metadata:
          labels:
            app: admission-webhook-example
        spec:
          containers:
            - name: admission-webhook-example
              image: docker-registry.lihao04.virtual/jarvis-image/admission-webhook-example:v2
              #image: cnych/admission-webhook-example:v1
              args:
                - -tlsCertFile=/etc/webhook/certs/cert.pem
                - -tlsKeyFile=/etc/webhook/certs/key.pem
                - -alsologtostderr
                - -v=4
                - 2>&1
              volumeMounts:
                - name: webhook-certs
                  mountPath: /etc/webhook/certs
                  readOnly: true
          volumes:
            - name: webhook-certs
              secret:
                secretName: admission-webhook-example-certs
    
    [root@master deployment]# kubectl apply -f deployment.yaml
    deployment.apps/admission-webhook-example-deployment create
    

    部署 service

    让该 deployment(webhook server) 能被 apiserver 访问,必须套一个 service,注意,访问 service 的 443 会访问 webhook deployment 中的 pod 的 443 端口,而 pod 中的服务正是启动在 443 端口上,这个大家可以看 admission-webhook-example 源码。

    [root@master deployment]# cat service.yaml
    apiVersion: v1
    kind: Service
    metadata:
      name: admission-webhook-example-svc
      labels:
        app: admission-webhook-example
    spec:
      ports:
      - port: 443
        targetPort: 443
      selector:
        app: admission-webhook-example
    
    [root@master deployment]#  kubectl create -f service.yaml
    

    在 k8s 中使能 webhook

    当 webhook server 部署好了之后,apiserver 如何知道它要访问 webhook server?

    因此需要配置 k8s 集群,让 webhook 被 k8s 感知到,并能在适当的时候调用到。

    因为 webhook 有两个部分的功能

    • 修改
    • 验证

    修改在先,验证在后,因此这两个功能都需要独立配置;

    配置 ValidatingWebhookConfiguration

    配置名为:ValidatingWebhookConfiguration

    [root@master deployment]# cat validatingwebhook-ca-bundle.yaml
    apiVersion: admissionregistration.k8s.io/v1beta1
    kind: ValidatingWebhookConfiguration
    metadata:
      name: validation-webhook-example-cfg
      labels:
        app: admission-webhook-example
    webhooks:
      - name: required-labels.qikqiak.com
        clientConfig:
          service:
            name: admission-webhook-example-svc
            namespace: default
            path: "/validate"
          # 通过 kubectl config view --raw --flatten -o json|grep certificate-authority-data 获得
    	  caBundle: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRFNU1Ea3lOekEzTVRZd04xb1hEVEk1TURreU5EQTNNVFl3TjFvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTUlFCmZnRDJva2RpK3BiK2U5VmVMRFJaamxQRG9UTGpwS1NVc05OaHVSbW9Xbmoyek9od3J5cGRmd2V3QTJtdlYyVGIKdDFKYTRHSENWRmNZeWVveFE2REw5SkVLWGZoOGxKQkZxS0JuNzc3RFlFWXl5b1hqWjE5Wk5NUUZzUnZhL2RtRgpPVGlrR3ZIdDZqS0xiQmhqVlRhNTlPbmhXZTFiRUlGR0I3SXNlcG8vRXEwSDhPMW53UlVONEVYRnBPMXhiQktxCk5saS9IQ0FLZ2pWRlQrWjdpUjY3QXlMbzFUUk5JRkl1VkhDU2xRaERjaU9xTE1OM1FLeWN5ZnY2VXFsNllSeW8KeW42eGFkb1JQM2RJOUlnNWpJQ3c0dUNZeEJlbW14YnNURkQ5Tm90YkdqSnBNeVhKbGhjcGM1aVlUVDR3WUJMUwpRakNkcWlJYmNzODNSWkhqTW1jQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFMeTN4ZWZFYmtUaUgxczVmbHlMVzZEb08wNUgKdWMrUW83RmdnSmlqZjA3eFNwVXpYVTRiVjhtSk55RlFJS1BiRzhHa3dRRFhiVzMvYXlZa2gyT3Z1Si9Bb2U5dAoxeGlFL3NMbTlGeTdlMTJRenZFcjlsanVpMzJWSjBtYkRpVm9Sd3FEMFh0R1JnZGhVeGltdU9PZzd0aXd1WUtkCkhBY0NDdUNReHRnWXpuQXdnVnJYVWpaSnRRV2RoNUpiVUwrZGp6ejYxSVdpTytiQSt2c0d3cjRoV09SVlk2K0sKcFNIMlhiL2JIaG5XOHBSREdsbEl2U2piZmlzY0d6SU1tUHN5end4ZG9sRXlqcnpCdGdLaW5pcjJvZmdJcEVHQwpuUzdZSDR3WE1kTkp4TEQ3U2JWdWpDbEwwQzhaUWFvamFkNUlkU2FZaldjSHFKelFmczRaKzQvRHN1OD0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
        rules:
          - operations: [ "CREATE" ]
            apiGroups: ["apps", ""]
            apiVersions: ["v1"]
            resources: ["deployments","services"]
    	# 对 namespace 中打了 admission-webhook-example = enabled 的进行验证,其他 namespace 不验证
        namespaceSelector:
          matchLabels:
            admission-webhook-example: enabled
    
    [root@master deployment]# kubectl apply -f validatingwebhook-ca-bundle.yaml
    validatingwebhookconfiguration.admissionregistration.k8s.io/validation-webhook-example-cfg created
    

    测试

    [root@master deployment]# cat sleep.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: sleep
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: sleep
      template:
        metadata:
          labels:
            app: sleep
        spec:
          containers:
          - name: sleep
            image: tutum/curl
            command: ["/bin/sleep","infinity"]
            imagePullPolicy: IfNotPresent
    
    [root@master deployment]# kubectl apply -f sleep.yaml
    Error from server (required labels are not set): error when creating "sleep.yaml": admission webhook "required-labels.qikqiak.com" denied the request: required labels are not set
    

    可以发现,如果没有带特定的内容,会报错,达到了验证的效果

    配置 MutatingWebhookConfiguration

    配置名为:MutatingWebhookConfiguration

    [root@master deployment]# cat mutatingwebhook-ca-bundle.yaml
    apiVersion: admissionregistration.k8s.io/v1beta1
    kind: MutatingWebhookConfiguration
    metadata:
      name: mutating-webhook-example-cfg
      labels:
        app: admission-webhook-example
    webhooks:
      - name: mutating-example.qikqiak.com
        clientConfig:
          service:
            name: admission-webhook-example-svc
            namespace: default
            path: "/mutate"
          caBundle: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN5RENDQWJDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRFNU1Ea3lOekEzTVRZd04xb1hEVEk1TURreU5EQTNNVFl3TjFvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTUlFCmZnRDJva2RpK3BiK2U5VmVMRFJaamxQRG9UTGpwS1NVc05OaHVSbW9Xbmoyek9od3J5cGRmd2V3QTJtdlYyVGIKdDFKYTRHSENWRmNZeWVveFE2REw5SkVLWGZoOGxKQkZxS0JuNzc3RFlFWXl5b1hqWjE5Wk5NUUZzUnZhL2RtRgpPVGlrR3ZIdDZqS0xiQmhqVlRhNTlPbmhXZTFiRUlGR0I3SXNlcG8vRXEwSDhPMW53UlVONEVYRnBPMXhiQktxCk5saS9IQ0FLZ2pWRlQrWjdpUjY3QXlMbzFUUk5JRkl1VkhDU2xRaERjaU9xTE1OM1FLeWN5ZnY2VXFsNllSeW8KeW42eGFkb1JQM2RJOUlnNWpJQ3c0dUNZeEJlbW14YnNURkQ5Tm90YkdqSnBNeVhKbGhjcGM1aVlUVDR3WUJMUwpRakNkcWlJYmNzODNSWkhqTW1jQ0F3RUFBYU1qTUNFd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFMeTN4ZWZFYmtUaUgxczVmbHlMVzZEb08wNUgKdWMrUW83RmdnSmlqZjA3eFNwVXpYVTRiVjhtSk55RlFJS1BiRzhHa3dRRFhiVzMvYXlZa2gyT3Z1Si9Bb2U5dAoxeGlFL3NMbTlGeTdlMTJRenZFcjlsanVpMzJWSjBtYkRpVm9Sd3FEMFh0R1JnZGhVeGltdU9PZzd0aXd1WUtkCkhBY0NDdUNReHRnWXpuQXdnVnJYVWpaSnRRV2RoNUpiVUwrZGp6ejYxSVdpTytiQSt2c0d3cjRoV09SVlk2K0sKcFNIMlhiL2JIaG5XOHBSREdsbEl2U2piZmlzY0d6SU1tUHN5end4ZG9sRXlqcnpCdGdLaW5pcjJvZmdJcEVHQwpuUzdZSDR3WE1kTkp4TEQ3U2JWdWpDbEwwQzhaUWFvamFkNUlkU2FZaldjSHFKelFmczRaKzQvRHN1OD0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
        rules:
          - operations: [ "CREATE" ]
            apiGroups: ["apps", ""]
            apiVersions: ["v1"]
            resources: ["deployments","services"]
        namespaceSelector:
          matchLabels:
            admission-webhook-example: enabled
    
    [root@master deployment]# kubectl apply -f mutatingwebhook-ca-bundle.yaml
    mutatingwebhookconfiguration.admissionregistration.k8s.io/mutating-webhook-example-cfg created
    
    

    测试

    同样的 sleep 任务

    [root@master deployment]# kubectl apply -f sleep.yaml
    deployment.apps/sleep created
    

    可以创建出来了,并且可以看到,增加了很多附加的 label,因为信息被添加,所以 validation 也通过了

    [root@master deployment]# kubectl describe deployment sleep
    Name:                   sleep
    Namespace:              default
    CreationTimestamp:      Fri, 25 Oct 2019 19:17:51 +0800
    Labels:                 app.kubernetes.io/component=not_available
                            app.kubernetes.io/instance=not_available
                            app.kubernetes.io/managed-by=not_available
                            app.kubernetes.io/name=not_available
                            app.kubernetes.io/part-of=not_available
                            app.kubernetes.io/version=not_available
    Annotations:            admission-webhook-example.qikqiak.com/status: mutated
                            deployment.kubernetes.io/revision: 1
    Selector:               app=sleep
    Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
    StrategyType:           RollingUpdate
    MinReadySeconds:        0
    RollingUpdateStrategy:  25% max unavailable, 25% max surge
    Pod Template:
      Labels:  app=sleep
      Containers:
       sleep:
        Image:      tutum/curl
        Port:       <none>
        Host Port:  <none>
        Command:
          /bin/sleep
          infinity
        Environment:  <none>
        Mounts:       <none>
      Volumes:        <none>
    Conditions:
      Type           Status  Reason
      ----           ------  ------
      Available      True    MinimumReplicasAvailable
      Progressing    True    NewReplicaSetAvailable
    OldReplicaSets:  <none>
    NewReplicaSet:   sleep-674f75ff4d (3/3 replicas created)
    Events:
      Type    Reason             Age   From                   Message
      ----    ------             ----  ----                   -------
      Normal  ScalingReplicaSet  57s   deployment-controller  Scaled up replica set sleep-674f75ff4d to 3
    

    遇到的问题

    1. webhook server 验证不起作用,发现是 bad certificate
    [root@master deployment]# kubectl log admission-webhook-example-deployment-5594df959f-6rjql
    log is DEPRECATED and will be removed in a future version. Use logs instead.
    I1025 09:32:21.348823       1 main.go:50] Server started
    2019/10/25 09:33:01 http: TLS handshake error from 10.244.0.0:10131: remote error: tls: bad certificate
    

    发现,参考文档中,下面步骤有问题

    cat ./deployment/validatingwebhook.yaml | ./deployment/webhook-patch-ca-bundle.sh > ./deployment/validatingwebhook-ca-bundle.yaml
    

    参考文档

  • 相关阅读:
    Vector3函数理解-计算两向量之间的角度
    Android报错Type Error executing aapt: Return code -1
    android中 onResume()方法什么时候执行 ??(转)
    自行实现Kinect 手势Demo踩的坑
    Kinect 2.0 默认姿势的中文意思
    C#限制float有两位小数
    Android View 从左边滑出动画 ,以及从左上,左下,右上,右下放大动画。
    注册谷歌账户时最后一步验证账户输入手机号说此电话号码无法用于进行验证,如何解决?
    Mac使用sonarqube进行代码检测
    Unable to find method 'org.gradle.api.tasks.TaskInputs.file
  • 原文地址:https://www.cnblogs.com/oolo/p/11778727.html
Copyright © 2011-2022 走看看