博客 / 詳情

返回

從手動到自動:基於 Mutating Admission Webhook 實現 Envoy Sidecar 自動注入

前言

上一小節我們詳細討論瞭如何做流量劫持,並且使用initContainers來做自動劫持的配置。但是目前還有一個問題,如果我們的系統有好幾百個微服務,那作為重要的代理envoy,是手動注入的,難道每個微服務都要手動編輯一次 ?這顯然是不可承受的,所以這一節,我們來詳細討論一下自動注入的問題

環境準備

由於本節只討論容器注入,所以只需要準備一個普通的deployment就行了

apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
      - image: backend-service:v1
        imagePullPolicy: Never
        name: backend
        ports:
        - containerPort: 10000
          protocol: TCP
        resources: {}
      restartPolicy: Always

mutating admission webhooks

watermarked-auto_injection_1.png

簡單來説,就是當pod重啓的時候,會發起一系列的過程,其中在mutating admission controller 這裏,k8s提供了一個webhooks,可以回調到指定的地方去,所以我們需要創建一個server來處理該回調,添加一個容器進去,完成容器注入的工作

創建相關證書

cd /etc/kubernetes/pki

創建openssl.cnf

[ req ]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no

[ req_distinguished_name ]
CN = sidecar-webhook.default.svc

[ v3_req ]
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[ alt_names ]
DNS.1 = sidecar-webhook
DNS.2 = sidecar-webhook.default
DNS.3 = sidecar-webhook.default.svc

從k8s根證書中創建證書

sudo openssl req -x509 -newkey rsa:2048 \
  -keyout tls.key \
  -out tls.crt \
  -days 365 -nodes \
  -config openssl.cnf \
  -extensions v3_req

創建MutatingWebhookConfiguration

mytls=`cat tls.crt | base64 | tr -d '\n'`

echo 'apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
  name: sidecar-injector
webhooks:
- name: sidecar.demo.io
  clientConfig:
    service:
      name: sidecar-webhook
      namespace: default
      path: /mutate
    caBundle: '$mytls'
  rules:
  - apiGroups: [""]
    apiVersions: ["v1"]
    operations: ["CREATE"]
    resources: ["pods"]
  admissionReviewVersions: ["v1"]
  sideEffects: None' | kubectl apply -f -

創建服務來接收webhook

自動注入服務

這沒什麼可説的,需要注意的就是注入pod的配置直接寫在了代碼裏面,並且注入了2個部分,首先是sidecar container,其次是sidecar的volumes配置(pod級別的)

        patch := []map[string]interface{}{
                {
                        "op":   "add",
                        "path": "/spec/containers/-",
                        "value": map[string]interface{}{
                                "image":           "registry.cn-beijing.aliyuncs.com/wilsonchai/envoy:v1.32-latest",
                                "imagePullPolicy": "IfNotPresent",
                                "name":            "envoy",
                                "args":            []string{"-c", "/etc/envoy/envoy.yaml"},
                                "volumeMounts": []map[string]interface{}{
                                        {
                                                "mountPath": "/etc/envoy",
                                                "name":      "envoy-config",
                                        },
                                },
                        },
                },
                {
                        "op":   "add",
                        "path": "/spec/volumes/-",
                        "value": map[string]interface{}{
                                "configMap": map[string]interface{}{
                                        "defaultMode": 420,
                                        "name":        "envoy-config",
                                },
                                "name": "envoy-config",
                        },
                },
        }
▶ go run inject.go
2025/12/29 14:58:19 Webhook listening on :8443

打開8443端口以便接收請求

創建訪問路徑

我們的服務在集羣外,所以創建一個endpoint指向集羣之外

echo 'apiVersion: v1
kind: Service
metadata:
  name: sidecar-webhook
spec:
  ports:
  - port: 443
    targetPort: 8443
    protocol: TCP
  type: ClusterIP

---

apiVersion: v1
kind: Endpoints
metadata:
  name: sidecar-webhook
  namespace: default
subsets:
- addresses:
  - ip: 10.22.12.178
  ports:
  - port: 8443
    protocol: TCP' | kubectl apply -f -

驗證

重啓backend服務,kubectl rollout restart deploy backend

cannot bind '0.0.0.0:10000': Address already in use

出現了報錯,這應該是由於envoy是監聽10000端口,backend服務監聽的也是10000端口,現在它們在一個net namespace,就肯定要報錯了,所以改一下envoy的配置,監聽另外一個端口吧,10000改成10001

      listeners:
        - name: ingress_listener
          address:
            socket_address:
              address: 0.0.0.0
              port_value: 10001

再次重啓查看pod狀態

▶ kubectl get pod -owide -l app=backend
NAME                       READY   STATUS    RESTARTS   AGE    IP             NODE     NOMINATED NODE   READINESS GATES
backend-6bdf5d484b-5czgx   2/2     Running   0          100s   10.244.0.184   wilson   <none>           <none>

查看詳情

watermarked-auto_injection_2.png

自動注入了envoy容器

至此,架構圖如下:

watermarked-auto_injection_3.png

精細化注入

按照目前的配置,只要有pod create,就立刻回調集羣外的注入服務,如果k8s集羣的服務很多,並且頻繁的create/destroy,那就會對注入服務產生較大的壓力。如果在這些服務中,只有一些服務是需要使用自動注入功能的,那 就需要更精細化的注入管理

namespace打標籤

首先要調整一下MutatingWebhookConfiguration

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
  name: sidecar-injector
webhooks:
- name: sidecar.demo.io
  namespaceSelector:
    matchLabels:
      sidecar-inject: "true"
...

加上標籤 sidecar-inject: "true",只有滿足這個標籤,才會回調到外部的注入服務,這樣就可以大大減輕注入服務的壓力了

再給namespace打上標籤

kubectl label ns default sidecar-inject=true

default namespace裏面所有的pod,都會回調至外部注入服務

pod 打標籤

這次不在namespace下,而是基於某個pod label

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
  name: sidecar-injector
webhooks:
- name: sidecar.demo.io
  objectSelector:
    matchLabels:
      sidecar-inject-pod: "true"
...

然後再給deployment打標籤,這裏要注意打的是pod的標籤

kubectl patch deployment backend \
  --type='merge' \
  -p '{
    "spec": {
      "template": {
        "metadata": {
          "labels": {
            "sidecar-inject-pod": "true"
          }
        }
      }
    }
  }'

小結

本文詳細描述了怎麼做自動注入:k8s配置修改+外部注入服務。其中需要注入的pod是寫死在注入服務的,這部分可以抽出來,將配置寫成configmap,或者在其他的配置中心中,這樣就不用頻繁的修改注入服務了

另外mutating webhooks可以攔截大部分k8s支持的資源,並且發送到所配置的外部服務中進行需要的配置,這就不單單是pod自動注入,而是資源攔截。比如我需要攔截configmap

echo 'apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
  name: callback-configmap
webhooks:
- name: sidecar.demo.io
  clientConfig:
    service:
      name: sidecar-webhook
      namespace: default
      path: /mutate
    caBundle: '$mytls'
  rules:
  - apiGroups: [""]
    apiVersions: ["v1"]
    operations: ["CREATE", "UPDATE"]
    resources: ["configmaps"]
  admissionReviewVersions: ["v1"]
  sideEffects: None' | kubectl apply -f -

resources變更為configmap之後,就可以直接回調到外部服務。本章由於篇幅有限,就不對這個話題展開了,這個以後有需要再來詳細討論

聯繫我

  • 聯繫我,做深入的交流


至此,本文結束
在下才疏學淺,有撒湯漏水的,請各位不吝賜教...

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.