K8s 的 Admission Webhooks 开发与策略管理:运行时策略执行

好的,各位观众,各位朋友,欢迎来到今天的“K8s魔法学院”特别讲座!🧙‍♂️ 今天我们要聊点刺激的,聊聊如何在K8s王国里,像一位手握生杀大权的国王一样,制定并执行你的运行时策略!

K8s Admission Webhooks:你的运行时策略卫士

想象一下,你的 K8s 集群是一个熙熙攘攘的城市,每天都有无数的居民(Pod),车辆(Service),房屋(Deployment)想要涌入。没有规矩,不成方圆,对吧?如果每个人都想干嘛就干嘛,那还不乱套了?

这个时候,就需要我们的“城管大队”——Admission Webhooks 出马了! 它们就像一群尽职尽责的守门人,在任何新的资源(Resource)被创建、更新或删除之前,都会拦截下来,进行一番细致的检查,看看是否符合咱们事先定好的规矩。

什么是 Admission Webhooks?

简单来说,Admission Webhooks 是 Kubernetes 提供的一种扩展机制,允许你在集群中实施自定义的策略。 它们就像拦截器,拦截所有对 Kubernetes API Server 的请求,并根据你的策略进行验证或修改。

两种“城管”:Mutating 和 Validating

我们的“城管大队”还分为两支队伍,各有分工:

  • Mutating Admission Webhook(变异准入 Webhook): 这支队伍可厉害了,他们不仅能检查,还能“整容”!他们可以修改你提交的资源对象,比如自动添加标签、设置默认值,甚至改变容器的镜像版本! 就像给毛坯房精装修一样,让你省心省力。

  • Validating Admission Webhook(验证准入 Webhook): 这支队伍更像“质检员”,他们只负责检查,如果发现不符合要求的资源,直接驳回,绝不手软! 就像海关一样,不符合规定的东西,一律禁止入内!

工作流程:一场完美的拦截表演

  1. 用户通过 kubectl 或 API 客户端向 Kubernetes API Server 发起请求(比如创建 Pod)。
  2. API Server 接收到请求后,会先进行身份验证和授权。
  3. 如果配置了 Admission Webhooks,API Server 会将请求发送到相应的 Webhook 服务。
  4. Webhook 服务根据自身的策略进行验证或修改。
  5. Webhook 服务将结果返回给 API Server。
  6. 如果 Webhook 验证通过,API Server 将继续处理请求,否则将拒绝请求。

可以用一个表格来更清晰地展示这个流程:

步骤 组件 动作
1 用户/API 客户端 发起请求(创建、更新、删除资源)
2 API Server 接收请求,进行身份验证和授权
3 API Server 检查是否配置了 Admission Webhooks,如果有,则将请求发送到相应的 Webhook 服务
4 Admission Webhook 接收请求,根据自身策略进行验证或修改
5 Admission Webhook 将结果返回给 API Server(允许或拒绝,以及修改后的资源)
6 API Server 根据 Webhook 的结果,继续处理请求或拒绝请求

为什么要用 Admission Webhooks?

  • 增强安全性: 限制容器的能力,防止恶意代码的执行。
  • 提高合规性: 强制执行命名规范、资源限制等,确保符合组织的安全和合规性要求。
  • 自动化运维: 自动注入 sidecar 容器,配置网络策略,简化运维流程。
  • 定制化策略: 根据业务需求,自定义各种复杂的策略,满足个性化需求。

实战演练:手把手打造你的策略卫士

光说不练假把式,接下来,咱们来动手写一个简单的 Validating Admission Webhook,实现一个“强制标签”的策略:所有 Pod 都必须包含 owner=team-a 这个标签,否则就拒绝创建。

1. 创建 Webhook 服务

首先,我们需要创建一个 Webhook 服务,这个服务会接收来自 API Server 的请求,并根据我们的策略进行验证。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mandatory-label-webhook
spec:
  selector:
    matchLabels:
      app: mandatory-label-webhook
  replicas: 1
  template:
    metadata:
      labels:
        app: mandatory-label-webhook
    spec:
      containers:
      - name: webhook
        image: your-image-here # 替换成你自己的镜像
        imagePullPolicy: Always
        ports:
        - containerPort: 443
---
apiVersion: v1
kind: Service
metadata:
  name: mandatory-label-webhook
spec:
  selector:
    app: mandatory-label-webhook
  ports:
  - port: 443
    targetPort: 443

注意: 上面的 your-image-here 需要替换成你自己的镜像,这个镜像需要包含 Webhook 服务的代码。 后面我们会讲到如何编写 Webhook 服务的代码。

2. 编写 Webhook 服务代码

接下来,我们需要编写 Webhook 服务的代码,这个代码会接收来自 API Server 的请求,并进行验证。 这里我们使用 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/certs/tls.crt", "/etc/certs/tls.key", nil))
}

func validateHandler(w http.ResponseWriter, r *http.Request) {
    var body []byte
    if r.Body != nil {
        if data, err := ioutil.ReadAll(r.Body); err == nil {
            body = data
        }
    }

    // Verify the content type is accurate
    contentType := r.Header.Get("Content-Type")
    if contentType != "application/json" {
        log.Printf("contentType=%s, expect application/json", contentType)
        return
    }

    var review admissionv1.AdmissionReview
    if err := json.Unmarshal(body, &review); err != nil {
        log.Printf("Could not unmarshal admission review: %v", err)
        http.Error(w, "Could not unmarshal admission review", http.StatusBadRequest)
        return
    }

    pod := corev1.Pod{}
    if err := json.Unmarshal(review.Request.Object.Raw, &pod); err != nil {
        log.Printf("Could not unmarshal pod: %v", err)
        http.Error(w, "Could not unmarshal pod", http.StatusBadRequest)
        return
    }

    // Check if the pod has the required label
    if _, ok := pod.ObjectMeta.Labels["owner"]; !ok || pod.ObjectMeta.Labels["owner"] != "team-a" {
        review.Response = &admissionv1.AdmissionResponse{
            Allowed: false,
            Result: &metav1.Status{
                Message: "Pod must have label 'owner=team-a'",
            },
        }
    } else {
        review.Response = &admissionv1.AdmissionResponse{
            Allowed: true,
        }
    }

    review.Response.UID = review.Request.UID
    review.Response.Kind = review.Kind
    review.Response.APIVersion = review.APIVersion

    resp, err := json.Marshal(review)
    if err != nil {
        log.Printf("Could not marshal admission review response: %v", err)
        http.Error(w, fmt.Sprintf("Could not marshal admission review response: %v", err), http.StatusInternalServerError)
        return
    }

    w.Header().Set("Content-Type", "application/json")
    if _, err := w.Write(resp); err != nil {
        log.Printf("Could not write response: %v", err)
        http.Error(w, fmt.Sprintf("Could not write response: %v", err), http.StatusInternalServerError)
        return
    }
}

代码解释:

  • validateHandler 函数是 Webhook 服务的核心,它接收来自 API Server 的请求,并进行验证。
  • 代码首先解析 AdmissionReview 对象,这个对象包含了请求的所有信息。
  • 然后,代码检查 Pod 是否包含 owner=team-a 标签,如果没有,就拒绝创建 Pod。
  • 最后,代码将验证结果返回给 API Server。

3. 创建 TLS 证书

为了保证通信安全,我们需要为 Webhook 服务创建 TLS 证书。

openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes -subj '/CN=mandatory-label-webhook.default.svc'

注意: CN (Common Name) 必须是 Webhook 服务的完整域名,包括 Service Name 和 Namespace。

4. 创建 Secret 存储证书

将证书存储在 Kubernetes Secret 中,供 Webhook 服务使用。

apiVersion: v1
kind: Secret
metadata:
  name: mandatory-label-webhook-certs
type: Opaque
data:
  tls.crt: $(base64 -w 0 cert.pem)
  tls.key: $(base64 -w 0 key.pem)

注意: 使用 base64 命令将证书内容进行编码。

5. 配置 Webhook

最后,我们需要配置 Webhook,告诉 Kubernetes API Server 如何调用我们的 Webhook 服务。

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  name: mandatory-label-webhook-configuration
webhooks:
  - name: mandatory-label.example.com
    clientConfig:
      service:
        name: mandatory-label-webhook
        namespace: default
        path: /validate
      caBundle: $(base64 -w 0 cert.pem)
    rules:
      - apiGroups:   [""]
        apiVersions: ["v1"]
        operations:  ["CREATE"]
        resources:   ["pods"]
    admissionReviewVersions: ["v1"]
    sideEffects: None

配置解释:

  • clientConfig.service 指定了 Webhook 服务的地址和路径。
  • caBundle 指定了用于验证 Webhook 服务证书的 CA 证书。
  • rules 指定了 Webhook 拦截的资源类型和操作。
  • sideEffects 指定了 Webhook 的副作用,None 表示没有副作用。

6. 测试 Webhook

现在,我们可以测试一下我们的 Webhook 是否生效了。

首先,创建一个不包含 owner=team-a 标签的 Pod:

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

尝试创建这个 Pod,你会发现 API Server 会拒绝这个请求,并返回错误信息:Pod must have label 'owner=team-a'

然后,创建一个包含 owner=team-a 标签的 Pod:

apiVersion: v1
kind: Pod
metadata:
  name: test-pod-with-label
  labels:
    owner: team-a
spec:
  containers:
  - name: nginx
    image: nginx

这次,API Server 会允许创建这个 Pod。

🎉 恭喜你!你已经成功创建了一个 Validating Admission Webhook,并实现了“强制标签”的策略!

策略管理:运筹帷幄,决胜千里

有了 Webhooks,就像有了强大的武器,但如何运用这些武器,才能真正发挥它们的威力呢? 这就需要我们进行有效的策略管理。

策略设计的原则:

  • 最小权限原则: 只授予必要的权限,避免过度授权。
  • 分层原则: 将策略分为不同的层级,比如集群级别、命名空间级别、应用级别,方便管理和维护。
  • 可观测性原则: 监控 Webhook 的运行状态,及时发现和解决问题。
  • 可审计性原则: 记录 Webhook 的执行日志,方便审计和追溯。

策略管理的工具:

  • OPA (Open Policy Agent): 一个通用的策略引擎,可以用于各种场景,包括 K8s Admission Control。 OPA 使用 Rego 语言来编写策略,Rego 是一种声明式语言,易于学习和使用。
  • Kyverno: 一个专门为 Kubernetes 设计的策略引擎。 Kyverno 使用 Kubernetes 原生的资源对象来定义策略,无需学习新的语言。
  • Gatekeeper: 一个基于 OPA 的 K8s Admission Controller。 Gatekeeper 提供了一些预定义的策略,方便用户快速上手。

策略更新的策略:

  • 灰度发布: 将新的策略先应用到小部分 Pod 上,观察其运行情况,如果没有问题,再逐步扩大范围。
  • 蓝绿部署: 创建一个包含新策略的 Webhook 服务,与旧的 Webhook 服务并行运行,如果没有问题,再切换流量到新的 Webhook 服务。
  • 回滚机制: 建立完善的回滚机制,如果新的策略出现问题,可以快速回滚到旧的策略。

高级技巧:让你的 Webhooks 更上一层楼

  • 动态配置: 将 Webhook 的配置存储在 ConfigMap 中,动态更新 Webhook 的行为。
  • 外部数据源: 从外部数据源(比如数据库、API)获取策略配置,实现更灵活的策略管理。
  • 多 Webhook 组合: 将多个 Webhook 组合在一起,实现更复杂的策略。
  • 监控和告警: 监控 Webhook 的性能指标(比如延迟、错误率),并设置告警,及时发现和解决问题。

总结:K8s 策略的未来

K8s Admission Webhooks 是一个强大的工具,可以帮助你在运行时实施自定义的策略,增强安全性,提高合规性,自动化运维,并满足个性化需求。 掌握 Admission Webhooks 的使用,就像掌握了 K8s 王国的钥匙,让你能够更好地管理和控制你的集群。

当然,Admission Webhooks 也不是万能的,它也有一些局限性,比如:

  • 复杂性: 编写和维护 Webhook 代码需要一定的技术水平。
  • 性能影响: Webhook 会增加 API Server 的延迟,需要进行优化。
  • 依赖性: Webhook 依赖于外部服务,需要保证外部服务的可用性。

未来,K8s 策略管理将会朝着更加自动化、智能化、可观测的方向发展。 我们可以期待更多的策略管理工具的出现,以及更加强大的策略引擎。

好了,今天的讲座就到这里,希望大家有所收获! 如果大家还有什么问题,欢迎在评论区留言,我们一起讨论! 谢谢大家! 🎉

发表回复

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