目录

自己动手写Kubernetes的webhook

概述

都是叫 admissionwebhook,实际会分成两类,分别是 mutating 和 validating。虽然 kubebuilder 可以帮忙实现 webhook,但是在本文暂不讨论,希望通过非脚手架的方式,以最原始的方式把 webhook 搭建起来。

实验计划

为了简单的实现一个 webhook 的完整功能,计划部署一个 webhook 进程,但需要注意的是,这个 webhook 进程不需要以容器的方式部署在 Kubernetes 集群,因此为了测试 webhook 的功能,部署 Kubernetes 集群的时候可以通过 kubeadm 创建一个 control-plane 即可。当然也可以用 kind 或者任何其他方式启动,下面是本人的测试环境。

1
2
3
# k get no -o wide
NAME            STATUS   ROLES           AGE   VERSION   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE        KERNEL-VERSION           CONTAINER-RUNTIME
192.168.1.201   Ready    control-plane   47h   v1.30.4   192.168.1.201   <none>        Anolis OS 8.8   5.10.134-13.an8.x86_64   containerd://1.7.14

操作步骤

通过 Python 实现一个 webhook 的基本功能,下面的 webhook 程序中,当接收到 kube-apiserver 的对 /validate 端口的请求就会在日志中打印请求体和最终的返回体。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
cat > webhook.py <<EOF
from flask import Flask, request, jsonify
import json

app = Flask(__name__)

@app.route('/validate', methods=['POST'])
def validate():
    admission_review = request.get_json()
    print("Pod creation request:", json.dumps(admission_review, indent=2))

    admission_response = {
        "uid": admission_review["request"]["uid"],
        "allowed": True
    }

    response = {
        "apiVersion": "admission.k8s.io/v1",
        "kind": "AdmissionReview",
        "response": admission_response
    }

    return jsonify(response)

if __name__ == '__main__':
    # 使用 SSL 证书启动 Flask 应用
    app.run(host='0.0.0.0', port=5000, ssl_context=('etcd_server.crt', 'etcd_server.key'))
EOF

然后在 Kubernetes 中创建名为 my-admission-webhook 的资源即可,需要注意 caBundle 是需要自行生成的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
cat > webhook.yaml <<EOF
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  name: my-admission-webhook
webhooks:
  - name: my-admission-webhook.example.com
    clientConfig:
      url: "https://192.168.1.201:5000/validate"
      caBundle: %%
    rules:
      - operations: ["CREATE", "DELETE"]
        apiGroups: ["*"]
        apiVersions: ["*"]
        resources: ["pods"]
    admissionReviewVersions: ["v1"]
    sideEffects: None
EOF

实验结果

打开两个终端,一边用来操作 Kubernetes 的资源,一边用来观察 webhook 的日志输出,从下图可以看到,在 delete/create Pod 这样简单的操作下,webhook 就可以成功拦截并且打印出日志了。

/%E8%87%AA%E5%B7%B1%E5%8A%A8%E6%89%8B%E5%86%99kubernetes%E7%9A%84webhook/img.png

参考资料

  1. 准入控制器参考
  2. mutatingadmissionwebhook
  3. validatingadmissionwebhook