准入控制器会在验证和授权请求之后,对象被持久化之前,拦截kube-apiserver的请求,拦截后的请求进入准入控制器中处理,对请求的资源对象执行自定义(校验、修改或拒绝等)操作。

    准入控制器以插件的形式运行在kube-apiserver进程中,插件化的好处在于可扩展插件并单独启用/禁用指定插件,也可以将每个准入控制器称为准入控制器插件。kube-apiserver支持多种准入控制器机制,并支持同时开启多个准入控制器功能,如果开启了多个准入控制器,则按照顺序执行准入控制器。

    K8S默认提供很多内置的admission controller,通过kube-apiserver启动命令参数可以 查看到支持的admission controller plugin有哪些。

    1. # kube-apiserver --help |grep enable-admission-plugins
    2. # 支持的plugin有如下
    3. AlwaysAdmit, AlwaysDeny, AlwaysPullImages,
    4. DefaultStorageClass, DefaultTolerationSeconds, DenyEscalatingExec,
    5. DenyExecOnPrivileged, EventRateLimit, ExtendedResourceToleration,
    6. ImagePolicyWebhook, Initializers, LimitPodHardAntiAffinityTopology,
    7. LimitRanger, MutatingAdmissionWebhook, NamespaceAutoProvision,
    8. NamespaceExists, NamespaceLifecycle, NodeRestriction,
    9. OwnerReferencesPermissionEnforcement, PersistentVolumeClaimResize,
    10. PersistentVolumeLabel, PodNodeSelector, PodPreset, PodSecurityPolicy,
    11. PodTolerationRestriction, Priority, ResourceQuota, SecurityContextDeny,
    12. ServiceAccount, StorageObjectInUseProtection, ValidatingAdmissionWebhook.

    这里enable的admission-plugins如下

    --enable-admission-plugins=PersistentVolumeClaimResize,NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota,NodeRestriction,ValidatingAdmissionWebhook,MutatingAdmissionWebhook
    

    客户端发起一个请求,在请求经过准入控制器列表时,只要有一个准入控制器拒绝了该请求,则整个请求被拒绝(HTTP 403 Forbidden)并返回一个错误给客户端。准入控制器流程如图7-33所示。
    image.png

    kube-apiserver目前支持如下两种准入控制器。

    • 变更准入控制器(Mutating Admission Controller):用于变更信息,能够修改用户提交的资源对象信息。
    • 验证准入控制器(Validating Admission Controller):用于身份验证,能够验证用户提交的资源对象信息。提示:变更准入控制器运行在验证准入控制器之前。变更准入控制器和验证准入控制器接口定义分别是MutationInterface和ValidationInterface,代码示例如下:

    代码路径:vendor/k8s.io/apiserver/pkg/admission/interfaces.go

    // Interface is an abstract, pluggable interface for Admission Control decisions.
    type Interface interface {
        // Handles returns true if this admission controller can handle the given operation
        // where operation can be one of CREATE, UPDATE, DELETE, or CONNECT
        Handles(operation Operation) bool
    }
    
    type MutationInterface interface {
        Interface
    
        // Admit makes an admission decision based on the request attributes.
        // Context is used only for timeout/deadline/cancellation and tracing information.
        Admit(ctx context.Context, a Attributes, o ObjectInterfaces) (err error)
    }
    
    // ValidationInterface is an abstract, pluggable interface for Admission Control decisions.
    type ValidationInterface interface {
        Interface
    
        // Validate makes an admission decision based on the request attributes.  It is NOT allowed to mutate
        // Context is used only for timeout/deadline/cancellation and tracing information.
        Validate(ctx context.Context, a Attributes, o ObjectInterfaces) (err error)
    

    kube-apiserver中的所有已启用的准入控制器(Admit方法及Validate方法)由vendor/k8s.io/apiserver/pkg/admission/chain.go下的chainAdmissionHandler []Interface数据结构管理,chainAdmissionHandler数据结构如图所示。
    image.png

    在Admission Controller Handler中,会遍历已启用的准入控制器列表,按顺序尝试执行每个准入控制器,执行所有的变更操作。代码示例如下:
    代码路径:vendor/k8s.io/apiserver/pkg/admission/chain.go

    // Admit performs an admission control check using a chain of handlers, and returns immediately on first error
    func (admissionHandler chainAdmissionHandler) Admit(ctx context.Context, a Attributes, o ObjectInterfaces) error {
        for _, handler := range admissionHandler {
            if !handler.Handles(a.GetOperation()) {
                continue
            }
            if mutator, ok := handler.(MutationInterface); ok {
                err := mutator.Admit(ctx, a, o)
                if err != nil {
                    return err
                }
            }
        }
        return nil
    }
    

    同样的方式执行Validate函数,Validate函数会遍历已启用的准入控制器列表,并执行验证操作的准入控制器(即拥有Validate方法的准入控制器),代码示例如下:

    // Validate performs an admission control check using a chain of handlers, and returns immediately on first error
    func (admissionHandler chainAdmissionHandler) Validate(ctx context.Context, a Attributes, o ObjectInterfaces) error {
        for _, handler := range admissionHandler {
            if !handler.Handles(a.GetOperation()) {
                continue
            }
            if validator, ok := handler.(ValidationInterface); ok {
                err := validator.Validate(ctx, a, o)
                if err != nil {
                    return err
                }
            }
        }
        return nil
    }
    

    应用场景:

    • node资源超卖
    • 自动打标签 比如启动一个应用,应用包括deployment、service、ingress; 怎么快速过滤出哪些资源属于应用? 在K8S中,pod、service、ingress 都是独立的资源,通过给这些资源打上label,是最快速的方式。
    • 自动注入sidecar容器 应用启动后,应用的监控、日志如何处理?借助sidecar容器注入到其pod中