4. Webhook 服务搭建:使用 Go 语言编写一个基础的 HTTPS Webhook Server
Webhook 接收端是调试流程中的核心组件。Kubernetes APIServer 会以 HTTPS POST 请求的方式,将 AdmissionReview 对象发送到我们搭建的服务上。本节将使用 Go 标准库 net/http 构建一个最小可用的 Webhook Server,并为其配置 TLS 证书,使其能够被 APIServer 信任。
4.1 项目结构与依赖
我们采用 Go 模块化组织代码,不依赖第三方 Web 框架,仅使用标准库和官方 k8s.io/api 包解析 AdmissionReview。
| 文件/目录 | 作用 |
|---|---|
main.go |
入口:启动 HTTPS 服务,注册路由 |
handler.go |
AdmissionReview 处理逻辑(校验/变更) |
tls/ |
存放服务端证书 server.crt 和私钥 server.key |
go.mod |
模块定义,依赖 k8s.io/api 和 k8s.io/apimachinery |
调试建议: 开发阶段可使用自签名证书,但务必在
ValidatingWebhookConfiguration 的 caBundle 字段中注入对应的 CA 证书,否则 APIServer 会拒绝连接。
4.2 核心代码实现
以下代码展示了 Webhook Server 的最小骨架。它监听 /validate 和 /mutate 两个端点,分别对应校验和变更准入控制。
// main.go
package main
import (
"log"
"net/http"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/validate", handleAdmissionReview)
mux.HandleFunc("/mutate", handleAdmissionReview)
// 使用 TLS 启动服务
log.Println("Webhook server starting on :8443")
err := http.ListenAndServeTLS(":8443", "tls/server.crt", "tls/server.key", mux)
if err != nil {
log.Fatalf("server failed: %v", err)
}
}
// handler.go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
admissionv1 "k8s.io/api/admission/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func handleAdmissionReview(w http.ResponseWriter, r *http.Request) {
// 1. 读取请求体
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "unable to read body", http.StatusBadRequest)
return
}
defer r.Body.Close()
// 2. 反序列化为 AdmissionReview
var review admissionv1.AdmissionReview
if err := json.Unmarshal(body, &review); err != nil {
http.Error(w, fmt.Sprintf("unmarshal error: %v", err), http.StatusBadRequest)
return
}
// 3. 构造响应(此处为透传允许)
responseReview := admissionv1.AdmissionReview{
TypeMeta: metav1.TypeMeta{
APIVersion: "admission.k8s.io/v1",
Kind: "AdmissionReview",
},
Response: &admissionv1.AdmissionResponse{
UID: review.Request.UID,
Allowed: true,
},
}
// 4. 序列化并返回
respBytes, _ := json.Marshal(responseReview)
w.Header().Set("Content-Type", "application/json")
w.Write(respBytes)
}
常见错误: 响应中的
UID 必须与请求中的 UID 一致,否则 APIServer 会丢弃该响应并记录 uid mismatch 错误。
4.3 HTTPS 与证书配置
Kubernetes APIServer 强制要求 Webhook 使用 HTTPS,且证书必须由受信任的 CA 签发。自签名证书的生成步骤如下:
- 生成 CA 私钥和根证书
- 生成服务端私钥和证书签名请求(CSR)
- 使用 CA 签发服务端证书
- 将 CA 证书 Base64 编码后填入
caBundle
# 生成 CA 私钥和证书
openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -subj "/CN=webhook-ca" -days 365 -out ca.crt
# 生成服务端私钥和 CSR
openssl genrsa -out tls/server.key 2048
openssl req -new -key tls/server.key -subj "/CN=webhook-server.webhook-demo.svc" -out server.csr
# 签发服务端证书(有效期 1 年)
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out tls/server.crt -days 365
# 获取 caBundle(用于 WebhookConfiguration)
cat ca.crt | base64 -w0
关键点: 证书的 Common Name (CN) 必须与 Webhook 服务的 Kubernetes Service DNS 名称一致,例如
webhook-server.webhook-demo.svc。如果使用集群外部署,CN 应为可解析的域名或 IP。
4.4 Webhook 请求/响应流程
下图展示了 APIServer 与 Webhook Server 之间的完整交互过程:
4.5 启动与验证
完成代码和证书准备后,按以下步骤启动服务并验证:
| 步骤 | 命令/操作 | 预期结果 |
|---|---|---|
| 1. 编译 | go build -o webhook-server . |
生成可执行文件 |
| 2. 启动 | ./webhook-server |
日志输出 Webhook server starting on :8443 |
| 3. 本地测试 | curl -k -X POST https://localhost:8443/validate -d @test-review.json |
返回 JSON 格式的 AdmissionReview 响应 |
| 4. 检查证书 | openssl s_client -connect localhost:8443 -showcerts |
显示服务端证书链 |
测试数据准备: 创建一个
test-review.json 文件,内容为最小化的 AdmissionReview 请求体,用于快速验证服务是否正常响应。
{
"apiVersion": "admission.k8s.io/v1",
"kind": "AdmissionReview",
"request": {
"uid": "test-uid-12345",
"object": {
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "test-pod"
}
}
}
}
4.6 常见问题与排查
- 证书错误: APIServer 日志中出现
x509: certificate signed by unknown authority,检查caBundle是否与 CA 证书一致。 - 超时: 如果 Webhook 处理时间超过 30 秒,APIServer 会记录
context deadline exceeded,需要优化处理逻辑或调整timeoutSeconds。 - UID 不匹配: 响应中
UID字段缺失或错误,APIServer 会丢弃响应并记录uid mismatch。 - 端口被占用: 确保 8443 端口未被其他进程占用,或修改监听端口。