typora/daliy_note/10.9/kubernetes admission webhook.md

231 lines
8.2 KiB
Markdown
Raw Normal View History

2024-12-11 21:48:55 -05:00
`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 创建时自动添加一个环境变量。这个示例可以作为实现更复杂逻辑的起点。