typora/daliy_note/10.9/kubernetes admission webhook.md
2024-12-12 10:48:55 +08:00

231 lines
8.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

`AdmissionWebhook` 是 Kubernetes 中的一种动态准入控制器,用于在请求进入 Kubernetes API 服务器时对其进行修改或验证。准入控制器是 Kubernetes 安全模型的一部分,负责拦截请求并对其进行特定的检查或修改,以确保集群的安全性和策略的一致性。
### AdmissionWebhook 的作用
1. **验证Validating Webhooks**
- 这些 Webhook 在资源被创建、更新或删除之前对请求进行检查,以确保请求符合某些策略或规则。
- 如果验证失败,请求将被拒绝,并返回给用户一个错误信息。
2. **变更Mutating Webhooks**
- 这些 Webhook 可以在请求到达 API 服务器时修改请求对象。
- 例如,可以自动填充某些字段,或者根据预定义的规则调整资源配置。
### 使用场景
- **策略实施**:确保所有部署的应用程序都符合公司或组织的安全和合规策略。例如,确保所有 Pod 都具有指定的标签或资源限制。
- **自动化变更**:自动为资源添加或修改某些属性,例如为每个 Pod 添加一个特定的环境变量。
- **资源验证**:在资源被持久化之前,对其进行额外的验证,以防止错误配置。
- **审计和日志**:记录和监控特定请求,以帮助进行审计和故障排查。
### 工作原理
1. **注册 Webhook**
- 使用 `MutatingWebhookConfiguration``ValidatingWebhookConfiguration` 资源在 Kubernetes 中注册 Webhook。
2. **处理请求**
- 当一个请求到达 Kubernetes API 服务器时,配置的 Webhook 会被调用。
- Webhook 服务接收到请求对象,执行逻辑并返回一个响应,指示是否允许、拒绝或修改请求。
3. **响应处理**
- 对于 Mutating WebhookAPI 服务器将根据 Webhook 的响应修改请求对象。
- 对于 Validating WebhookAPI 服务器将检查 Webhook 的响应以决定是否执行请求。
### 实现示例
要实现一个 AdmissionWebhook通常需要以下步骤
1. **编写 Webhook 服务器**
- 使用编程语言(例如 Go编写一个 HTTP 服务器,能够处理 Kubernetes 的 AdmissionReview 请求。
2. **部署 Webhook 服务**
- 将 Webhook 服务器作为一个 Pod 部署到 Kubernetes 集群中。
3. **创建 Webhook 配置**
- 编写并应用 `MutatingWebhookConfiguration``ValidatingWebhookConfiguration`,指向你的 Webhook 服务。
### 注意事项
- **安全性**:确保 Webhook 服务的安全性,使用 TLS 进行通信,确保只有经过身份验证的请求才能访问。
- **性能影响**Webhook 会影响 API 请求的延迟,因为请求需要经过 Webhook 的处理。尽量优化 Webhook 的处理逻辑以减少开销。
- **故障处理**:设计 Webhook 时需要考虑故障处理,确保 Webhook 不会因为某些错误而导致集群无法正常工作。
通过使用 AdmissionWebhookKubernetes 提供了一个强大的机制来实施自定义策略和自动化配置,帮助运维人员更好地管理和控制集群行为。
### 举例
当然!下面是一个简单的示例,展示如何实现一个 Kubernetes Admission Webhook。这个示例将创建一个简单的 Mutating Admission Webhook它会在每个 Pod 创建之前向其中添加一个环境变量。
### 步骤 1: 编写 Webhook 服务器
我们将用 Go 编写一个简单的 HTTP 服务器,作为我们的 Webhook 服务。
```go
package main
import (
"encoding/json"
"net/http"
"log"
admissionv1 "k8s.io/api/admission/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
corev1 "k8s.io/api/core/v1"
)
func handleAdmission(w http.ResponseWriter, r *http.Request) {
var admissionReviewReq admissionv1.AdmissionReview
if err := json.NewDecoder(r.Body).Decode(&admissionReviewReq); err != nil {
log.Printf("Could not decode request: %v", err)
http.Error(w, "Bad request", http.StatusBadRequest)
return
}
podResource := metav1.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}
if admissionReviewReq.Request.Resource != podResource {
log.Printf("Expect resource to be %s", podResource)
http.Error(w, "Bad request", http.StatusBadRequest)
return
}
raw := admissionReviewReq.Request.Object.Raw
pod := corev1.Pod{}
if err := json.Unmarshal(raw, &pod); err != nil {
log.Printf("Could not unmarshal raw object: %v", err)
http.Error(w, "Bad request", http.StatusBadRequest)
return
}
// 添加环境变量
env := corev1.EnvVar{Name: "ADDED_BY_WEBHOOK", Value: "true"}
pod.Spec.Containers[0].Env = append(pod.Spec.Containers[0].Env, env)
// 创建补丁操作
patchBytes, err := json.Marshal([]map[string]interface{}{
{"op": "add", "path": "/spec/containers/0/env", "value": pod.Spec.Containers[0].Env},
})
if err != nil {
log.Printf("Could not marshal patch: %v", err)
http.Error(w, "Bad request", http.StatusBadRequest)
return
}
admissionReviewResponse := admissionv1.AdmissionReview{
Response: &admissionv1.AdmissionResponse{
UID: admissionReviewReq.Request.UID,
Allowed: true,
Patch: patchBytes,
PatchType: func() *admissionv1.PatchType {
pt := admissionv1.PatchTypeJSONPatch
return &pt
}(),
},
}
respBytes, err := json.Marshal(admissionReviewResponse)
if err != nil {
log.Printf("Could not marshal response: %v", err)
http.Error(w, "Bad request", http.StatusBadRequest)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(respBytes)
}
func main() {
http.HandleFunc("/mutate", handleAdmission)
log.Println("Starting server on :8080")
if err := http.ListenAndServeTLS(":8080", "/path/to/tls.crt", "/path/to/tls.key", nil); err != nil {
log.Fatalf("Failed to start server: %v", err)
}
}
```
### 步骤 2: 部署 Webhook 服务
1. **编译和打包服务**
- 将上述代码编译为二进制文件,创建一个 Docker 镜像,并将其推送到容器镜像仓库。
2. **创建 Kubernetes Deployment 和 Service**
编写一个 YAML 文件,将 Webhook 服务部署到 Kubernetes 集群中。
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: webhook-server
spec:
replicas: 1
selector:
matchLabels:
app: webhook-server
template:
metadata:
labels:
app: webhook-server
spec:
containers:
- name: webhook-server
image: <your-docker-image>
ports:
- containerPort: 8080
volumeMounts:
- name: webhook-certs
mountPath: "/etc/webhook/certs"
readOnly: true
volumes:
- name: webhook-certs
secret:
secretName: webhook-certs
---
apiVersion: v1
kind: Service
metadata:
name: webhook-server
spec:
ports:
- port: 443
targetPort: 8080
selector:
app: webhook-server
```
### 步骤 3: 创建 Webhook 配置
1. **创建 TLS 证书**
- 为 Webhook 服务创建 TLS 证书,并将其存储在 Kubernetes Secret 中。
2. **创建 `MutatingWebhookConfiguration`**
编写一个 YAML 文件,为 Kubernetes 注册 Webhook。
```yaml
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
name: pod-mutator
webhooks:
- name: pod-mutator.example.com
clientConfig:
service:
name: webhook-server
namespace: default
path: "/mutate"
caBundle: <base64-encoded-ca-cert>
rules:
- operations: ["CREATE"]
apiGroups: [""]
apiVersions: ["v1"]
resources: ["pods"]
admissionReviewVersions: ["v1"]
sideEffects: None
```
### 注意事项
- 确保 Webhook 服务的 TLS 证书正确配置,并与 `MutatingWebhookConfiguration` 中的 `caBundle` 匹配。
- 此示例只是一个简单的实现,生产环境中需要考虑安全性、性能优化和错误处理等方面。
- `caBundle` 应该是你的 CA 证书的 base64 编码。
通过上述步骤,你可以实现一个简单的 Admission Webhook它在 Pod 创建时自动添加一个环境变量。这个示例可以作为实现更复杂逻辑的起点。