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容器;