安装 Sidecar

注入

为了充分利用 Istio 的所有特性,网格中的 Pod 必须运行一个 Istio Sidecar 代理。

下面的章节描述了向 Pod 中注入 Istio Sidecar 的两种方法:使用 istioctl 手动注入或启用 Pod 所属命名空间的 Istio Sidecar 注入器自动注入。

当 Pod 所属命名空间启用自动注入后,自动注入器会使用准入控制器在创建 Pod 时自动注入代理配置。

手动注入直接修改配置,如 Deployment,并将代理配置注入其中。

如果您不确定使用哪一种方法,建议使用自动注入。

自动注入 Sidecar

使用 Istio 提供的准入控制器变更 Webhook, 可以将 Sidecar 自动添加到可用的 Kubernetes Pod 中。

虽然准入控制器默认情况下是启用的,但一些 Kubernetes 发行版会禁用这些控制器。 如果出现这种情况,根据指示说明来启用准入控制器

当您在一个命名空间中设置了 istio-injection=enabled 标签,且 Injection Webhook 被启用后,任何新的 Pod 都有将在创建时自动添加 Sidecar。

请注意,区别于手动注入,自动注入发生在 Pod 层面。您将看不到 Deployment 本身有任何更改。 取而代之,需要检查单独的 Pod(使用 kubectl describe)来查询被注入的代理。

部署应用

部署 sleep 应用,验证 Deployment 和 Pod 只有一个容器。

1
2
3
4
$ kubectl apply -f samples/sleep/sleep.yaml
$ kubectl get deployment -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
sleep 1/1 1 1 12s sleep curlimages/curl app=sleep
1
2
3
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
sleep-8f795f47d-hdcgs 1/1 Running 0 42s

default 命名空间标记为 istio-injection=enabled

1
2
3
4
5
$ kubectl label namespace default istio-injection=enabled --overwrite
$ kubectl get namespace -L istio-injection
NAME STATUS AGE ISTIO-INJECTION
default Active 5m9s enabled
...

注入发生在 Pod 创建时。杀死正在运行的 Pod 并验证新创建的 Pod 是否注入 Sidecar。 原来的 Pod 具有 1/1 READY 个容器,注入 Sidecar 后的 Pod 则具有 READY 为 2/2 READY 个容器。

1
2
3
4
5
6
$ kubectl delete pod -l app=sleep
$ kubectl get pod -l app=sleep
pod "sleep-776b7bcdcd-7hpnk" deleted
NAME READY STATUS RESTARTS AGE
sleep-776b7bcdcd-7hpnk 1/1 Terminating 0 1m
sleep-776b7bcdcd-bhn9m 2/2 Running 0 7s

查看已注入 Pod 的详细状态。您应该看到被注入的 istio-proxy 容器和对应的卷。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ kubectl describe pod -l app=sleep
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
...
Normal Created 11s kubelet Created container istio-init
Normal Started 11s kubelet Started container istio-init
...
Normal Created 10s kubelet Created container sleep
Normal Started 10s kubelet Started container sleep
...
Normal Created 9s kubelet Created container istio-proxy
Normal Started 8s kubelet Started container istio-proxy

禁用 default 命名空间的注入,并确认新的 Pod 在创建时没有 Sidecar。

1
2
3
4
5
6
7
8
$ kubectl label namespace default istio-injection-
$ kubectl delete pod -l app=sleep
$ kubectl get pod
namespace/default labeled
pod "sleep-776b7bcdcd-bhn9m" deleted
NAME READY STATUS RESTARTS AGE
sleep-776b7bcdcd-bhn9m 2/2 Terminating 0 2m
sleep-776b7bcdcd-gmvnr 1/1 Running 0 2s

控制注入策略

在上面例子中,您在命名空间层级启用和禁用了注入。 注入也可以通过配置 Pod 上的 sidecar.istio.io/inject 标签,在每个 Pod 的基础上进行控制。

资源标签启用的值禁用的值
Namespaceistio-injectionenableddisabled
Podsidecar.istio.io/inject"true""false"

如果您正在使用控制平面修订版,将通过匹配 istio.io/rev 标签来转为使用特定修订版的标签。例如,对于名为 canary 的修订版:

资源启用的标签禁用的标签
Namespaceistio.io/rev=canaryistio-injection=disabled
Podistio.io/rev=canarysidecar.istio.io/inject="false"

如果 istio-injection 标签和 istio.io/rev 标签在同一个命名空间中,则优先使用 istio-injection 标签。

按照以下逻辑配置注入器:

  1. 如果禁用其中任何一个标签(istio-injectionsidecar.istio.io/inject), 则不会注入 Pod。
  2. 如果启用其中任何一个标签(istio-injectionsidecar.istio.io/injectistio.io/rev), 则会注入 Pod。
  3. 如果两个标签都没有设置,且启用了 .values.sidecarInjectorWebhook.enableNamespacesByDefault, 则会注入 Pod。这在默认情况下是不启用的,所以 Pod 通常不会被注入。

手动注入 Sidecar

要手动注入 Deployment,请使用 istioctl kube-inject

1
2
3
4
$ istioctl kube-inject -f samples/sleep/sleep.yaml | kubectl apply -f -
serviceaccount/sleep created
service/sleep created
deployment.apps/sleep created

默认情况下将使用集群内的配置,或者使用该配置的本地副本来完成注入。

1
2
3
$ kubectl -n istio-system get configmap istio-sidecar-injector -o=jsonpath='{.data.config}' > inject-config.yaml
$ kubectl -n istio-system get configmap istio-sidecar-injector -o=jsonpath='{.data.values}' > inject-values.yaml
$ kubectl -n istio-system get configmap istio -o=jsonpath='{.data.mesh}' > mesh-config.yaml

指定输入文件,运行 kube-inject 并部署。

1
2
3
4
5
6
7
8
9
$ istioctl kube-inject \
--injectConfigFile inject-config.yaml \
--meshConfigFile mesh-config.yaml \
--valuesFile inject-values.yaml \
--filename samples/sleep/sleep.yaml \
| kubectl apply -f -
serviceaccount/sleep created
service/sleep created
deployment.apps/sleep created

验证 Sidecar 是否已经被注入到 READY 列下 2/2 的 Sleep Pod 中。

1
2
3
$ kubectl get pod  -l app=sleep
NAME READY STATUS RESTARTS AGE
sleep-64c6f57bc8-f5n4x 2/2 Running 0 24s

自定义注入

通常,Pod 的注入是基于 Sidecar 注入模板,在 istio-sidecar-injector Configmap 中配置。每个 Pod 的配置可用于覆盖各个 Pod 上的选项。可通过在 Pod 中添加一个 istio-proxy 容器来完成。Sidecar 注入将会把自定义的任何配置视为默认注入模板的覆盖。

自定义这些设置时,需格外小心,因为允许完全自定义生成的 Pod,包括进行一些更改而导致 Sidecar 容器无法正常运行。

例如,以下配置可自定义各种设置,包括降低 CPU 请求,添加 Volume 挂载,和添加 preStop Hook:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: v1
kind: Pod
metadata:
name: example
spec:
containers:
- name: hello
image: alpine
- name: istio-proxy
image: auto
resources:
requests:
cpu: "100m"
volumeMounts:
- mountPath: /etc/certs
name: certs
lifecycle:
preStop:
exec:
command: ["sleep", "10"]
volumes:
- name: certs
secret:
secretName: istio-certs

通常,可以设置 Pod 中的任何字段。但是必须注意某些字段:

  • Kubernetes 要求在注入运行之前配置 image。虽然可以您可以设置一个指定的 Image 来覆盖默认的 image 配置,但建议将 image 设置为 auto,可使 Sidecar 注入自动选择要使用的 Image。
  • Pod 中一些字段取决于相关设置。例如,CPU 请求必须小于 CPU 限制。 如果两个字段没有一起配置,Pod 可能会无法启动。

另外,某些字段可通过在 Pod 上的注解进行配置, 但是不建议使用上述方法进行自定义设置。必须特别注意某些注解:

  • 如果设置了 sidecar.istio.io/proxyCPU,则务必显式设置 sidecar.istio.io/proxyCPULimit。 否则该 Sidecar 的 cpu 限制将被设置为 unlimited。
  • 如果设置了 sidecar.istio.io/proxyMemory,则务必显式设置 sidecar.istio.io/proxyMemoryLimit。 否则该 Sidecar 的 memory 限制将被设置为 unlimited。

例如,参见以下不完整的资源注解配置和相应注入的资源设置:

1
2
3
4
5
6
spec:
template:
metadata:
annotations:
sidecar.istio.io/proxyCPU: "200m"
sidecar.istio.io/proxyMemoryLimit: "5Gi"
1
2
3
4
5
6
7
8
9
10
11
spec:
containers:
- name: istio-proxy
resources:
limits:
memory: 5Gi
requests:
cpu: 200m
memory: 5Gi
securityContext:
allowPrivilegeEscalation: false

自定义模板(试验特性)

此功能为试验特性功能,可随时更改或删除。

可以在安装时定义一个完全自定义的模板。 例如,定义一个自定义模板,将 GREETING 环境变量注入到 istio-proxy 容器中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
name: istio
spec:
values:
sidecarInjectorWebhook:
templates:
custom: |
spec:
containers:
- name: istio-proxy
env:
- name: GREETING
value: hello-world

默认情况下,istio 会使用自动创建的 sidecar 模板对 Pod 进行注入。 这可通过 inject.istio.io/templates 注解来替换。 例如要应用默认模板和自定义模板,您可以设置 inject.istio.io/templates=sidecar,custom

除了 sidecar 之外,默认还会提供 gateway 模板以支持将代理注入到 Gateway Deployment 中。

Sidecar 自动注入问题

注入的结果和预期不一致

不一致包括 sidecar 的非预期注入和预期未注入。

  1. 确保您的 pod 不在 kube-systemkube-public 名称空间中。这些命名空间中的 pod 将忽略 sidecar 自动注入。

  2. 确保您的 pod 在其 pod 定义中没有 hostNetwork:truehostNetwork:true 的 pod 将忽略 sidecar 自动注入。

    sidecar 模型假定 iptables 会拦截所有 pod 中的流量给 Envoy,但是 hostNetwork:true 的 pod 不符合此假设,并且会导致主机级别的路由失败。

  3. 通过检查 webhook 的 namespaceSelector 以确定目标命名空间是否包含在 webhook 范围内。

    包含在范围内的 namespaceSelector 如下所示:

    1
    2
    3
    4
    5
    6
    7
    $ kubectl get mutatingwebhookconfiguration istio-sidecar-injector -o yaml | grep "namespaceSelector:" -A5
    namespaceSelector:
    matchLabels:
    istio-injection: enabled
    rules:
    - apiGroups:
    - ""

    在有 istio-injection=enabled 标签的命名空间中创建 pod 就会调用注入 webhook。

    1
    2
    3
    4
    5
    6
    $ kubectl get namespace -L istio-injection
    NAME STATUS AGE ISTIO-INJECTION
    default Active 18d enabled
    istio-system Active 3d
    kube-public Active 18d
    kube-system Active 18d

    不包含在注入范围的 namespaceSelector 如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    $ kubectl get mutatingwebhookconfiguration istio-sidecar-injector -o yaml | grep "namespaceSelector:" -A5
    namespaceSelector:
    matchExpressions:
    - key: istio-injection
    operator: NotIn
    values:
    - disabled
    rules:
    - apiGroups:
    - ""

    在没有标记 istio-injection=disabled 标签的命名空间中创建 pod,注入 webhook 就会被调用。

    1
    2
    3
    4
    5
    6
    $ kubectl get namespace -L istio-injection
    NAME STATUS AGE ISTIO-INJECTION
    default Active 18d
    istio-system Active 3d disabled
    kube-public Active 18d disabled
    kube-system Active 18d disabled

    验证应用程序 pod 的命名空间是否已相应地被正确(重新)标记,例如:

    1
    $ kubectl label namespace istio-system istio-injection=disabled --overwrite

    (对所有需要自动注入 webhook 的命名空间都重复上述步骤)

    1
    $ kubectl label namespace default istio-injection=enabled --overwrite
  4. 检查默认策略

    istio-sidecar-injector configmap 中检查默认注入策略。

    1
    2
    $ kubectl -n istio-system get configmap istio-sidecar-injector -o jsonpath='{.data.config}' | grep policy:
    policy: enabled

    策略允许的值为 disabled 或者 enabled。仅当 webhook 的 namespaceSelector 与目标命名空间匹配时,默认策略才会生效。无法识别的策略值默认为 disabled

  5. 检查每个 pod 的注解

    可以使用 pod template spec metadata 中的注解 sidecar.istio.io/inject 来覆盖默认策略,如果这样的话 deployment 相应的 metadata 将被忽略。注释值为 true 会被强制注入 sidecar,为 false 则会强制不注入 sidecar。

    以下注解会覆盖默认策略并强制注入 sidecar:

    1
    2
    3
    4
    5
    6
    7
    $ kubectl get deployment sleep -o yaml | grep "sidecar.istio.io/inject:" -C3
    template:
    metadata:
    annotations:
    sidecar.istio.io/inject: "true"
    labels:
    app: sleep

pods 不能创建

在失败的 pod 的 deployment 上运行 kubectl describe -n namespace deployment name。通常能在事件中看到调用注入 webhook 失败的原因。

x509 证书相关的错误

1
2
3
4
Warning  FailedCreate  3m (x17 over 8m)  replicaset-controller  Error creating: Internal error occurred: \
failed calling admission webhook "sidecar-injector.istio.io": Post https://istio-sidecar-injector.istio-system.svc:443/inject: \
x509: certificate signed by unknown authority (possibly because of "crypto/rsa: verification error" while trying \
to verify candidate authority certificate "Kubernetes.cluster.local")

x509: certificate signed by unknown authority 错误通常由 webhook 配置中的空 caBundle 引起。

验证 mutatingwebhookconfiguration 配置中的 caBundle 是否与 istio-sidecar-injector 中 pod 安装的根证书匹配。

1
2
3
4
$ kubectl get mutatingwebhookconfiguration istio-sidecar-injector -o yaml -o jsonpath='{.webhooks[0].clientConfig.caBundle}' | md5sum
4b95d2ba22ce8971c7c92084da31faf0 -
$ kubectl -n istio-system get secret istio.istio-sidecar-injector-service-account -o jsonpath='{.data.root-cert\.pem}' | md5sum
4b95d2ba22ce8971c7c92084da31faf0 -

CA 证书必须匹配,否则需要重新启动 sidecar-injector pods。

1
2
3
$ kubectl -n istio-system patch deployment istio-sidecar-injector \
-p "{\"spec\":{\"template\":{\"metadata\":{\"labels\":{\"date\":\"`date +'%s'`\"}}}}}"
deployment.extensions "istio-sidecar-injector" patched

deployment 状态中出现 no such hostsno endpoints available

注入是失效关闭的(fail-close)。如果 istio-sidecar-injector pod 尚未准备就绪,则无法创建 pod。在这种情况下,则会出现 no endpoints available

1
2
3
Internal error occurred: failed calling admission webhook "istio-sidecar-injector.istio.io": \
Post https://istio-sidecar-injector.istio-system.svc:443/admitPilot?timeout=30s: \
no endpoints available for service "istio-sidecar-injector"
1
2
3
$  kubectl -n istio-system get pod -listio=sidecar-injector
NAME READY STATUS RESTARTS AGE
istio-sidecar-injector-5dbbbdb746-d676g 1/1 Running 0 2d
1
2
3
$ kubectl -n istio-system get endpoints istio-sidecar-injector
NAME ENDPOINTS AGE
istio-sidecar-injector 10.48.6.108:15014,10.48.6.108:443 3d

如果 pod 或 endpoint 尚未准备就绪,可以通过检查 pod 日志和状态查找有关 webhook pod 无法启动的原因。

1
2
3
4
5
6
$ for pod in $(kubectl -n istio-system get pod -listio=sidecar-injector -o jsonpath='{.items[*].metadata.name}'); do \
kubectl -n istio-system logs ${pod} \
done
$ for pod in $(kubectl -n istio-system get pod -listio=sidecar-injector -o name); do \
kubectl -n istio-system describe ${pod} \
done

如果 Kubernetes API server 有代理设置的话,sidecar 的自动注入功能是不能用的

当 Kubernetes API server 包含诸如以下的代理设置时:

1
2
3
4
5
6
7
env:
- name: http_proxy
value: http://proxy-wsa.esl.foo.com:80
- name: https_proxy
value: http://proxy-wsa.esl.foo.com:80
- name: no_proxy
value: 127.0.0.1,localhost,dockerhub.foo.com,devhub-docker.foo.com,10.84.100.125,10.84.100.126,10.84.100.127

使用这些设置,sidecar 自动注入就会失败。相关的报错可以在 kube-apiserver 日志中找到:

1
W0227 21:51:03.156818       1 admission.go:257] Failed calling webhook, failing open sidecar-injector.istio.io: failed calling admission webhook "sidecar-injector.istio.io": Post https://istio-sidecar-injector.istio-system.svc:443/inject: Service Unavailable

根据 *_proxy 相关的的环境变量设置,确保 pod 和 service CIDRs 是没有被代理的。检查 kube-apiserver 的运行日志验证是否有请求正在被代理。

一种解决方法是在 kube-apiserver 的配置中删除代理设置,另一种解决方法是把 istio-sidecar-injector.istio-system.svc 或者 .svc 加到 no_proxyvalue 里面。每种解决方法都需要重新启动 kube-apiserver

Kubernetes 与此有关的一个 issue 已被 PR #58698 解决。

在 pods 中使用 tcpdump 的限制

tcpdump 在 sidecar 中不能工作 - 因为该容器不以 root 身份运行。但是由于同一 pod 内容器的网络命名空间是共享的,因此 pod 中的其他容器也能看到所有数据包。iptables 也能查看到 pod 级别的相关配置。

Envoy 和应用程序之间的通信是通过 127.0.0.1 进行的,这个通讯过程未加密。

群集不会自动缩小

由于 Sidecar 容器安装了本地存储卷,因此节点自动缩放器无法使用注入的 Pod 驱逐节点。这是一个已知的问题。解决方法是向 Pod 添加注释 “cluster-autoscaler.kubernetes.io/safe-to-evict”:“true”