需求:使用 Python 编写一个 Mutating Admission Webhook,以便在创建新的 Pod 时自动注入 Sidecar 容器(例如用于日志收集的 Fluentd)
背景知识
- Admission Webhook:Kubernetes 中的一种机制,允许你在对象被创建或更新时对其进行拦截和修改。
- Mutating Webhook:一种 Admission Webhook,可以修改(变异)传入的 Kubernetes 对象(如 Pod)。
- Sidecar 容器:与主容器一起运行的辅助容器,通常用于日志收集、监控等。
总体步骤
- 编写一个 Python Web 服务器,处理 AdmissionReview 请求并注入 Sidecar。
- 部署 Webhook 服务到 Kubernetes 集群中,并确保它可以被 API Server 访问。
- 配置 MutatingWebhookConfiguration,使得 Kubernetes 在创建 Pod 时调用你的 Webhook。
- 验证你的 Webhook 是否按预期工作。
编写 Mutating Webhook 服务器
1. 创建 Webhook 服务器
首先,使用 Python 的 flask
或 aiohttp
等框架创建一个 HTTPS 服务器。这里我们使用 flask
作为示例。
安装依赖:
pip install flask flask-jsonschema-validator cryptography
2. 处理 AdmissionReview 请求
编写服务器代码,处理 Kubernetes 发送的 AdmissionReview 请求,并根据需要修改 Pod 配置。
from flask import Flask, request, jsonify
import json
import base64
import ssl
app = Flask(__name__)
@app.route('/mutate', methods=['POST'])
def mutate():
admission_review = request.get_json()
request_uid = admission_review['request']['uid']
pod = admission_review['request']['object']
# 检查是否需要跳过注入
if 'sidecar-injected' in pod['metadata'].get('annotations', {}):
return admission_response(request_uid, allowed=True)
# 构建 Patch 操作
patch = get_pod_patch()
admission_response_data = {
'uid': request_uid,
'allowed': True,
'patch': base64.b64encode(json.dumps(patch).encode()).decode(),
'patchType': 'JSONPatch'
}
response = {
'apiVersion': 'admission.k8s.io/v1',
'kind': 'AdmissionReview',
'response': admission_response_data
}
return jsonify(response)
def admission_response(uid, allowed, patch=None, patch_type=None):
response = {
'uid': uid,
'allowed': allowed
}
if patch and patch_type:
response['patch'] = base64.b64encode(json.dumps(patch).encode()).decode()
response['patchType'] = patch_type
return jsonify({
'apiVersion': 'admission.k8s.io/v1',
'kind': 'AdmissionReview',
'response': response
})
def get_pod_patch():
# 定义要注入的 Sidecar 容器
sidecar_container = {
'name': 'fluentd-sidecar',
'image': 'registry.cn-shanghai.aliyuncs.com/oversea_speedup/fluentd:latest',
'resources': {},
'volumeMounts': [
{
'name': 'varlog',
'mountPath': '/var/log'
}
]
}
# 定义要添加的 Volume
volume = {
'name': 'varlog',
'emptyDir': {}
}
# JSON Patch 操作
patch = [
{'op': 'add', 'path': '/spec/containers/-', 'value': sidecar_container},
{'op': 'add', 'path': '/spec/volumes/-', 'value': volume},
{'op': 'add', 'path': '/metadata/annotations', 'value': {}},
{'op': 'add', 'path': '/metadata/annotations/sidecar-injected', 'value': 'true'}
]
return patch
if __name__ == '__main__':
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain('/tls/tls.crt', '/tls/tls.key')
app.run(host='0.0.0.0', port=443, ssl_context=context)
解释:
- mutate():接收 AdmissionReview 请求,解析 Pod 对象。
- get_pod_patch():定义如何修改 Pod,包括添加 Sidecar 容器、卷和注解。
- admission_response():构建 AdmissionReview 响应。
3. 注入 Sidecar 容器
在 get_pod_patch()
函数中,定义了要注入的 Sidecar 容器和需要添加的卷。使用 JSON Patch 格式指定对 Pod 的修改。
注意:
- Volume 和 VolumeMounts:确保 Sidecar 容器和主容器共享日志目录。
- Annotations:使用注解标记已注入的 Pod,防止重复注入。
4. 构建和运行 Docker 镜像
Dockerfile:
FROM registry.cn-shanghai.aliyuncs.com/joezhao/python:3.9-slim
RUN pip install flask flask-jsonschema-validator cryptography
COPY server.py /server.py
CMD ["python", "/server.py"]
构建镜像:
docker build -t your-registry/sidecar-injector:latest .
推送镜像:
docker push your-registry/sidecar-injector:latest
部署到 Kubernetes 集群
证书生成的完整步骤
1. 生成 CA(自签名根证书)
首先,生成 CA 的私钥和证书,这个证书用于签署 Webhook 服务的证书:
# 生成 CA 私钥
openssl genrsa -out ca.key 2048
# 生成自签名 CA 证书
openssl req -x509 -new -nodes -key ca.key -sha256 -days 365 -out ca.crt -subj "/CN=Webhook CA"
ca.key
:CA 的私钥。ca.crt
:CA 的自签名证书,后续会用来验证服务端证书。
2. 生成 Webhook 服务的私钥和 CSR
Webhook 服务的证书必须包含 SAN 字段,匹配 Kubernetes 服务域名 sidecar-injector.default.svc
。
创建一个配置文件 csr.conf
:
conf
复制代码
[ req ]
default_bits = 2048
prompt = no
default_md = sha256
req_extensions = req_ext
distinguished_name = dn
[ dn ]
CN = sidecar-injector.default.svc
[ req_ext ]
subjectAltName = @alt_names
[ alt_names ]
DNS.1 = sidecar-injector
DNS.2 = sidecar-injector.default
DNS.3 = sidecar-injector.default.svc
生成 Webhook 服务的私钥和 CSR:
# 生成私钥和 CSR
openssl req -new -nodes -keyout tls.key -out tls.csr -config csr.conf
tls.key
:Webhook 服务的私钥。tls.csr
:证书签名请求文件。
3. 使用 CA 签发服务端证书
使用 ca.key
和 ca.crt
签发 Webhook 服务的证书:
openssl x509 -req -in tls.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-out tls.crt -days 365 -extensions req_ext -extfile csr.conf
输出文件:
tls.crt
:服务端的证书。ca.crt
:CA 根证书,用于验证tls.crt
。
4. 将证书添加到 Kubernetes
- 创建 Kubernetes Secret,包含
tls.crt
和tls.key
:
kubectl create secret tls sidecar-injector-certs \
--cert=tls.crt --key=tls.key -n default
- 获取
ca.crt
的 Base64 编码:
cat ca.crt | base64 -w 0
将编码后的内容用于 MutatingWebhookConfiguration
中的 caBundle
字段。
5. 总结证书的关系
- CA 证书 (
ca.crt
):
- 用于验证服务端证书 (
tls.crt
)。 - 其 Base64 编码内容写入
MutatingWebhookConfiguration
的caBundle
。 - Webhook 服务证书 (
tls.crt
和tls.key
):
- 用于 Webhook 服务器的 HTTPS 通信。
- 必须包含 SAN,确保与
sidecar-injector.default.svc
匹配。
2. 创建 Kubernetes 服务和部署
部署文件:
apiVersion: apps/v1
kind: Deployment
metadata:
name: sidecar-injector
labels:
app: sidecar-injector
spec:
replicas: 1
selector:
matchLabels:
app: sidecar-injector
template:
metadata:
labels:
app: sidecar-injector
spec:
containers:
- name: sidecar-injector
#下面换成刚才自己push的镜像
image: registry.cn-shanghai.aliyuncs.com/joezhao/sidecar-fluentd:latest
ports:
- containerPort: 443
volumeMounts:
- name: webhook-certs
mountPath: "/tls"
readOnly: true
volumes:
- name: webhook-certs
secret:
secretName: sidecar-injector-certs
---
apiVersion: v1
kind: Service
metadata:
name: sidecar-injector
labels:
app: sidecar-injector
spec:
ports:
- port: 443
targetPort: 443
selector:
app: sidecar-injector
应用部署:
kubectl apply -f deployment.yaml
3. 创建 MutatingWebhookConfiguration
获取 CA Bundle:
# 如果使用自签名证书
CA_BUNDLE=$(cat tls.crt | base64 | tr -d '\n')
# 查出来直接贴进去
cat ca.crt | base64 -w 0
Webhook 配置文件:
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
name: sidecar-injector-webhook
webhooks:
- name: sidecar-injector.k8s.io
clientConfig:
service:
name: sidecar-injector
namespace: default
path: "/mutate"
# 下面别用变量,直接贴刚刚查出来的证书编码base64解码
caBundle: "${CA_BUNDLE}"
rules:
- operations: ["CREATE"]
apiGroups: [""]
apiVersions: ["v1"]
resources: ["pods"]
admissionReviewVersions: ["v1", "v1beta1"]
sideEffects: None
timeoutSeconds: 5
应用配置:
kubectl apply -f webhook-configuration.yaml
验证 Webhook 功能
创建一个测试 Pod:
apiVersion: v1
kind: Pod
metadata:
name: test-pod
labels:
app: test
spec:
containers:
- name: nginx
image: nginx
创建 Pod:
kubectl apply -f test-pod.yaml
检查 Pod 是否被注入 Sidecar:
kubectl describe pod test-pod
在输出中,你应该看到 fluentd-sidecar
容器已被注入。