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