Sidecar
问题:当创建一个Pod的时候,Istio需要对其进行观察,服务治理、信息采集等,这些操作都是通过Envoy处理的;那Envoy什么时候创建的呢?
在k8s当中一个Pod里面可以有多个Container,如果想要创建一个Sidecar,那最简单的思路便是把原来创建一个Pod的Deployment配置文件修改一下,再创建一个Container即可,这便是Istio注入的操作过程;
在单个pod的deployment创建的时候,我们添加一个拦截功能,“偷偷”修改掉deployment的配置即可完成这样的操作,这就需要用到k8s的 admission controllers;
注入
手工注入
从源码当中可以看到需要的参数信息
## istioctl/cmd/kubeinject.go
istioctl kube-inject -f samples/bookinfo/platform/kube/bookinfo.yaml \
    --injectConfigFile /tmp/inj-template.tmpl \
    --meshConfigFile /tmp/mesh.yaml \
    --valuesFile /tmp/values.json
按 官方的文档 生成inject-config.yaml、mesh-config.yaml、inject-values.yaml,执行即可创建出来一个2个Container的Pod
 ~ kubectl get pod  -l app=sleep
NAME                    READY   STATUS    RESTARTS   AGE
sleep-8f795f47d-96gpd   2/2     Running   0          3h14m
这里面发生了什么呢?这个可以通过命令行工具生成一个deployment.yaml文件
$ istioctl kube-inject \
    --injectConfigFile inject-config.yaml \
    --meshConfigFile mesh-config.yaml \
    --valuesFile inject-values.yaml \
    --filename samples/sleep/sleep.yaml  > sleep_delpoyment.yaml
对比生成的文件和``samples/sleep/sleep.yaml,可以看到对原deployment`进行了以下的改动
- 添加annotations,labels

- 添加istio-proxy容器,添加代理,转发

- 添加istio-init容器:主要作用是创建iptables规则

自动注入
自动注入 在官方文档里面写的比较清楚,主要步骤如下:
- 开启允许istio注入的namespace标签,主要对default ns进行处理
$ kubectl label namespace default istio-injection=enabled
- 创建Pod即可
kubectl apply -f samples/sleep/sleep.yaml
这里面发生了什么事情呢?执行完上面的命令后,会进入istio-pilot的/inject请求当中;在istiod容器内部的日志当中可以看到如下的信息

在istio-sidecar-injector当中早早添加了配置如下,这个配置告诉了k8s-apiserver,会有如下的条件判断
- namespace的标签里面带有- istio-injection
- 当apiserver当中存在CREATE POD时;
- 将此请求转发至istiod serverice /inject接口;
 ~ kubectl get  mutatingwebhookconfiguration istio-sidecar-injector -o yaml
webhooks:
- admissionReviewVersions:
  - v1beta1
  - v1
  clientConfig:
    caBundle: ......
    service:
      name: istiod
      namespace: istio-system
      path: /inject
      port: 443
  failurePolicy: Fail
  matchPolicy: Exact
  name: sidecar-injector.istio.io
  namespaceSelector:
    matchLabels:
      istio-injection: enabled
  objectSelector: {}
  reinvocationPolicy: Never
  rules:
  - apiGroups:
    - ""
    apiVersions:
    - v1
    operations:
    - CREATE
    resources:
    - pods
    scope: '*'
  sideEffects: None
  timeoutSeconds: 30
在启动容器istiod的时候会有一个pilot-discovery进程,这个便是istio-pilot的主进程
$ ps -aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
istio-p+     1  0.2  0.8 852068 132900 ?       Ssl  Jan26  11:26 /usr/local/bin/pilot-discovery discovery --monitoringAddr=:15014 --log_output_level=default:info --domain cluster.local --keepaliveMaxServerConnectionAge 30m
通过源码可以看到在NewWebhook的时候,已经添加了/inject的路由
// NewWebhook creates a new instance of a mutating webhook for automatic sidecar injection.
func NewWebhook(p WebhookParameters) (*Webhook, error) {
   ......
   p.Mux.HandleFunc("/inject", wh.serveInject)
   p.Mux.HandleFunc("/inject/", wh.serveInject)
	.....
}
在serverInject的时候,会去解析请求信息url path、content-type是否为application/json、body是否为空等,若初步的校验没有问题,则会进入webhook.inject逻辑;
//  pkg/kube/inject/webhook.go
func (wh *Webhook) inject(ar *kube.AdmissionReview, path string) *kube.AdmissionResponse {
  .......
	// Deal with potential empty fields, e.g., when the pod is created by a deployment
	podName := potentialPodName(&pod.ObjectMeta)
	if pod.ObjectMeta.Namespace == "" {
		pod.ObjectMeta.Namespace = req.Namespace
	}
  // 这个地方的日志信息便是上图日志打印的出处;
	log.Infof("Sidecar injection request for %v/%v", req.Namespace, podName)
	log.Debugf("Object: %v", string(req.Object.Raw))
	log.Debugf("OldObject: %v", string(req.OldObject.Raw))
	......
  // 获取 pod、deployment、注解等信息,下面就进行注入逻辑
	patchBytes, err := injectPod(params)
	......
	return &reviewResponse
}
注入逻辑也是比较清楚,即解析oldPod,再将pod配置进行组装,包括面提到的几个操作,修改spec/label、spec/annotations,添加istio-proxy、istio-init容器,组装出来新的Pod配置信息后,放入http response(AdmissionResponse)当中返回给k8s-apiserver,并对注入成功的的Pod累加计数;
	reviewResponse := kube.AdmissionResponse{
		Allowed: true,
		Patch:   patchBytes,
		PatchType: func() *string {
			pt := "JSONPatch"
			return &pt
		}(),
	}
	totalSuccessfulInjections.Increment()
k8s-apisever拿到response后根据对应的配置信息创建Pod。
整体的流程图如下:

- 首先注入MutatingWebhookConfiguration,配置k8s-apiserver创建pod时的转发规则
- 执行创建命令,请求至k8s-apisever
- k8s-apiserver转发- injecdt至- pilot webhook,对- pod配置进行重组
- 返回重组后的AdmissionResponse给k8s-apiserver
- k8s-apisever根据- Response当中的配置创建- sleep、- istio-init容器;