Kubernetes 中的 admission controllers 作用与实践

Kubernetes Admission Controllers:Kubernetes 世界的“安检员”与“魔法师”

各位观众,各位朋友,欢迎来到今天的 Kubernetes 深度剖析讲座!今天我们要聊聊 Kubernetes 世界里一群神秘而重要的角色:Admission Controllers。

想象一下,Kubernetes 集群是一个热闹非凡的城市,各种应用服务就像熙熙攘攘的人群,争先恐后地想进入这个城市。没有秩序,这个城市就会变成一团乱麻,甚至崩溃。而 Admission Controllers,就好像是这座城市的“安检员”和“魔法师”,他们负责对所有进入集群的请求进行检查、验证,甚至修改,确保这个城市安全、稳定、高效地运转。

简单来说,Admission Controllers 就是 Kubernetes 的准入控制机制,它拦截对 Kubernetes API Server 的请求,并在对象持久化之前对请求进行验证(validation)或修改(mutation)。

为什么我们需要 Admission Controllers?

你可能会问,Kubernetes 本身不是已经有认证和授权了吗?为什么还需要 Admission Controllers 这么一道“额外”的工序?

嗯,这个问题问得好!认证和授权就像是检查你的身份证和门票,验证你是否有资格进入这个城市。而 Admission Controllers 则更进一步,他们会检查你携带的物品是否安全、是否符合城市的规定,甚至可以根据规定修改你的服装,让你更适合这个城市的环境。

具体来说,我们需要 Admission Controllers 来实现以下目标:

  • 安全加固: 阻止恶意镜像的部署,限制特权容器的使用,防止资源滥用等。
  • 策略执行: 强制执行组织内的命名规范、资源限制、安全策略等。
  • 资源优化: 自动注入 sidecar 容器,设置默认资源请求,优化资源利用率。
  • 自定义逻辑: 根据业务需求,实现自定义的验证和修改逻辑,满足特殊场景的需求。

总而言之,Admission Controllers 提供了一种灵活、强大的方式来扩展 Kubernetes 的功能,并使其更符合我们的实际需求。

Admission Controllers 的工作原理:一场精妙的拦截与变形

现在,让我们深入了解一下 Admission Controllers 的工作原理。你可以把 Admission Controllers 想象成一个管道,所有对 Kubernetes API Server 的请求都要经过这个管道。

这个管道分为两个阶段:

  1. Mutating Admission Webhooks (变形阶段): 这个阶段就像是“魔法师”在施展魔法,它可以修改请求的对象。例如,自动注入 sidecar 容器,设置默认的资源请求等。这个阶段可以有多个 Mutating Admission Webhooks,它们按照配置的顺序依次执行。
  2. Validating Admission Webhooks (验证阶段): 这个阶段就像是“安检员”在进行严格的检查,它会验证请求的对象是否符合预定义的规则。例如,检查镜像是否来自可信的仓库,检查资源限制是否超标等。这个阶段也可以有多个 Validating Admission Webhooks,它们按照配置的顺序依次执行。

整个流程可以用下图来概括:

graph LR
    A[API Server] --> B{Authentication/Authorization};
    B --> C{Mutating Admission Webhooks};
    C --> D{Validating Admission Webhooks};
    D --> E{Object Persistence};
    E --> F[Response to Client];
    C -- Mutated Object --> D;
    D -- Validation Success --> E;
    D -- Validation Failed --> G[Rejection];
    G --> F;
  • API Server: 接收客户端的请求。
  • Authentication/Authorization: 认证和授权用户身份,判断其是否有权限进行操作。
  • Mutating Admission Webhooks: 修改请求的对象。
  • Validating Admission Webhooks: 验证请求的对象。
  • Object Persistence: 将对象持久化到 etcd 中。
  • Response to Client: 向客户端返回响应。
  • Rejection: 拒绝请求。

如果任何一个 Admission Webhook 拒绝了请求,整个流程就会中断,API Server 会向客户端返回错误信息。

Admission Controllers 的类型:内置 vs. 自定义

Kubernetes 提供了两种类型的 Admission Controllers:

  • 内置 Admission Controllers: 这些是 Kubernetes 自带的,默认情况下会启用一些常用的 Admission Controllers,例如 NamespaceLifecycleLimitRangerServiceAccount 等。 你可以通过查看 API Server 的启动参数来确认哪些是默认开启的。
  • Dynamic Admission Webhooks: 这是我们可以自定义的,通过 HTTP 回调到我们自己的服务,实现更灵活的验证和修改逻辑。

内置的 Admission Controllers 提供了基础的功能,但往往无法满足我们所有的需求。这时,我们就需要使用 Dynamic Admission Webhooks 来扩展 Kubernetes 的功能。

实践:手把手教你打造一个自定义 Admission Webhook

接下来,让我们通过一个简单的例子,来演示如何创建一个自定义的 Admission Webhook。

假设我们有一个需求:我们需要强制所有 Pod 都必须包含一个特定的 Label,例如 owner=team-a

我们可以通过以下步骤来实现:

  1. 编写 Webhook 服务: 我们需要编写一个 HTTP 服务,接收 AdmissionReview 请求,并返回 AdmissionReview 响应。这个服务需要实现以下逻辑:

    • 解析 AdmissionReview 请求,获取 Pod 对象。
    • 检查 Pod 对象是否包含 owner=team-a Label。
    • 如果包含,则返回允许的 AdmissionReview 响应。
    • 如果不包含,则返回拒绝的 AdmissionReview 响应,并给出错误信息。

    我们可以使用任何编程语言来实现这个 Webhook 服务,例如 Go、Python、Java 等。这里我们以 Go 语言为例,提供一个简单的示例代码:

    package main
    
    import (
        "encoding/json"
        "fmt"
        "io/ioutil"
        "log"
        "net/http"
    
        admissionv1 "k8s.io/api/admission/v1"
        corev1 "k8s.io/api/core/v1"
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    )
    
    func main() {
        http.HandleFunc("/validate", validateHandler)
        log.Fatal(http.ListenAndServeTLS(":443", "/etc/webhook/certs/tls.crt", "/etc/webhook/certs/tls.key", nil))
    }
    
    func validateHandler(w http.ResponseWriter, r *http.Request) {
        body, err := ioutil.ReadAll(r.Body)
        if err != nil {
            log.Printf("Error reading request body: %v", err)
            http.Error(w, "Error reading request body", http.StatusBadRequest)
            return
        }
    
        var admissionReview admissionv1.AdmissionReview
        if err := json.Unmarshal(body, &admissionReview); err != nil {
            log.Printf("Error unmarshaling admission review: %v", err)
            http.Error(w, "Error unmarshaling admission review", http.StatusBadRequest)
            return
        }
    
        pod := corev1.Pod{}
        if err := json.Unmarshal(admissionReview.Request.Object.Raw, &pod); err != nil {
            log.Printf("Error unmarshaling pod: %v", err)
            http.Error(w, "Error unmarshaling pod", http.StatusBadRequest)
            return
        }
    
        allowed := true
        message := ""
        if _, ok := pod.ObjectMeta.Labels["owner"]; !ok {
            allowed = false
            message = "Pod must have the 'owner' label."
        }
    
        response := admissionv1.AdmissionReview{
            Response: &admissionv1.AdmissionResponse{
                UID:     admissionReview.Request.UID,
                Allowed: allowed,
                Result: &metav1.Status{
                    Message: message,
                },
            },
        }
    
        respBytes, err := json.Marshal(response)
        if err != nil {
            log.Printf("Error marshaling admission review response: %v", err)
            http.Error(w, "Error marshaling admission review response", http.StatusInternalServerError)
            return
        }
    
        w.Header().Set("Content-Type", "application/json")
        fmt.Fprint(w, string(respBytes))
    }
  2. 部署 Webhook 服务: 我们需要将 Webhook 服务部署到 Kubernetes 集群中。可以使用 Deployment 和 Service 来管理 Webhook 服务。需要注意的是,Webhook 服务必须使用 HTTPS 协议,并且需要配置 TLS 证书。

    这里提供一个简单的 Deployment 和 Service 的 YAML 文件:

    # deployment.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: admission-webhook
      labels:
        app: admission-webhook
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: admission-webhook
      template:
        metadata:
          labels:
            app: admission-webhook
        spec:
          containers:
          - name: admission-webhook
            image: your-image:latest # 替换成你的镜像地址
            ports:
            - containerPort: 443
            volumeMounts:
            - name: webhook-certs
              mountPath: /etc/webhook/certs
              readOnly: true
          volumes:
          - name: webhook-certs
            secret:
              secretName: webhook-certs # 替换成你的 Secret 名称
    ---
    # service.yaml
    apiVersion: v1
    kind: Service
    metadata:
      name: admission-webhook
      labels:
        app: admission-webhook
    spec:
      ports:
      - port: 443
        protocol: TCP
        targetPort: 443
      selector:
        app: admission-webhook
  3. 创建 TLS 证书: 我们需要为 Webhook 服务创建 TLS 证书,并将其存储在 Kubernetes Secret 中。可以使用 openssl 等工具来生成证书。

    openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes -subj "/CN=admission-webhook.default.svc" # 替换成你的 Service 名称
    kubectl create secret tls webhook-certs --key key.pem --cert cert.pem
  4. 注册 Admission Webhook: 我们需要创建一个 MutatingWebhookConfiguration 或 ValidatingWebhookConfiguration 对象,将 Webhook 服务注册到 Kubernetes 集群中。

    这里提供一个 ValidatingWebhookConfiguration 的 YAML 文件:

    apiVersion: admissionregistration.k8s.io/v1
    kind: ValidatingWebhookConfiguration
    metadata:
      name: pod-owner-label-validator
    webhooks:
      - name: pod-owner-label.example.com
        clientConfig:
          service:
            name: admission-webhook # 替换成你的 Service 名称
            namespace: default # 替换成你的 Service 所在的 Namespace
            path: /validate
          caBundle: $(base64 -w 0 cert.pem) # 替换成你的 CA 证书
        rules:
          - apiGroups:   [""]
            apiVersions: ["v1"]
            operations:  ["CREATE", "UPDATE"]
            resources:   ["pods"]
        sideEffects: None
        admissionReviewVersions: ["v1", "v1beta1"]

    需要注意的是,clientConfig 中的 caBundle 字段需要填写 CA 证书的内容。你可以使用 base64 命令将证书文件转换成 Base64 编码的字符串。

  5. 测试 Webhook: 我们可以创建一个不包含 owner Label 的 Pod,并尝试将其部署到 Kubernetes 集群中。如果一切配置正确,API Server 应该会拒绝这个 Pod 的创建请求,并返回错误信息。

    apiVersion: v1
    kind: Pod
    metadata:
      name: test-pod
    spec:
      containers:
      - name: test-container
        image: nginx

    执行 kubectl apply -f pod.yaml 后,你会看到类似以下的错误信息:

    Error from server: admission webhook "pod-owner-label.example.com" denied the request: Pod must have the 'owner' label.

Admission Controllers 的注意事项

在使用 Admission Controllers 时,需要注意以下几点:

  • 性能影响: Admission Controllers 会增加 API Server 的处理时间,因此需要谨慎使用,避免过度复杂的逻辑。
  • 故障处理: 如果 Admission Webhook 服务出现故障,可能会导致 API Server 无法正常工作。因此,需要确保 Webhook 服务的稳定性和可用性。
  • 顺序依赖: Mutating Admission Webhooks 的执行顺序非常重要,需要仔细考虑,避免出现冲突或错误。
  • 权限管理: 需要对 Admission Webhook 服务进行适当的权限管理,防止被恶意利用。

总结:Admission Controllers,Kubernetes 的守护者与改造者

Admission Controllers 是 Kubernetes 中一个非常强大和灵活的工具,它可以帮助我们实现各种各样的安全策略、资源优化、自定义逻辑等。它就像 Kubernetes 世界的“安检员”和“魔法师”,守护着集群的安全和稳定,并根据我们的需求改造着集群的功能。

虽然 Admission Controllers 的配置和使用可能比较复杂,但只要我们掌握了其基本原理和使用方法,就可以充分利用它来提升 Kubernetes 集群的价值。

希望今天的讲座对大家有所帮助!谢谢大家! 👏

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注