• Sidecar Injector
    • Dynamic Admission Control
    • Sidecar 注入内容分析

    Sidecar Injector

    用户空间的 Pod 要想加入 mesh, 首先需要注入 sidecar 容器, istio 提供了 2 种方式实现注入:

    • 自动注入: 利用 Kubernetes Dynamic Admission Webhooks 对 新建的 pod 进行注入: initContainer + sidecar
    • 手动注入: 使用命令istioctl kube-inject

    「注入」本质上就是修改 Pod 的资源定义, 添加相应的 sidecar 容器定义, 内容包括 2 个新容器:

    • 名为istio-init的 initContainer: 通过配置 iptables 来劫持 Pod 中的流量
    • 名为istio-proxy的 sidecar 容器: 两个进程 pilot-agent 和 envoy, pilot-agent 进行初始化并启动 envoy

    Sidecar Injector - 图1

    Dynamic Admission Control

    kubernetes 的准入控制(Admission Control)有 2 种:

    • Built in Admission Control: 这些 Admission 模块可以选择性地编译进 api server, 因此需要修改和重启 kube-apiserver
    • Dynamic Admission Control: 可以部署在 kube-apiserver 之外, 同时无需修改或重启 kube-apiserver。

    其中, Dynamic Admission Control 包含 2 种形式:

    • Admission Webhooks: 该 controller 提供 http server, 被动接受 kube-apiserver 分发的准入请求。
    • Initializers: 该 controller 主动 list and watch 关注的资源对象, 对 watch 到的未初始化对象进行相应的改造。

      其中, Admission Webhooks 又包含 2 种准入控制:

    • ValidatingAdmissionWebhook

    • MutatingAdmissionWebhook

    istio 使用了 MutatingAdmissionWebhook 来实现对用户 Pod 的注入, 首先需要保证以下条件满足:

    • 确保 kube-apiserver 启动参数 开启了 MutatingAdmissionWebhook
    • 给 namespace 增加 label: kubectl label namespace default istio-injection=enabled
    • 同时还要保证 kube-apiserver 的 aggregator layer 开启: --enable-aggregator-routing=true 且证书和 api server 连通性正确设置。

    另外还需要一个配置对象, 来告诉 kube-apiserver istio 关心的资源对象类型, 以及 webhook 的服务地址。 如果使用 helm 安装 istio, 配置对象已经添加好了, 查阅 MutatingWebhookConfiguration:

    1. % kubectl get mutatingWebhookConfiguration -oyaml
    2. - apiVersion: admissionregistration.k8s.io/v1beta1
    3. kind: MutatingWebhookConfiguration
    4. metadata:
    5. name: istio-sidecar-injector
    6. webhooks:
    7. - clientConfig:
    8. service:
    9. name: istio-sidecar-injector
    10. namespace: istio-system
    11. path: /inject
    12. name: sidecar-injector.istio.io
    13. namespaceSelector:
    14. matchLabels:
    15. istio-injection: enabled
    16. rules:
    17. - apiGroups:
    18. - ""
    19. apiVersions:
    20. - v1
    21. operations:
    22. - CREATE
    23. resources:
    24. - pods

    该配置告诉 kube-apiserver: 命名空间 istio-system 中的服务 istio-sidecar-injector(默认 443 端口), 通过路由/inject, 处理v1/pods的 CREATE, 同时 pod 需要满足命名空间istio-injection: enabled, 当有符合条件的 pod 被创建时, kube-apiserver 就会对该服务发起调用, 服务返回的内容正是添加了 sidecar 注入的 pod 定义。

    Sidecar 注入内容分析

    查看 Pod istio-sidecar-injector的 yaml 定义:

    1. % kubectl -n istio-system get pod istio-sidecar-injector-5f7894f54f-w7f9v -oyaml
    2. ......
    3. volumeMounts:
    4. - mountPath: /etc/istio/inject
    5. name: inject-config
    6. readOnly: true
    7. volumes:
    8. - configMap:
    9. items:
    10. - key: config
    11. path: config
    12. name: istio-sidecar-injector
    13. name: inject-config

    可以看到该 Pod 利用projected volume将istio-sidecar-injector这个 config map 的 config 挂到了自己容器路径/etc/istio/inject/config, 该 config map 内容正是注入用户空间 pod 所需的模板。

    如果使用 helm 安装 istio, 该 configMap 模板源码位于: https://github.com/istio/istio/blob/master/install/kubernetes/helm/istio/templates/sidecar-injector-configmap.yaml。

    该 config map 是在安装 istio 时添加的, kubernetes 会自动维护 projected volume 的更新, 因此 容器 sidecar-injector只需要从本地文件直接读取所需配置。

    高级用户可以按需修改这个模板内容。

    1. kubectl -n istio-system get configmap istio-sidecar-injector -o=jsonpath='{.data.config}'

    查看该 configMap, data.config包含以下内容(简化):

    1. policy: enabled # 是否开启自动注入
    2. template: |- # 使用go template 定义的pod patch
    3. initContainers:
    4. [[ if ne (annotation .ObjectMeta `sidecar.istio.io/interceptionMode` .ProxyConfig.InterceptionMode) "NONE" ]]
    5. - name: istio-init
    6. image: "docker.io/istio/proxy_init:1.1.0"
    7. ......
    8. securityContext:
    9. capabilities:
    10. add:
    11. - NET_ADMIN
    12. ......
    13. containers:
    14. - name: istio-proxy
    15. args:
    16. - proxy
    17. - sidecar
    18. ......
    19. image: [[ annotation .ObjectMeta `sidecar.istio.io/proxyImage` "docker.io/istio/proxyv2:1.1.0" ]]
    20. ......
    21. readinessProbe:
    22. httpGet:
    23. path: /healthz/ready
    24. port: [[ annotation .ObjectMeta `status.sidecar.istio.io/port` 0 ]]
    25. ......
    26. securityContext:
    27. capabilities:
    28. add:
    29. - NET_ADMIN
    30. runAsGroup: 1337
    31. ......
    32. volumeMounts:
    33. ......
    34. - mountPath: /etc/istio/proxy
    35. name: istio-envoy
    36. - mountPath: /etc/certs/
    37. name: istio-certs
    38. readOnly: true
    39. ......
    40. volumes:
    41. ......
    42. - emptyDir:
    43. medium: Memory
    44. name: istio-envoy
    45. - name: istio-certs
    46. secret:
    47. optional: true
    48. [[ if eq .Spec.ServiceAccountName "" -]]
    49. secretName: istio.default
    50. [[ else -]]
    51. secretName: [[ printf "istio.%s" .Spec.ServiceAccountName ]]
    52. ......

    对 istio-init 生成的部分参数分析:

    • -u 1337 排除用户 ID 为 1337,即 Envoy 自身的流量
    • 解析用户容器.Spec.Containers, 获得容器的端口列表, 传入-b参数(入站端口控制)
    • 指定要从重定向到 Envoy 中排除(可选)的入站端口列表, 默认写入-d 15020, 此端口是 sidecar 的 status server
    • 赋予该容器NET_ADMIN 能力, 允许容器 istio-init 进行网络管理操作

    对 istio-proxy 生成的部分参数分析:

    • 启动参数proxy sidecar xxx 用以定义该节点的代理类型(NodeType)
    • 默认的 status server 端口--statusPort=15020
    • 解析用户容器.Spec.Containers, 获取用户容器的 application Ports, 然后设置到 sidecar 的启动参数--applicationPorts中, 该参数会最终传递给 envoy, 用以确定哪些端口流量属于该业务容器。
    • 设置/healthz/ready 作为该代理的 readinessProbe
    • 同样赋予该容器NET_ADMIN能力

    另外istio-sidecar-injector还给容器istio-proxy挂了 2 个 volumes:

    • 名为istio-envoy的 emptydir volume, 挂载到容器目录/etc/istio/proxy, 作为 envoy 的配置文件目录

    • 名为istio-certs的 secret volume, 默认 secret 名为istio.default, 挂载到容器目录/etc/certs/, 存放相关的证书, 包括服务端证书, 和可能的 mtls 客户端证书

      1. % kubectl exec productpage-v1-6597cb5df9-xlndw -c istio-proxy -- ls /etc/certs/
      2. cert-chain.pem
      3. key.pem
      4. root-cert.pem