istio-sidecar自动注入原理

发布时间 2024-01-03 18:35:50作者: 爬上巨人的肩膀

0.原理

istio通过添加MutatingWebhook识别打了自动注入标签的Pod-create请求,请求pilot暴露的sidecar注入接口,pilot根据template生成sidecar并修改patch到pod元数据中返回完成sidecar注入。

1.指定namespace打注入标签

kubectl label namespace {namespace} istio-injection=enabled --overwrite

2.注入分析

暴露webhook:/inject

xiaohan@xiaohandeMacBook-Pro istio-1.17.2 %  -oyaml
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
  labels:
    app: sidecar-injector
    install.operator.istio.io/owning-resource: unknown
    install.operator.istio.io/owning-resource-namespace: istio-system
    istio.io/rev: default
    operator.istio.io/component: Pilot
    operator.istio.io/managed: Reconcile
    operator.istio.io/version: 1.17.2
    release: istio
  name: istio-sidecar-injector
webhooks:
- admissionReviewVersions:
  - v1beta1
  - v1
  clientConfig:
    caBundle: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMvVENDQWVXZ0F3SUJBZ0lSQU11cWc3d1psVGJ2elNXUW94VzhlNk13RFFZSktvWklodmNOQVFFTEJRQXcKR0RFV01CUUdBMVVFQ2hNTlkyeDFjM1JsY2k1c2IyTmhiREFlRncweU16QTBNVFF3TmpRM016aGFGdzB6TXpBMApNVEV3TmpRM016aGFNQmd4RmpBVUJnTlZCQW9URFdOc2RYTjBaWEl1Ykc5allXd3dnZ0VpTUEwR0NTcUdTSWIzCkRRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRQ2lrc2lQNzlQOWFxcnowQ3d1bFNIQUxlNnVLMFdWdmdPL1pXbDQKRUtWak5oVEkwUnN6TWtSRjBGa3JxVjE2WlhJZVNKQXRROFVOTnpDMW1INWRjU2cyTTR1RTlJTHN4NUh6eTV2MwpteXl5eDIveTlRb2hFdFRGamJ5dmtyMkRoWWtSb040d2tudWtzTHhhd21vbDVDUFR0NXJpQmtxVFJHQXZkbXh5CkIrRmVSZHcyeGtrQzRINTkxSjBrSXlKNzEvaFVUeWtteXR0aEluR1hTM2NQY25UN0I5cFp5Wk85dWUrOGhLOTEKOStLVVRaMFJIdGVUWGJzdmd4QTNmRWdYdDduOEVWUHRFZ2RoazM4MHlkZ0lLSEluNWlicDkyK2FHcEFOdmgwYwpDbldzbWEvTnh6b0EyTnlXajZBTk9CcHZYcTJhWlBrK2hWWW5wTWJGcVZUK3lMWkRBZ01CQUFHalFqQkFNQTRHCkExVWREd0VCL3dRRUF3SUNCREFQQmdOVkhSTUJBZjhFQlRBREFRSC9NQjBHQTFVZERnUVdCQlRZakpLSngvb2kKelNmREZaVDVJMHFaMGZCT2NqQU5CZ2txaGtpRzl3MEJBUXNGQUFPQ0FRRUFpc3hJRk9YWCtkRGhSMzhMYmdsMApNQ1doTlo3bFNldHlkdDZUZy9XS1ljVGdkNUxkZzZMQmM3emF6Q2lqL0lBTUYxcjR4Ni90UE5pWUVac2ozank5CjN3N1ZGeGt3SGYyY2ovR1BjMGR2SFczYUVRM216b3UyMmxBNmVIdUxDZFNkaVYyNjJoc3krYXdpdi9xaTAvVEoKL1NDUXQxcDFSNnlRbm0yalhObHljMllwYk5OWE5WK1I5SE0wVmpZc05Yd3hYdm1xM0tkMVdEN0tOaXNGSTdMRApXRkFzeW9WaitxSlRlbXVGTzR1WUN5TzQvMWx3dEdSQ1VjOFRqb1NzSjBMdHFMOU13NHVRV1FxMU4zM0Rxc29kCmdXNTVKbHlZOWVPaWMzMFZZOU5NLzRXa1o4TnZ5Z3lrcTZ0R1R6R2oyeXpBamUxb3pSQytqYnlWMjlPUkdVbkMKNXc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
    service:
      name: istiod
      namespace: istio-system
      path: /inject
      port: 443
  failurePolicy: Fail
  matchPolicy: Equivalent
  name: namespace.sidecar-injector.istio.io
  namespaceSelector:
    matchLabels:
      istio.io/deactivated: never-match
  objectSelector:
    matchLabels:
      istio.io/deactivated: never-match
  reinvocationPolicy: Never
  rules:
  - apiGroups:
    - ""
    apiVersions:
    - v1
    operations:
    - CREATE
    resources:
    - pods
    scope: '*'

/inject server 转发istio-pilot-15017

xiaohan@xiaohandeMacBook-Pro istio-1.17.2 % kubectl get svc -n istio-system istiod -oyaml
apiVersion: v1
kind: Service
metadata:
  annotations:
  labels:
    app: istiod
    install.operator.istio.io/owning-resource: unknown
    install.operator.istio.io/owning-resource-namespace: istio-system
    istio: pilot
    istio.io/rev: default
    operator.istio.io/component: Pilot
    operator.istio.io/managed: Reconcile
    operator.istio.io/version: 1.17.2
    release: istio
  name: istiod
  namespace: istio-system
spec:
  clusterIP: 10.96.7.238
  clusterIPs:
  - 10.96.7.238
  internalTrafficPolicy: Cluster
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - name: grpc-xds
    port: 15010
    protocol: TCP
    targetPort: 15010
  - name: https-dns
    port: 15012
    protocol: TCP
    targetPort: 15012
  - name: https-webhook
    port: 443
    protocol: TCP
    targetPort: 15017
  - name: http-monitoring
    port: 15014
    protocol: TCP
    targetPort: 15014
  selector:
    app: istiod
    istio: pilot
  sessionAffinity: None
  type: ClusterIP
xiaohan@xiaohandeMacBook-Pro istio-1.17.2 % kubectl get po -n istio-system istiod-dbf5ff64-mqgrk -oyaml
apiVersion: v1
kind: Pod
spec:
  containers:
  - args:
    - discovery
    - --monitoringAddr=:15014
    - --log_output_level=default:info
    - --domain
    - cluster.local
    - --keepaliveMaxServerConnectionAge
    - 30m
    image: docker.io/istio/pilot:1.17.2
    imagePullPolicy: IfNotPresent
    name: discovery
    ports:
    - containerPort: 8080
      protocol: TCP
    - containerPort: 15010
      protocol: TCP
    - containerPort: 15017
      protocol: TCP

pkg/kube/inject/webhook.go:187 pilot暴露/inject http server

func NewWebhook(p WebhookParameters) (*Webhook, error) {
	if p.Mux == nil {
		return nil, errors.New("expected mux to be passed, but was not passed")
	}

	wh := &Webhook{
		watcher:    p.Watcher,
		meshConfig: p.Env.Mesh(),
		env:        p.Env,
		revision:   p.Revision,
	}

	mc := NewMulticast(p.Watcher, wh.GetConfig)
	mc.AddHandler(wh.updateConfig)
	wh.MultiCast = mc
	sidecarConfig, valuesConfig, err := p.Watcher.Get()
	if err != nil {
		return nil, err
	}
	if err := wh.updateConfig(sidecarConfig, valuesConfig); err != nil {
		log.Errorf("failed to process webhook config: %v", err)
	}

	p.Mux.HandleFunc("/inject", wh.serveInject)
	p.Mux.HandleFunc("/inject/", wh.serveInject)

	p.Env.Watcher.AddMeshHandler(func() {
		wh.mu.Lock()
		wh.meshConfig = p.Env.Mesh()
		wh.mu.Unlock()
	})

	return wh, nil
}

pkg/kube/inject/webhook.go:436 inject sidecar

func injectPod(req InjectionParameters) ([]byte, error) {
	checkPreconditions(req)

	// The patch will be built relative to the initial pod, capture its current state
	originalPodSpec, err := json.Marshal(req.pod)
	if err != nil {
		return nil, err
	}

	// Run the injection template, giving us a partial pod spec
	mergedPod, injectedPodData, err := RunTemplate(req)
	if err != nil {
		return nil, fmt.Errorf("failed to run injection template: %v", err)
	}

	mergedPod, err = reapplyOverwrittenContainers(mergedPod, req.pod, injectedPodData)
	if err != nil {
		return nil, fmt.Errorf("failed to re apply container: %v", err)
	}

	// Apply some additional transformations to the pod
	if err := postProcessPod(mergedPod, *injectedPodData, req); err != nil {
		return nil, fmt.Errorf("failed to process pod: %v", err)
	}

	patch, err := createPatch(mergedPod, originalPodSpec)
	if err != nil {
		return nil, fmt.Errorf("failed to create patch: %v", err)
	}

	log.Debugf("AdmissionResponse: patch=%v\n", string(patch))
	return patch, nil
}