1.深入Istio:Sidecar自动注入如何实现的? (2)

源码位置:pkg/kube/inject/webhook.go

func NewWebhook(p WebhookParameters) (*Webhook, error) { wh := &Webhook{ ... } ... if p.Mux != nil { p.Mux.HandleFunc("/inject", wh.serveInject) mux = p.Mux } else { wh.server = &http.Server{ Addr: fmt.Sprintf(":%v", p.Port), TLSConfig: &tls.Config{GetCertificate: wh.getCert}, } mux = http.NewServeMux() mux.HandleFunc("/inject", wh.serveInject) wh.server.Handler = mux } ... }

在初始化Webhook实例的时候会注册/inject对应的处理器,也就是当apiserver回调/inject请求的时候会调用到serveInject方法中。

然后我们进入到serveInject方法中:

文件位置:pkg/kube/inject/webhook.go

func (wh *Webhook) serveInject(w http.ResponseWriter, r *http.Request) { totalInjections.Increment() var body []byte if r.Body != nil { //读取请求体 if data, err := ioutil.ReadAll(r.Body); err == nil { body = data } } ... var reviewResponse *v1beta1.AdmissionResponse ar := v1beta1.AdmissionReview{} //解码请求体 if _, _, err := deserializer.Decode(body, nil, &ar); err != nil { handleError(fmt.Sprintf("Could not decode body: %v", err)) reviewResponse = toAdmissionResponse(err) } else { //解码成功调用inject方法,并传入AdmissionReview reviewResponse = wh.inject(&ar) } //构建AdmissionReview作为参数返回给调用方 response := v1beta1.AdmissionReview{} if reviewResponse != nil { response.Response = reviewResponse if ar.Request != nil { response.Response.UID = ar.Request.UID } } resp, err := json.Marshal(response) if err != nil { log.Errorf("Could not encode response: %v", err) http.Error(w, fmt.Sprintf("could not encode response: %v", err), http.StatusInternalServerError) } if _, err := w.Write(resp); err != nil { log.Errorf("Could not write response: %v", err) http.Error(w, fmt.Sprintf("could not write response: %v", err), http.StatusInternalServerError) } }

这个方法很简单,主要就是读取请求体并解码,然后调用inject方法,构建AdmissionReview作为参数返回给调用方。

主要逻辑从这里可以看出都在inject方法里面,下面看看这个方法:

func (wh *Webhook) inject(ar *v1beta1.AdmissionReview) *v1beta1.AdmissionResponse { req := ar.Request var pod corev1.Pod //json反序列化请求数据 if err := json.Unmarshal(req.Object.Raw, &pod); err != nil { handleError(fmt.Sprintf("Could not unmarshal raw object: %v %s", err, string(req.Object.Raw))) return toAdmissionResponse(err) } ... //封装模板数据 spec, iStatus, err := InjectionData(wh.Config.Template, wh.valuesConfig, wh.sidecarTemplateVersion, typeMetadata, deployMeta, &pod.Spec, &pod.ObjectMeta, wh.meshConfig.DefaultConfig, wh.meshConfig) // nolint: lll if err != nil { handleError(fmt.Sprintf("Injection data: err=%v spec=%v\n", err, iStatus)) return toAdmissionResponse(err) } ... //将需要注入的有istio-init/istio-proxy container封装成patch操作 //具体可以看这里:https://kubernetes.io/zh/docs/reference/access-authn-authz/extensible-admission-controllers/#response patchBytes, err := createPatch(&pod, injectionStatus(&pod), annotations, spec, deployMeta.Name) if err != nil { handleError(fmt.Sprintf("AdmissionResponse: err=%v spec=%v\n", err, spec)) return toAdmissionResponse(err) } log.Debugf("AdmissionResponse: patch=%v\n", string(patchBytes)) //将需要patch的配置封装成AdmissionResponse返回 reviewResponse := v1beta1.AdmissionResponse{ Allowed: true, Patch: patchBytes, PatchType: func() *v1beta1.PatchType { pt := v1beta1.PatchTypeJSONPatch return &pt }(), } totalSuccessfulInjections.Increment() return &reviewResponse }

inject方法逻辑主要分为以下几个步骤:

json反序列化请求数据到pod中;

调用InjectionData根据模板封装数据,主要是构造istio-init、istio-proxy等容器配置;

调用createPatch方法将模板数据转化成json形式,到时候在创建容器的时候会patch到创建容器的配置中,具体可以看这里:

最后将数据封装成AdmissionResponse返回;

总结

本篇文章重点讲解Sidecar容器注入实现原理,通过使用k8s的准入控制器来做到在每个新建的pod里面都无感知的创建sidecar做流量托管。

Reference

https://github.com/istio/istio.io/blob/release-1.1/content/blog/2019/data-plane-setup/index.md

https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/

https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/

https://jimmysong.io/blog/envoy-sidecar-injection-in-istio-service-mesh-deep-dive/

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpwxwy.html