Admission Controller 概述
Kubernetes Admission Controller 是 API Server 中的一個重要元件,負責在物件持久化到 etcd 之前攔截請求並進行處理。當一個 API 請求通過身份驗證(Authentication)和授權(Authorization)之後,Admission Controller 會對請求進行額外的驗證或修改。
Admission Controller 的工作流程如下:
1
| API 請求 → 身份驗證 → 授權 → Admission Controller → etcd 持久化
|
主要用途包括:
- 強制執行安全策略
- 設定資源預設值
- 限制資源配額
- 注入 sidecar 容器
驗證型與變更型
Admission Controller 分為兩種類型:
驗證型(Validating)
驗證型 Admission Controller 只會檢查請求是否符合特定條件,不會修改請求內容。如果驗證失敗,請求會被拒絕。
常見用途:
- 檢查 Pod 是否有設定資源限制
- 驗證映像檔是否來自可信任的 Registry
- 確保 Label 符合命名規範
變更型(Mutating)
變更型 Admission Controller 可以修改請求的內容,通常用於設定預設值或注入額外配置。
常見用途:
- 自動注入 sidecar 容器(如 Istio)
- 設定預設的資源請求和限制
- 添加 Label 或 Annotation
執行順序:Mutating Admission Controller 會先於 Validating Admission Controller 執行。
內建 Admission Controller
Kubernetes 提供多個內建的 Admission Controller,以下是常用的幾個:
| 名稱 | 類型 | 說明 |
|---|
| NamespaceLifecycle | Validating | 防止在終止中或不存在的 Namespace 建立資源 |
| LimitRanger | Mutating | 為 Pod 設定預設的資源限制 |
| ResourceQuota | Validating | 確保資源使用不超過配額 |
| PodSecurity | Validating | 強制執行 Pod 安全標準 |
| DefaultStorageClass | Mutating | 為 PVC 設定預設的 StorageClass |
查看目前啟用的 Admission Controller:
1
2
| kubectl exec -it kube-apiserver-<node> -n kube-system -- \
kube-apiserver -h | grep enable-admission-plugins
|
動態 Admission Webhook
除了內建的 Admission Controller,Kubernetes 還支援動態 Admission Webhook,讓您可以自定義准入邏輯。
Webhook 的運作方式:
- API Server 收到請求
- 將請求內容以 AdmissionReview 格式發送到 Webhook 服務
- Webhook 服務處理請求並回傳結果
- API Server 根據結果決定是否允許請求
ValidatingWebhookConfiguration
ValidatingWebhookConfiguration 用於定義驗證型 Webhook。以下是完整的配置範例:
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
29
30
31
32
33
34
35
36
37
38
39
40
| apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: pod-policy-validator
webhooks:
- name: pod-policy.example.com
admissionReviewVersions:
- v1
- v1beta1
clientConfig:
service:
name: webhook-service
namespace: webhook-system
path: /validate
port: 443
caBundle: LS0tLS1CRUdJTi... # Base64 編碼的 CA 憑證
rules:
- apiGroups:
- ""
apiVersions:
- v1
operations:
- CREATE
- UPDATE
resources:
- pods
scope: Namespaced
failurePolicy: Fail
matchPolicy: Equivalent
sideEffects: None
timeoutSeconds: 10
namespaceSelector:
matchLabels:
environment: production
objectSelector:
matchExpressions:
- key: skip-validation
operator: NotIn
values:
- "true"
|
重要欄位說明:
failurePolicy:Webhook 無法連線時的處理方式(Fail 或 Ignore)sideEffects:是否有副作用(None、Some、Unknown)timeoutSeconds:請求逾時時間(預設 10 秒,最大 30 秒)
MutatingWebhookConfiguration
MutatingWebhookConfiguration 用於定義變更型 Webhook。以下範例展示如何自動注入 Label:
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
29
| apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
name: pod-label-injector
webhooks:
- name: label-injector.example.com
admissionReviewVersions:
- v1
clientConfig:
service:
name: label-injector-service
namespace: webhook-system
path: /mutate
port: 443
caBundle: LS0tLS1CRUdJTi...
rules:
- apiGroups:
- ""
apiVersions:
- v1
operations:
- CREATE
resources:
- pods
scope: Namespaced
failurePolicy: Ignore
sideEffects: None
timeoutSeconds: 5
reinvocationPolicy: IfNeeded
|
reinvocationPolicy 說明:
Never:每個 Webhook 只會被呼叫一次IfNeeded:如果其他 Webhook 修改了物件,可能會再次呼叫
Webhook 實作範例
以下是使用 Python Flask 實作的簡易 Webhook 服務:
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
| from flask import Flask, request, jsonify
import base64
import json
app = Flask(__name__)
@app.route('/validate', methods=['POST'])
def validate():
admission_review = request.get_json()
pod = admission_review['request']['object']
# 檢查 Pod 是否有設定資源限制
allowed = True
message = "Validation passed"
containers = pod.get('spec', {}).get('containers', [])
for container in containers:
resources = container.get('resources', {})
if not resources.get('limits'):
allowed = False
message = f"Container {container['name']} 必須設定資源限制"
break
response = {
"apiVersion": "admission.k8s.io/v1",
"kind": "AdmissionReview",
"response": {
"uid": admission_review['request']['uid'],
"allowed": allowed,
"status": {
"message": message
}
}
}
return jsonify(response)
@app.route('/mutate', methods=['POST'])
def mutate():
admission_review = request.get_json()
# 建立 JSON Patch 來添加 Label
patch = [
{
"op": "add",
"path": "/metadata/labels/injected-by",
"value": "admission-webhook"
}
]
response = {
"apiVersion": "admission.k8s.io/v1",
"kind": "AdmissionReview",
"response": {
"uid": admission_review['request']['uid'],
"allowed": True,
"patchType": "JSONPatch",
"patch": base64.b64encode(
json.dumps(patch).encode()
).decode()
}
}
return jsonify(response)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=443, ssl_context=('cert.pem', 'key.pem'))
|
故障排除
常見問題與解決方案
1. Webhook 連線失敗
1
2
3
4
5
6
| # 檢查 Webhook 服務是否正常運作
kubectl get svc -n webhook-system
kubectl get pods -n webhook-system
# 檢查憑證是否正確
kubectl get secret webhook-tls -n webhook-system -o yaml
|
2. 請求被意外拒絕
1
2
3
4
5
6
| # 查看 API Server 日誌
kubectl logs kube-apiserver-<node> -n kube-system | grep admission
# 暫時將 failurePolicy 改為 Ignore 進行測試
kubectl patch validatingwebhookconfiguration pod-policy-validator \
--type='json' -p='[{"op": "replace", "path": "/webhooks/0/failurePolicy", "value": "Ignore"}]'
|
3. 憑證問題
確保 caBundle 包含正確的 CA 憑證:
1
2
3
4
5
6
| # 取得 CA 憑證並編碼
CA_BUNDLE=$(kubectl config view --raw -o jsonpath='{.clusters[0].cluster.certificate-authority-data}')
# 更新 WebhookConfiguration
kubectl patch validatingwebhookconfiguration pod-policy-validator \
--type='json' -p="[{\"op\": \"replace\", \"path\": \"/webhooks/0/clientConfig/caBundle\", \"value\": \"${CA_BUNDLE}\"}]"
|
最佳實踐
安全性
- 使用 TLS:Webhook 必須使用 HTTPS,確保通訊安全
- 限制 Namespace:使用
namespaceSelector 限制 Webhook 的作用範圍 - 排除系統 Namespace:避免影響 kube-system 等關鍵 Namespace
1
2
3
4
5
6
7
| namespaceSelector:
matchExpressions:
- key: kubernetes.io/metadata.name
operator: NotIn
values:
- kube-system
- kube-public
|
可靠性
- 設定合理的 timeout:建議 5-10 秒
- 使用 failurePolicy: Ignore:在非關鍵場景下避免阻塞整個叢集
- 實作健康檢查:確保 Webhook 服務高可用
效能
- 使用 objectSelector:精確過濾需要處理的物件
- 快取頻繁存取的資料:減少 Webhook 處理時間
- 水平擴展:部署多個 Webhook Pod 副本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| apiVersion: apps/v1
kind: Deployment
metadata:
name: webhook-server
spec:
replicas: 3
selector:
matchLabels:
app: webhook-server
template:
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
topologyKey: kubernetes.io/hostname
|
參考資料