用Webhook 自动给注入sidecar收集日志

需求:使用 Python 编写一个 Mutating Admission Webhook,以便在创建新的 Pod 时自动注入 Sidecar 容器(例如用于日志收集的 Fluentd)

背景知识

  • Admission Webhook:Kubernetes 中的一种机制,允许你在对象被创建或更新时对其进行拦截和修改。
  • Mutating Webhook:一种 Admission Webhook,可以修改(变异)传入的 Kubernetes 对象(如 Pod)。
  • Sidecar 容器:与主容器一起运行的辅助容器,通常用于日志收集、监控等。

总体步骤

  1. 编写一个 Python Web 服务器,处理 AdmissionReview 请求并注入 Sidecar。
  2. 部署 Webhook 服务到 Kubernetes 集群中,并确保它可以被 API Server 访问。
  3. 配置 MutatingWebhookConfiguration,使得 Kubernetes 在创建 Pod 时调用你的 Webhook。
  4. 验证你的 Webhook 是否按预期工作。

编写 Mutating Webhook 服务器

1. 创建 Webhook 服务器

首先,使用 Python 的 flaskaiohttp 等框架创建一个 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.keyca.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
  1. 创建 Kubernetes Secret,包含 tls.crttls.key
kubectl create secret tls sidecar-injector-certs \
  --cert=tls.crt --key=tls.key -n default
  1. 获取 ca.crt 的 Base64 编码
cat ca.crt | base64 -w 0

将编码后的内容用于 MutatingWebhookConfiguration 中的 caBundle 字段。


5. 总结证书的关系
  • CA 证书 (ca.crt)
  • 用于验证服务端证书 (tls.crt)。
  • 其 Base64 编码内容写入 MutatingWebhookConfigurationcaBundle
  • Webhook 服务证书 (tls.crttls.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 容器已被注入。


参考资料

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇