Istio Sidecar 注入:例外和除错

原文:Istio sidecar injection: enabling automatic injection, adding exceptions and debugging

作者:Jonh Wendell

Kubernetes 环境下的 Istio 使用了 Sidecar 模型进行部署,把一个辅助容器(也就是 Sidecar)附加到业务 Pod 之中。这一过程让 Sidecar 容器和业务容器共享同样的网络栈,可以视为同一主机上的两个进程。这样一来,Istio 就能够接管业务应用的所有网络调用,就有了增强服务间通信能力的基础。

这个 Sidecar 容器命名为 istio-proxy,能够用手工或者自动方式进行注入。其实这个手工注入也不是 100% 徒手完成的。

手工注入

Istio 的发行版本中会带有一个 istioctl 工具。看名字就知道这工具很棒:)。它的一个能力就是把 istio-proxy Sidecar 注入到业务容器之中。我们可以用一个简单的 busybox Pod 来看看它的功能:

$ cat busybox.yaml

apiVersion: v1
kind: Pod
metadata:
  name: busybox-test
spec:
  containers:
  - name: busybox-container
    image: busybox
    command: ['sh', '-c', 'echo Hello Kubernetes! && sleep infinity']

$ istioctl kube-inject -f busybox.yaml > busybox-injected.yaml
$ cat busybox-injected.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
  name: busybox-test
spec:
  containers:
  - command:
    - sh
    - -c
    - echo Hello Kubernetes! && sleep infinity
    image: busybox
    name: busybox-container

  - image: docker.io/istio/proxyv2:1.0.2
    imagePullPolicy: IfNotPresent
    name: istio-proxy
    args:
      - proxy
      - sidecar
   ...

上文可见,这个命令生成了一个 YAML 文件,和原始文件有点像,但是 Pod 中加入了一个 Sidecar(istio-proxy)。这的确不是纯手工吧——至少让我们少打了不少字。然后就可以使用 kubectl apply 命令把这个修改后的文件提交给 Kubernetes 集群了:

$ kubectl apply -f busybox-injected.yaml
# 如果不想生成中间文件,可以直接用管道完成全部操作。
$ kubectl apply -f <(istioctl kube-inject -f busybox.yaml)

随之而来的一个问题就是:这些数据是哪来的?它怎么知道 Sidecar 镜像是 docker.io/istio/proxyv2:1.0.2 的?答案很简单:所有数据都来自于一个 ConfigMap,这个对象保存在 istio-system 命名空间:

$ kubectl -n istio-system describe configmap istio-sidecar-injector
Name:         istio-sidecar-injector
Namespace:    istio-system
 
Data
====
config:
----
policy: enabled
template: |-
  initContainers:
  - name: istio-init
    image: "docker.io/istio/proxy_init:1.0.2"
...

可以对这个 ConfigMap 进行编辑,修改一些你需要的值,用来进行注入。不难发现,这主要是一个用于向 Pod 加入内容的模板。如果想要为 istio-proxy 容器使用其它对象,只要修改这个字段的内容就可以了,或者还可以调整任何其它要注入的东西。这个 ConfigMap 是用来完成网格中所有 Pod 的注入工作的,所以要小心从事。

因为 istioctl 要根据 ConfigMap 来获知注入内容,也就是说执行 istioctl 的用户必须能够访问到安装了 Istio 的 Kubernetes 集群的这一对象。如果因为某些原因无法访问到这一 ConfigMap,还可以在 istioctl 中使用一个本地的配置文件。

# 首先使用有权限的用户运行这一命令
$ kubectl -n istio-system get configmap istio-sidecar-injector -o=jsonpath='{.data.config}' > inject-config.yaml
# 这个文件可以随时进行任意修改。
$ istioctl kube-inject --injectConfigFile inject-config.yaml ...

自动注入

注入 istio-proxy 还有另一个方式,就是要求 Istio 进行自动注入。为命名空间设置标签 istio-injection=enabled 就能满足这一需要了。如果命名空间加上了这个标签,所有其中的 Pod 都会被注入 istio-proxy Sidecar,也就无需手工执行 istioctl 处理 YAML 文件了。

工作方式很简单:它使用了 Kubernetes 的 MutatingWebhook,新 Pod 创建之前,这一功能会通知 Istio,让 Istio 有机会对新 Pod 进行就地修改,stio 会使用在 ConfigMap 中的模板把 istio-proxy 注入到 Pod 中。

看起来不错?

自动注入过程有很大的弹性:

  • istio-sidecar-injector ConfigMap 中有一个布尔值用来指定自动注入是否启用。
  • 只有使用标签进行标注的命名空间才会进行自动注入,也就是说用命名空间标签能够对自动注入进行控制。
  • 还可以使用 kubectl -n istio-system edit MutatingWebhookConfiguration istio-sidecar-injector 命令,修改其中的 namespaceSelector 字段来调整这个标签的用法,甚至移除这个限制(也就是为所有命名空间使用自动注入,慎用)。
  • 可以禁用特定 Pod 的自动注入。如果 Pod 包含注解 sidecar.istio.io/inject: "false",Istio 就不会为在这一 Pod 中注入 Sidecar。
  • 还有更保守的方式:只对选择的 Pod 进行注入——也就是白名单方式。设置策略为 false(kubectl -n istio-system edit configmap istio-sidecar-injector),只对包含注解 sidecar.istio.io/inject: "true" 进行注入。

想要更大弹性?

还有些上面提到的方法无法满足的弹性要求:

如果不熟悉 Openshift 的用户,它有一个功能叫做 source-to-image(代码到镜像,s2i),这一功能会将代码构建为容器镜像。提供一个 git 仓库作为输入(支持多种语言),就会输出镜像并运行到 Openshift 集群上。

这是一个神奇的功能。这里不会介绍很多细节,我只会告诉你本文中需要了解的事情:在这一过程中 Openshift 会创建一或更多个的用于进行构建的中间、辅助 Pod。构建过程结束之后,二进制工件被构建为容器镜像,而辅助 Pod 会被销毁。

如果我们为指定命名空间启用了自动注入,并使用 Openshift 的 s2i 功能,会让这些辅助 Pod 也被注入 Sidecar,这些 Pod 是 Openshift 创建的,我们无法对其进行注解以阻止 Sidecar 的注入。辅助 Pod 无需进行注入,怎么办呢?

一个可能的解决方法就是用上面提到的保守方案,只对特定 Pod 启用注入,但是就要对 Pod 进行特别的注解。还有别的方式。

新方案

1.1.0 中,Istio 自动注入可以根据标签进行例外设置:不管命名空间标签如何,策略如何设置,对符合标签选择器要求的 Pod 都不进行注入。可以在 istio-sidecar-injector 中加入例外设置。

$ kubectl -n istio-system describe configmap istio-sidecar-injector
apiVersion: v1
kind: ConfigMap
metadata:
  name: istio-sidecar-injector
data:
  config: |-
    policy: enabled
    neverInjectSelector:
      - matchExpressions:
        - {key: openshift.io/build.name, operator: Exists}
      - matchExpressions:
        - {key: openshift.io/deployer-pod-for.name, operator: Exists}
    template: |-
      initContainers:
...

可以看到上面的 neverInjectSelector 字段,它是一个 Kubernetes 标签选择器的数组。不同元素之间是“或”关系,第一次发现有符合条件的标签之后就会跳过其它判断。上面的语句意味着:包含 openshift.io/build.name 或者 openshift.io/deployer-pod-for.name 标签的 Pod,不管标签值如何,都不会进行注入。

为了完整性起见,可以使用 alwaysInjectSelector 字段,这个字段会无视全局策略,向符合条件的 Pod 进行注入。

标签选择器方式产生了很大的弹性,能够处理更多的例外情况。阅读 Kubernetes 文档可以了解更多这方面的内容。

值得注意的是,Pod 注解还是有更高的优先级,如果 Pod 注解包含了 sidecar.istio.io/inject: "true/false",会被优先处理,所以自动注入的评估顺序是:

Pod 注解 → NeverInjectSelector → AlwaysInjectSelector → 命名空间策略。

注入选择器是新特性,这方面的文档还在更新,但是其它部分的文档和例子,都可以在官方文档中查看。

Pod 为什么没注入?

这是个常见问题。按照前面的介绍(例如给命名空间打标签)进行操作,结果 Pod 还没有被注入。

或者刚好相反,Pod 明明注解为 sidecar.istio.io/inject: "false,还是被注入了,为什么?

可以看看 sidecar-injector Pod 的日志:

$ pod=$(kubectl -n istio-system get pods -l istio=sidecar-injector -o jsonpath='{.items[0].metadata.name}')
$ kubectl -n istio-system logs -f $pod

然后可以创建业务 Pod,看看日志输出的具体内容。要看到更详细的日志(经常会很有用),可以编辑 sidecar-injector Deployment 对象,给它加上参数 --log_output_level=default:debug

$ kubectl -n istio-system edit deployment istio-sidecar-injector
...
      containers:
      - args:
        - --caCertFile=/etc/istio/certs/root-cert.pem
        - --tlsCertFile=/etc/istio/certs/cert-chain.pem
        - --tlsKeyFile=/etc/istio/certs/key.pem
        - --injectConfig=/etc/istio/inject/config
        - --meshConfig=/etc/istio/config/mesh
        - --healthCheckInterval=2s
        - --healthCheckFile=/health
        - --log_output_level=default:debug
        image: docker.io/istio/sidecar_injector:1.0.2
        imagePullPolicy: IfNotPresent
...

编辑成功之后 Pod 会重启,完成之后就可以重新查看日志了:

$ pod=$(kubectl -n istio-system get pods -l istio=sidecar-injector -o jsonpath='{.items[0].metadata.name}')
$ kubectl -n istio-system logs -f $pod

提示

如果在日志中还是找不到问题原因,就代表 sidecar-injector Pod 没有收到 Pod 创建的通知,也就不会触发自动注入的操作。这可能是因为命名空间没有正确标签导致的,因此需要检查一下命名空间的标签以及 MutatingWebhookConfiguration 中的配置。缺省情况下,命名空间应该设置 istio-injection=enabled 标签。可以使用 kubectl -n istio-system edit MutatingWebhookConfiguration istio-sidecar-injector 命令检查其中的 namespaceSelector 字段内容。

完成排查之后,可以再次编辑 sidecar-injector Deployment 对象,清除新加入的参数。

相关

comments powered by Disqus