• 准入控制器(Admission Controller):ResourceQuota,ImagePolicyWebhook


    一.系统环境

    本文主要基于Kubernetes1.22.2和Linux操作系统Ubuntu 18.04。

    服务器版本 docker软件版本 Kubernetes(k8s)集群版本 CPU架构
    Ubuntu 18.04.5 LTS Docker version 20.10.14 v1.22.2 x86_64

    Kubernetes集群架构:k8scludes1作为master节点,k8scludes2,k8scludes3作为worker节点。

    服务器 操作系统版本 CPU架构 进程 功能描述
    k8scludes1/192.168.110.128 Ubuntu 18.04.5 LTS x86_64 docker,kube-apiserver,etcd,kube-scheduler,kube-controller-manager,kubelet,kube-proxy,coredns,calico k8s master节点
    k8scludes2/192.168.110.129 Ubuntu 18.04.5 LTS x86_64 docker,kubelet,kube-proxy,calico k8s worker节点
    k8scludes3/192.168.110.130 Ubuntu 18.04.5 LTS x86_64 docker,kubelet,kube-proxy,calico k8s worker节点

    二.前言

    在本文中,我们将探讨Kubernetes中的准入控制器(Admission Controller)。准入控制器是Kubernetes API server的一部分,负责处理来自外部的API请求。它们确保只有满足特定条件的请求才能访问集群资源。通过使用准入控制器,我们可以实现对集群资源的细粒度控制,从而提高集群的安全性和可靠性。

    使用准入控制器(Admission Controller)的前提是已经有一套可以正常运行的Kubernetes集群,关于Kubernetes(k8s)集群的安装部署,可以查看博客《Ubuntu 安装部署Kubernetes(k8s)集群》https://www.cnblogs.com/renshengdezheli/p/17632858.html。

    三.准入控制器简介

    当用户账户或者service account要执行一些操作的时候,首先要进行账号认证,认证通过之后,如果账号已经被授权相关资源,就可以进行相关操作了。有时授权之后也会有Admission control准入控制器(可以理解为一系列规则,可以启用或者关闭某个功能,准入控制器也支持一系列的webhook,在webhook里可以定义一系列规则),满足Admission control准入控制器的规则就可以创建相关资源了。OPA Gatekeeper也是利用的准入控制器功能,详情请查看博客《OPA Gatekeeper:Kubernetes的策略和管理》。

    image-20240614152316703

    准入控制器 是一段代码,它会在请求通过认证和鉴权之后、对象被持久化之前拦截到达 API 服务器的请求。

    准入控制器可以执行验证(Validating) 和/或变更(Mutating) 操作。 变更(mutating)控制器可以根据被其接受的请求更改相关对象;验证(validating)控制器则不行。

    准入控制器限制创建、删除、修改对象的请求。 准入控制器也可以阻止自定义动作,例如通过 API 服务器代理连接到 Pod 的请求。 准入控制器不会 (也不能)阻止读取(get、watch 或 list)对象的请求。

    Kubernetes 1.30 中的准入控制器编译进 kube-apiserver 可执行文件,并且只能由集群管理员配置。 在该列表中,有两个特殊的控制器:MutatingAdmissionWebhook 和 ValidatingAdmissionWebhook。 它们根据 API 中的配置, 分别执行变更和验证准入控制 Webhook。

    准入控制过程分为两个阶段第一阶段,运行变更准入控制器。第二阶段,运行验证准入控制器。 再次提醒,某些控制器既是变更准入控制器又是验证准入控制器。

    如果两个阶段之一的任何一个控制器拒绝了某请求,则整个请求将立即被拒绝,并向最终用户返回错误。

    最后,除了对对象进行变更外,准入控制器还可能有其它副作用:将相关资源作为请求处理的一部分进行变更。 增加配额用量就是一个典型的示例,说明了这样做的必要性。 此类用法都需要相应的回收或回调过程,因为任一准入控制器都无法确定某个请求能否通过所有其它准入控制器。

    四.为什么需要准入控制器

    Kubernetes 的若干重要功能都要求启用一个准入控制器,以便正确地支持该特性。 因此,没有正确配置准入控制器的 Kubernetes API 服务器是不完整的,它无法支持你所期望的所有特性。

    五.启用/禁用ResourceQuota资源配额

    5.1 查看默认启用/禁用的准入控制器插件

    本文中的kube-apiserver是以pod的方式运行的,名字为:kube-apiserver-k8scludes1。

    root@k8scludes1:~# kubectl get pod -n kube-system
    NAME                                       READY   STATUS    RESTARTS        AGE
    calico-kube-controllers-65898446b5-qd9q6   1/1     Running   21 (2d8h ago)   28d
    calico-node-d6564                          1/1     Running   58 (2d8h ago)   59d
    calico-node-jgvjb                          1/1     Running   67 (10h ago)    59d
    calico-node-snkxp                          1/1     Running   58 (2d8h ago)   59d
    coredns-7f6cbbb7b8-5gpjt                   1/1     Running   20 (2d8h ago)   28d
    coredns-7f6cbbb7b8-xm6pl                   1/1     Running   20 (2d8h ago)   28d
    etcd-k8scludes1                            1/1     Running   54 (2d8h ago)   58d
    kube-apiserver-k8scludes1                  1/1     Running   15 (10h ago)    19d
    kube-controller-manager-k8scludes1         1/1     Running   249 (10h ago)   59d
    kube-proxy-464tx                           1/1     Running   52 (2d8h ago)   59d
    kube-proxy-mkwpx                           1/1     Running   52 (2d8h ago)   59d
    kube-proxy-vsc9q                           1/1     Running   53 (2d8h ago)   59d
    kube-scheduler-k8scludes1                  1/1     Running   247 (10h ago)   59d
    

    查看kube-apiserver的帮助信息。

    root@k8scludes1:~# kubectl exec -it kube-apiserver-k8scludes1 -n kube-system -- kube-apiserver -h
    The Kubernetes API server validates and configures data
    for the api objects which include pods, services, replicationcontrollers, and
    others. The API Server services REST operations and provides the frontend to the
         ......
          --goaway-chance float                                                                                                                                                                              
                    To prevent HTTP/2 clients from getting stuck on a single apiserver, randomly close a connection (GOAWAY). The client's other in-flight requests won't be affected, and the client will
                    reconnect, likely landing on a different apiserver after going through the load balancer again. This argument sets the fraction of requests that will be sent a GOAWAY. Clusters with single
                    apiservers, or which don't use a load balancer, should NOT enable this. Min is 0 (off), Max is .02 (1/50 requests); .001 (1/1000) is a recommended starting point.
          --livez-grace-period duration                                                                                                                                                                      
                    This option represents the maximum amount of time it should take for apiserver to complete its startup sequence and become live. From apiserver's start time to when this amount of time has
                    elapsed, /livez will assume that unfinished post-start hooks will complete successfully and therefore return true.
         
          --vmodule moduleSpec                                                                                                                                                                               
                    comma-separated list of pattern=N settings for file-filtered logging
    

    查看默认启用/禁止的准入控制器插件有哪些?可以看到默认启用的准入控制器插件有:CertificateApproval, CertificateSigning, CertificateSubjectRestriction, DefaultIngressClass, DefaultStorageClass, DefaultTolerationSeconds, LimitRanger, MutatingAdmissionWebhook, NamespaceLifecycle, PersistentVolumeClaimResize, PodSecurity, Priority, ResourceQuota, RuntimeClass, ServiceAccount, StorageObjectInUseProtection, TaintNodesByCondition, ValidatingAdmissionPolicy, ValidatingAdmissionWebhook。

    root@k8scludes1:~# kubectl exec -it kube-apiserver-k8scludes1 -n kube-system -- kube-apiserver -h | grep admission-plugins
          --admission-control strings              Admission is divided into two phases. In the first phase, only mutating admission plugins run. In the second phase, only validating admission plugins run. The names in the below list may represent a validating plugin, a mutating plugin, or both. The order of plugins in which they are passed to this flag does not matter. Comma-delimited list of: AlwaysAdmit, AlwaysDeny, AlwaysPullImages, CertificateApproval, CertificateSigning, CertificateSubjectRestriction, DefaultIngressClass, DefaultStorageClass, DefaultTolerationSeconds, DenyServiceExternalIPs, EventRateLimit, ExtendedResourceToleration, ImagePolicyWebhook, LimitPodHardAntiAffinityTopology, LimitRanger, MutatingAdmissionWebhook, NamespaceAutoProvision, NamespaceExists, NamespaceLifecycle, NodeRestriction, OwnerReferencesPermissionEnforcement, PersistentVolumeClaimResize, PersistentVolumeLabel, PodNodeSelector, PodSecurity, PodSecurityPolicy, PodTolerationRestriction, Priority, ResourceQuota, RuntimeClass, SecurityContextDeny, ServiceAccount, StorageObjectInUseProtection, TaintNodesByCondition, ValidatingAdmissionWebhook. (DEPRECATED: Use --enable-admission-plugins or --disable-admission-plugins instead. Will be removed in a future version.)
          --disable-admission-plugins strings      admission plugins that should be disabled although they are in the default enabled plugins list (NamespaceLifecycle, LimitRanger, ServiceAccount, TaintNodesByCondition, PodSecurity, Priority, DefaultTolerationSeconds, DefaultStorageClass, StorageObjectInUseProtection, PersistentVolumeClaimResize, RuntimeClass, CertificateApproval, CertificateSigning, CertificateSubjectRestriction, DefaultIngressClass, MutatingAdmissionWebhook, ValidatingAdmissionWebhook, ResourceQuota). Comma-delimited list of admission plugins: AlwaysAdmit, AlwaysDeny, AlwaysPullImages, CertificateApproval, CertificateSigning, CertificateSubjectRestriction, DefaultIngressClass, DefaultStorageClass, DefaultTolerationSeconds, DenyServiceExternalIPs, EventRateLimit, ExtendedResourceToleration, ImagePolicyWebhook, LimitPodHardAntiAffinityTopology, LimitRanger, MutatingAdmissionWebhook, NamespaceAutoProvision, NamespaceExists, NamespaceLifecycle, NodeRestriction, OwnerReferencesPermissionEnforcement, PersistentVolumeClaimResize, PersistentVolumeLabel, PodNodeSelector, PodSecurity, PodSecurityPolicy, PodTolerationRestriction, Priority, ResourceQuota, RuntimeClass, SecurityContextDeny, ServiceAccount, StorageObjectInUseProtection, TaintNodesByCondition, ValidatingAdmissionWebhook. The order of plugins in this flag does not matter.
          --enable-admission-plugins strings       admission plugins that should be enabled in addition to default enabled ones (NamespaceLifecycle, LimitRanger, ServiceAccount, TaintNodesByCondition, PodSecurity, Priority, DefaultTolerationSeconds, DefaultStorageClass, StorageObjectInUseProtection, PersistentVolumeClaimResize, RuntimeClass, CertificateApproval, CertificateSigning, CertificateSubjectRestriction, DefaultIngressClass, MutatingAdmissionWebhook, ValidatingAdmissionWebhook, ResourceQuota). Comma-delimited list of admission plugins: AlwaysAdmit, AlwaysDeny, AlwaysPullImages, CertificateApproval, CertificateSigning, CertificateSubjectRestriction, DefaultIngressClass, DefaultStorageClass, DefaultTolerationSeconds, DenyServiceExternalIPs, EventRateLimit, ExtendedResourceToleration, ImagePolicyWebhook, LimitPodHardAntiAffinityTopology, LimitRanger, MutatingAdmissionWebhook, NamespaceAutoProvision, NamespaceExists, NamespaceLifecycle, NodeRestriction, OwnerReferencesPermissionEnforcement, PersistentVolumeClaimResize, PersistentVolumeLabel, PodNodeSelector, PodSecurity, PodSecurityPolicy, PodTolerationRestriction, Priority, ResourceQuota, RuntimeClass, SecurityContextDeny, ServiceAccount, StorageObjectInUseProtection, TaintNodesByCondition, ValidatingAdmissionWebhook. The order of plugins in this flag does not matter.
    

    5.2 ResourceQuota资源配额示例

    创建目录存放yaml文件。

    root@k8scludes1:~# mkdir admissioncontr
    
    root@k8scludes1:~# cd admissioncontr/
    

    创建命名空间。

    root@k8scludes1:~/admissioncontr# kubectl create ns admissioncontr
    namespace/admissioncontr created
    

    切换命名空间到admissioncontr。

    root@k8scludes1:~/admissioncontr# kubens admissioncontr
    Context "kubernetes-admin@kubernetes" modified.
    Active namespace is "admissioncontr".
    

    编辑pod配置文件,使用nginx镜像生成pod。

    root@k8scludes1:~/admissioncontr# vim pod.yaml 
    
    root@k8scludes1:~/admissioncontr# cat pod.yaml 
    apiVersion: v1
    kind: Pod
    metadata:
      creationTimestamp: null
      labels:
        run: podtest
      name: podtest
    spec:
      #当需要关闭容器时,立即杀死容器而不等待默认的30秒优雅停机时长。
      terminationGracePeriodSeconds: 0
      containers:
      - image: hub.c.163.com/library/nginx:latest
        #imagePullPolicy: IfNotPresent:表示如果本地已经存在该镜像,则不重新下载;否则从远程 Docker Hub 下载该镜像
        imagePullPolicy: IfNotPresent
        name: podtest
        resources: {}
      dnsPolicy: ClusterFirst
      restartPolicy: Always
    status: {}
    

    创建pod。

    root@k8scludes1:~/admissioncontr# kubectl apply -f pod.yaml 
    pod/podtest created
    

    使用相同的yaml文件,生成另外两个pod。

    root@k8scludes1:~/admissioncontr# sed 's/podtest/podtest1/' pod.yaml | kubectl apply -f -
    pod/podtest1 created
    
    root@k8scludes1:~/admissioncontr# sed 's/podtest/podtest2/' pod.yaml | kubectl apply -f -
    pod/podtest2 created
    

    现在没有任何限制,可以正常创建多个pod。

    root@k8scludes1:~/admissioncontr# kubectl get pod -o wide
    NAME       READY   STATUS    RESTARTS   AGE   IP               NODE         NOMINATED NODE   READINESS GATES
    podtest    1/1     Running   0          61s   10.244.218.179   k8scludes2   <none>           <none>
    podtest1   1/1     Running   0          15s   10.244.218.133   k8scludes2   <none>           <none>
    podtest2   1/1     Running   0          9s    10.244.1.84      k8scludes3   <none>           <none>
    

    删除pod。

    root@k8scludes1:~/admissioncontr# kubectl delete pod podtest podtest1 podtest2
    pod "podtest" deleted
    pod "podtest1" deleted
    pod "podtest2" deleted
    

    现在没有设置resourcequota资源配额。

    root@k8scludes1:~/admissioncontr# kubectl get quota
    No resources found in admissioncontr namespace.
    
    root@k8scludes1:~/admissioncontr# kubectl get resourcequota
    No resources found in admissioncontr namespace.
    

    编辑ResourceQuota配置文件,表示创建一个resourcequota资源配额,当前命名空间只能创建2个pod。

    resourcequota 资源配额用来设置一个命名空间最多能创建多少个资源对象,比如能创建多少svc,能创建多少pod或者deploy,详细信息请查看博客《Kubernetes(k8s) 资源限制:resources,LimitRange,ResourceQuota》。

    root@k8scludes1:~/admissioncontr# vim resourcequota.yaml
    
    root@k8scludes1:~/admissioncontr# cat resourcequota.yaml 
    apiVersion: v1 
    kind: ResourceQuota 
    metadata: 
      name: myresourcequota 
    spec: 
      #hard:指定资源的硬性限制,即最大允许使用的资源数量。
      hard: 
        pods: "2"
    

    创建ResourceQuota。

    root@k8scludes1:~/admissioncontr# kubectl apply -f resourcequota.yaml 
    resourcequota/myresourcequota created
    

    查看resourcequota资源配额,可以看到pod资源限额是2,现在有0个pod。

    root@k8scludes1:~/admissioncontr# kubectl get resourcequota
    NAME              AGE   REQUEST     LIMIT
    myresourcequota   11s   pods: 0/2   
    

    再次创建pod。

    root@k8scludes1:~/admissioncontr#  kubectl apply -f pod.yaml
    pod/podtest created
    
    root@k8scludes1:~/admissioncontr# sed 's/podtest/podtest1/' pod.yaml | kubectl apply -f -
    pod/podtest1 created
    

    创建第三个pod的时候报错了,pod资源配额是2,超过2个pod就创建失败了。

    root@k8scludes1:~/admissioncontr# sed 's/podtest/podtest2/' pod.yaml | kubectl apply -f -
    Error from server (Forbidden): error when creating "STDIN": pods "podtest2" is forbidden: exceeded quota: myresourcequota, requested: pods=1, used: pods=2, limited: pods=2
    

    查看resourcequota,可以看到pods: 2/2,资源配额满了。

    root@k8scludes1:~/admissioncontr# kubectl get resourcequota
    NAME              AGE     REQUEST     LIMIT
    myresourcequota   5m48s   pods: 2/2   
    

    删除pod。

    root@k8scludes1:~/admissioncontr# kubectl delete pod podtest podtest1
    pod "podtest" deleted
    pod "podtest1" deleted
    

    5.3 禁用ResourceQuota

    resourcequota 资源配额是一种准入控制器插件,默认是启用的。我们可以把resourcequota禁用了。

    修改kube-apiserver.yaml文件,禁用resourcequota 资源配额。

    disable-admission-plugins=ResourceQuota表示禁用准入控制器插件ResourceQuota。

    root@k8scludes1:~/admissioncontr# vim /etc/kubernetes/manifests/kube-apiserver.yaml
    
    root@k8scludes1:~/admissioncontr# grep disable-admission-plugins /etc/kubernetes/manifests/kube-apiserver.yaml
        - --disable-admission-plugins=ResourceQuota
    

    重启kubelet使配置生效。

    root@k8scludes1:~/admissioncontr# systemctl restart kubelet
    

    现在resourcequota 资源配额还在,还是要求当前命名空间只能创建2个pod。

    root@k8scludes1:~/admissioncontr# kubectl get resourcequota
    NAME              AGE   REQUEST     LIMIT
    myresourcequota   16m   pods: 0/2   
    

    一口气创建了5个pod还是没有报错。

    root@k8scludes1:~/admissioncontr# kubectl apply -f pod.yaml 
    pod/podtest created
    
    root@k8scludes1:~/admissioncontr# sed 's/podtest/podtest1/' pod.yaml | kubectl apply -f -
    pod/podtest1 created
    
    root@k8scludes1:~/admissioncontr# sed 's/podtest/podtest2/' pod.yaml | kubectl apply -f -
    pod/podtest2 created
    
    root@k8scludes1:~/admissioncontr# sed 's/podtest/podtest3/' pod.yaml | kubectl apply -f -
    pod/podtest3 created
    
    root@k8scludes1:~/admissioncontr# sed 's/podtest/podtest4/' pod.yaml | kubectl apply -f -
    pod/podtest4 created
    
    root@k8scludes1:~/admissioncontr# kubectl get pod 
    NAME       READY   STATUS    RESTARTS   AGE
    podtest    1/1     Running   0          30s
    podtest1   1/1     Running   0          21s
    podtest2   1/1     Running   0          16s
    podtest3   1/1     Running   0          11s
    podtest4   1/1     Running   0          6s
    

    可以发现,禁用resourcequota功能之后,就算有resourcequota对象存在,资源配额也不生效。

    root@k8scludes1:~/admissioncontr# kubectl get resourcequota
    NAME              AGE     REQUEST     LIMIT
    myresourcequota   3m59s   pods: 5/2   
    

    删除pod。

    root@k8scludes1:~# kubectl delete pod podtest podtest1 podtest2 podtest3 podtest4
    pod "podtest" deleted
    pod "podtest1" deleted
    pod "podtest2" deleted
    pod "podtest3" deleted
    pod "podtest4" deleted
    
    root@k8scludes1:~# kubectl get pod 
    No resources found in admissioncontr namespace.
    

    编辑kube-apiserver.yaml,去掉disable-admission-plugins=ResourceQuota,让ResourceQuota默认启用。

    root@k8scludes1:~# vim /etc/kubernetes/manifests/kube-apiserver.yaml 
    
    root@k8scludes1:~# grep disable-admission-plugins /etc/kubernetes/manifests/kube-apiserver.yaml 
        #- --disable-admission-plugins=ResourceQuota
    

    重启kubelet使配置生效。

    root@k8scludes1:~# systemctl restart kubelet
    

    现在resourcequota 资源配额还是要求当前命名空间只能创建2个pod。

    root@k8scludes1:~# kubectl get resourcequota
    NAME              AGE     REQUEST     LIMIT
    myresourcequota   5h31m   pods: 0/2   
    

    现在ResourceQuota资源配额又生效了,只能创建2个pod。

    root@k8scludes1:~/admissioncontr# kubectl apply -f pod.yaml
    pod/podtest created
    
    root@k8scludes1:~/admissioncontr# sed 's/podtest/podtest1/' pod.yaml | kubectl apply -f -
    pod/podtest1 created
    
    root@k8scludes1:~/admissioncontr# sed 's/podtest/podtest2/' pod.yaml | kubectl apply -f -
    Error from server (Forbidden): error when creating "STDIN": pods "podtest2" is forbidden: exceeded quota: myresourcequota, requested: pods=1, used: pods=2, limited: pods=2
    

    删除pod。

    root@k8scludes1:~/admissioncontr# kubectl delete pod podtest podtest1 
    pod "podtest" deleted
    pod "podtest1" deleted
    

    删除ResourceQuota资源配额。

    root@k8scludes1:~/admissioncontr# kubectl delete resourcequota myresourcequota 
    resourcequota "myresourcequota" deleted
    
    root@k8scludes1:~/admissioncontr# kubectl get resourcequotas 
    No resources found in admissioncontr namespace.
    

    六.配置ImagePolicyWebhook准入控制器禁止使用后缀为latest的镜像

    6.1 搭建Webhook服务器

    ImagePolicyWebhook的类别为验证。ImagePolicyWebhook 准入控制器允许使用后端 Webhook 做出准入决策。此准入控制器默认被禁用。

    准入 Webhook 是一种用于接收准入请求并对其进行处理的 HTTP 回调机制。 可以定义两种类型的准入 webhook,即验证性质的准入 Webhook 和 修改性质的准入 Webhook。 修改性质的准入 Webhook 会先被调用。它们可以更改发送到 API 服务器的对象,以执行自定义的设置默认值操作。在完成了所有对象修改并且 API 服务器也验证了所传入的对象之后, 验证性质的 Webhook 会被调用,并通过拒绝请求的方式来强制实施自定义的策略。

    说明: 如果准入 Webhook 需要保证它们所看到的是对象的最终状态以实施某种策略。 则应使用验证性质的准入 Webhook,因为对象被修改性质 Webhook 看到之后仍然可能被修改。

    首先需要找一台机器搭建Webhook服务器,我们使用etcd2机器当Webhook服务器。

    首先需要安装docker。

    [root@etcd2 ~]# yum -y install docker-ce
    

    查看docker版本。

    [root@etcd2 ~]# docker -v
    Docker version 20.10.12, build e91ed57
    

    配置docker镜像加速器。

    [root@etcd2 ~]# cat /etc/docker/daemon.json
    {
        "registry-mirrors": [
            "https://frz7i079.mirror.aliyuncs.com"
        ]
    }
    

    拉取flavio/kube-image-bouncer镜像,flavio/kube-image-bouncer这个镜像的功能为:不允许使用后缀为latest的镜像。

    [root@etcd2 ~]# docker pull flavio/kube-image-bouncer
    Using default tag: latest
    latest: Pulling from flavio/kube-image-bouncer
    ff3a5c916c92: Pull complete 
    4947cf5a98cf: Pull complete 
    37ff781c0e4d: Pull complete 
    1545c1d26daf: Pull complete 
    dd581c9cf10b: Pull complete 
    Digest: sha256:01e1e0873d20cee1ffefb45421c798cb26250b7a9384978d34ffb1f57403d501
    Status: Downloaded newer image for flavio/kube-image-bouncer:latest
    docker.io/flavio/kube-image-bouncer:latest
    

    查看flavio/kube-image-bouncer镜像历史信息,可以发现开放端口为1323(EXPOSE 1323),使用的是web账号登录(USER web)。关于镜像Dockerfile的详细信息,请查看博客《构建自定义镜像并优化dockerfile文件》。

    [root@etcd2 ~]# docker history flavio/kube-image-bouncer
    IMAGE          CREATED       CREATED BY                                      SIZE      COMMENT
    3974e07cc0c8   3 years ago   /bin/sh -c #(nop)  EXPOSE 1323                  0B        
          3 years ago   /bin/sh -c #(nop)  ENTRYPOINT ["./kube-image…   0B        
          3 years ago   /bin/sh -c #(nop)  USER web                     0B        
          3 years ago   /bin/sh -c chown -R web:web *                   19.9MB    
          3 years ago   /bin/sh -c #(nop) COPY file:de99d16a3731b75b…   19.9MB    
          3 years ago   /bin/sh -c adduser -h /app -D web               4.79kB    
          3 years ago   /bin/sh -c #(nop) WORKDIR /app                  0B        
          4 years ago   /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B        
          4 years ago   /bin/sh -c #(nop) ADD file:093f0723fa46f6cdb…   4.15MB    
    

    使用flavio/kube-image-bouncer镜像创建容器。

    -vpwd/webhook-key.pem:/certs/webhook-key.pem:ro -v pwd/webhook.pem:/certs/webhook.pem:ro 表示做目录映射(-v 本地目录:容器目录),webhook-key.pem公钥和webhook.pem私钥不存在也没问题,ro表示容器只有只读权限。

    -p 1323:1323表示做端口映射(物理机端口1323:容器端口1323)。关于容器更多详细信息,请查看博客《一文搞懂docker容器基础:docker镜像管理,docker容器管理》。

    [root@etcd2 ~]# docker run -dit --name=c1 --restart=always -v `pwd`/webhook-key.pem:/certs/webhook-key.pem:ro -v `pwd`/webhook.pem:/certs/webhook.pem:ro -p 1323:1323 flavio/kube-image-bouncer
    7c677fb67522073932617f07ad72cd1a17e60eddc6a68dd25ffe0b8c4f80aaae
    

    容器创建成功了,这时候访问192.168.110.131:1323 就访问到该webhook服务器了。

    [root@etcd2 ~]# docker ps
    CONTAINER ID   IMAGE                         COMMAND                  CREATED          STATUS         PORTS                                       NAMES
    7c677fb67522   flavio/kube-image-bouncer     "./kube-image-bouncer"   10 seconds ago   Up 8 seconds   0.0.0.0:1323->1323/tcp, :::1323->1323/tcp   c1
    

    6.2 配置kubernetes连接后端webhook服务器

    回到kubernetes集群。

    root@k8scludes1:~/admissioncontr# cd /etc/kubernetes/
    
    root@k8scludes1:/etc/kubernetes# ls
    admin.conf  admission-control-config-file  controller-manager.conf  kubelet.conf  manifests  pki  scheduler.conf
    
    root@k8scludes1:/etc/kubernetes# cd admission-control-config-file/
    
    root@k8scludes1:/etc/kubernetes/admission-control-config-file# ls
    admission_configuration.json  apiserver-client-key.pem  apiserver-client.pem  kubeconfig.yaml  webhook-key.pem  webhook.pem
    
    root@k8scludes1:/etc/kubernetes/admission-control-config-file# pwd
    /etc/kubernetes/admission-control-config-file
    

    ImagePolicyWebhook 使用配置文件来为后端行为设置选项。该文件可以是 JSON 或 YAML。

    admission_configuration.json是ImagePolicyWebhook 的配置文件,参数解释如下:

    • allowTTL以秒计的时长,控制批准请求的缓存时间;
    • denyTTL以秒计的时长,控制拒绝请求的缓存时间;
    • retryBackoff以毫秒计的时长,控制重试间隔;
    • defaultAllow确定 Webhook 后端失效时的行为;
    • kubeConfigFile指定kubeconfig文件的路径。
    root@k8scludes1:/etc/kubernetes/admission-control-config-file# vim admission_configuration.json 
    
    root@k8scludes1:/etc/kubernetes/admission-control-config-file# cat admission_configuration.json 
    {
      "imagePolicy": {
         "kubeConfigFile": "/etc/kubernetes/admission-control-config-file/kubeconfig.yaml",
         "allowTTL": 50,
         "denyTTL": 50,
         "retryBackoff": 500,
         "defaultAllow": true
      }
    }
    
    root@k8scludes1:/etc/kubernetes/admission-control-config-file# ls /etc/kubernetes/admission-control-config-file/admission_configuration.json 
    /etc/kubernetes/admission-control-config-file/admission_configuration.json
    

    kubeconfig.yaml用来设置与后端Webhook服务器的连接,要求后端使用 TLS 进行通信。

    kubeconfig.yaml文件的 clusters 字段需要指向远端Webhook服务,server指定webhook服务器进行连接。

    users 字段需要包含已返回的授权者。关于kubeconfig文件的详细信息,请查看博客《Kubernetes(k8s)访问控制:身份认证》。

    root@k8scludes1:/etc/kubernetes/admission-control-config-file# vim kubeconfig.yaml 
    
    #指定各种证书
    root@k8scludes1:/etc/kubernetes/admission-control-config-file# cat kubeconfig.yaml 
    apiVersion: v1
    kind: Config
    clusters:
    - cluster:
        # CA 用于验证远程服务
        certificate-authority: /etc/kubernetes/admission-control-config-file/webhook.pem
        # 要查询的远程服务的 URL
        server: http://192.168.110.131:1323/image_policy
      name: bouncer_webhook
    contexts:
    - context:
        cluster: bouncer_webhook
        user: api-server
      name: bouncer_validator
    current-context: bouncer_validator
    preferences: {}
    users:
    - name: api-server
      user:
        # Webhook 准入控制器使用的证书
        client-certificate: /etc/kubernetes/admission-control-config-file/apiserver-client.pem
        # 证书匹配的密钥
        client-key:  /etc/kubernetes/admission-control-config-file/apiserver-client-key.pem
    

    ImagePolicyWebhook默认是没有开启的,现在启用ImagePolicyWebhook。

    enable-admission-plugins=NodeRestriction,ImagePolicyWebhook表示启用ImagePolicyWebhook准入控制器。

    通过 --admission-control-config-file 参数指定准入控制器ImagePolicyWebhook配置文件的位置。

    root@k8scludes1:/etc/kubernetes/admission-control-config-file# vim /etc/kubernetes/manifests/kube-apiserver.yaml 
    
    root@k8scludes1:/etc/kubernetes/admission-control-config-file# grep admission /etc/kubernetes/manifests/kube-apiserver.yaml
        - --enable-admission-plugins=NodeRestriction,ImagePolicyWebhook
        - --admission-control-config-file=/etc/kubernetes/admission-control-config-file/admission_configuration.json
    

    重启kubelet使配置生效。

    root@k8scludes1:/etc/kubernetes/admission-control-config-file# systemctl restart kubelet
    

    可以发现kube-apiserver.yaml添加参数之后,连接不上集群了。

    root@k8scludes1:/etc/kubernetes/admission-control-config-file# kubectl get node
    The connection to the server 192.168.110.128:6443 was refused - did you specify the right host or port?
    

    下面开始排查问题,kubectl使用不了,那就只能使用docker排查问题了,使用docker查看容器,可以发现k8s_kube-apiserver容器处于退出状态。

    root@k8scludes1:/etc/kubernetes/admission-control-config-file# cd
    
    root@k8scludes1:~# docker ps -a | grep api
    2aacfa2bdbc4   e64579b7d886                                        "kube-apiserver --ad…"   19 seconds ago      Exited (1) 18 seconds ago             k8s_kube-apiserver_kube-apiserver-k8scludes1_kube-system_054ae2c0df7fd5edd4ebb3b7057a0462_8
    cb0e2e181dda   registry.aliyuncs.com/google_containers/pause:3.5   "/pause"                 4 minutes ago       Up 4 minutes                          k8s_POD_kube-apiserver-k8scludes1_kube-system_054ae2c0df7fd5edd4ebb3b7057a0462_0
    ac234a8ee92b   e64579b7d886                                        "kube-apiserver --ad…"   4 minutes ago       Exited (1) 4 minutes ago              k8s_kube-apiserver_kube-apiserver-k8scludes1_kube-system_f38197908cec7cb32a42e0862e367c1c_0
    49e9b9c1b389   registry.aliyuncs.com/google_containers/pause:3.5   "/pause"                 4 minutes ago       Up 4 minutes                          k8s_POD_kube-apiserver-k8scludes1_kube-system_f38197908cec7cb32a42e0862e367c1c_0
    5d4985a8833c   e64579b7d886                                        "kube-apiserver --ad…"   5 minutes ago       Exited (1) 5 minutes ago              k8s_kube-apiserver_kube-apiserver-k8scludes1_kube-system_3dc64835bcbf5bdf937660d07f41b90b_87
    c674a6a6c794   registry.aliyuncs.com/google_containers/pause:3.5   "/pause"                 About an hour ago   Up About an hour                      k8s_POD_kube-apiserver-k8scludes1_kube-system_3dc64835bcbf5bdf937660d07f41b90b_2
    

    查看k8s_kube-apiserver容器日志,报错为:“open /etc/kubernetes/admission-control-config-file/admission_configuration.json: no such file or directory“,发现admission_configuration.json文件找不到。

    root@k8scludes1:~# docker logs 2aacfa2bdbc4
    I0616 01:39:59.927352       1 server.go:553] external host was not specified, using 192.168.110.128
    I0616 01:39:59.927965       1 server.go:161] Version: v1.22.2
    Error: failed to initialize admission: failed to read plugin config: unable to read admission control configuration from "/etc/kubernetes/admission-control-config-file/admission_configuration.json" [open /etc/kubernetes/admission-control-config-file/admission_configuration.json: no such file or directory]
    

    /etc/kubernetes/admission-control-config-file/admission_configuration.json文件是存在的,但是报错。

    原因为:/etc/kubernetes/admission-control-config-file/admission_configuration.json这个文件是在宿主机里的,并不在容器里。

    解决方法:做一个数据卷,把宿主机里的文件映射到容器里。

    root@k8scludes1:~# ls /etc/kubernetes/admission-control-config-file/admission_configuration.json
    /etc/kubernetes/admission-control-config-file/admission_configuration.json
    

    注意:生产环境里可以先备份下kube-apiserver.yaml 文件再修改,以免改不回来。

    定义一个数据卷把宿主机的/etc/kubernetes/admission-control-config-file目录挂载到容器/etc/kubernetes/admission-control-config-file目录。

    root@k8scludes1:~# vim /etc/kubernetes/manifests/kube-apiserver.yaml 
    
    root@k8scludes1:~# grep -A3 admission-control-config-file /etc/kubernetes/manifests/kube-apiserver.yaml
        - --admission-control-config-file=/etc/kubernetes/admission-control-config-file/admission_configuration.json
        #- --disable-admission-plugins=ResourceQuota
        #- --enable-admission-plugins=NodeRestriction,PodSecurityPolicy
        - --token-auth-file=/etc/kubernetes/pki/mytok.csv
    --
        - mountPath: /etc/kubernetes/admission-control-config-file
          name: admissionconfile
          readOnly: true
      hostNetwork: true
    --
          path: /etc/kubernetes/admission-control-config-file
          type: DirectoryOrCreate
        name: admissionconfile
      - hostPath:
    

    直接上截图可能更直观:

    image-20230809151421885

    重启kubelet使配置生效。

    root@k8scludes1:~# systemctl restart kubelet 
    

    现在k8s正常了。

    root@k8scludes1:~# kubectl get pod 
    No resources found in admissioncontr namespace.
    

    6.3 验证

    编辑pod配置文件,表示使用hub.c.163.com/library/nginx:latest镜像创建pod。

    root@k8scludes1:~# cd admissioncontr/
    
    root@k8scludes1:~/admissioncontr# vim pod.yaml 
    
    root@k8scludes1:~/admissioncontr# cat pod.yaml 
    apiVersion: v1
    kind: Pod
    metadata:
      creationTimestamp: null
      labels:
        run: podtest
      name: podtest
    spec:
      #当需要关闭容器时,立即杀死容器而不等待默认的30秒优雅停机时长。
      terminationGracePeriodSeconds: 0
      containers:
      - image: hub.c.163.com/library/nginx:latest
        #imagePullPolicy: IfNotPresent:表示如果本地已经存在该镜像,则不重新下载;否则从远程 Docker Hub 下载该镜像
        imagePullPolicy: IfNotPresent
        name: podtest
        resources: {}
      dnsPolicy: ClusterFirst
      restartPolicy: Always
    status: {}
    

    创建pod,可以发现:使用tag为latest的镜像不能创建pod。

    root@k8scludes1:~/admissioncontr# kubectl apply -f pod.yaml 
    Error from server (Forbidden): error when creating "pod.yaml": pods "podtest" is forbidden: image policy webhook backend denied one or more images: Images using latest tag are not allowed
    

    修改pod配置文件,表示使用hub.c.163.com/library/nginx:test镜像创建pod。

    root@k8scludes1:~/admissioncontr# vim pod.yaml 
    
    root@k8scludes1:~/admissioncontr# cat pod.yaml 
    apiVersion: v1
    kind: Pod
    metadata:
      creationTimestamp: null
      labels:
        run: podtest
      name: podtest
    spec:
      #当需要关闭容器时,立即杀死容器而不等待默认的30秒优雅停机时长。
      terminationGracePeriodSeconds: 0
      containers:
      - image: hub.c.163.com/library/nginx:test
      #- image: hub.c.163.com/library/nginx:latest
        #imagePullPolicy: IfNotPresent:表示如果本地已经存在该镜像,则不重新下载;否则从远程 Docker Hub 下载该镜像
        imagePullPolicy: IfNotPresent
        name: podtest
        resources: {}
      dnsPolicy: ClusterFirst
      restartPolicy: Always
    status: {}
    

    镜像tag为test,pod创建成功。

    root@k8scludes1:~/admissioncontr# kubectl apply -f pod.yaml 
    pod/podtest created
    
    root@k8scludes1:~/admissioncontr# kubectl get pod -o wide
    NAME      READY   STATUS    RESTARTS   AGE   IP               NODE         NOMINATED NODE   READINESS GATES
    podtest   1/1     Running   0          6s    10.244.218.145   k8scludes2              
    

    删除pod。

    root@k8scludes1:~/admissioncontr# kubectl delete pod podtest 
    pod "podtest" deleted
    

    七.总结

    准入控制器是Kubernetes中一个非常重要的组件,它负责拦截和处理来自用户或其他应用程序的API请求。通过使用准入控制器,我们可以实现对集群资源的细粒度控制,从而提高集群的安全性和可靠性。

  • 相关阅读:
    【生动理解】深度学习中常用的各项评价指标含义TP、FP、TN、FN、Recall、IoU、Accuracy
    GPU--学习笔记
    SpringBoot学习之Redis下载安装启动【Windows版本】(三十六)
    酷睿i5 12450h和i5 12650h差多少 i512450h和i512650h对比
    C#学生成绩查询(使用方法实现,查最大值,最小值,平均值,升序,降序)
    LangChain结合milvus向量数据库以及GPT3.5结合做知识库问答之二 --->代码实现
    2019-04《信息资源管理 02378》真卷(独家文字版),圈定章节考点+统计真题分布
    14个SpringBoot优化小妙招,看完后同事说写代码像写诗!
    《深入理解RPC框架原理与实现 华钟明》使用Netty、Zookeeper等实现一个简单的RPC框架、自定义注解、SPI机制实践与原理分析
    29. 一道简单背包题
  • 原文地址:https://www.cnblogs.com/renshengdezheli/p/18260051