istio从入门到放弃系列(四)——istio数据平面核心sidecar的流量劫持

1,165 阅读6分钟

前面三章已经简要的介绍了一下istio的使用方法,没看的朋友建议看一下,可以加深理解,这次我们来开始研究istio的数据平面,之前就说过要单独抽出来sidecar好好讲一讲,今天来兑现这个承诺。

什么是sidecar

将应用程序的功能划分为单独的进程运行在同一个最小调度单元中(例如 Kubernetes 中的 Pod)可以被视为 sidecar 模式。如下图所示,sidecar 模式允许您在应用程序旁边添加更多功能。

这样的模式可以保证我们在控制pod级别流量时,不会对应用本身产生修改的需求。

sidecar的优势有哪些

sidecar模式下,流量控制container和业务container共享着存储、网络等资源,有点类似于一个虚拟机里的两个线程,这种部署方式可以让sidecar具有以下优势:

  • 将与应用业务逻辑无关的功能抽象到共同基础设施,降低了微服务代码的复杂度。
  • 因为不再需要编写相同的第三方组件配置文件和代码,所以能够降低微服务架构中的代码重复度。
  • Sidecar 可独立升级,降低应用程序代码和底层平台的耦合度。

istio的sidecar里有什么

我们以之前实战文章中的bookinfo里的productpage为例,看一下sidecar会启动什么:

在productpage部署时,istio为其部署了两个sidecar,一个是init container,另外一个则是伴生的proxy,启动过程中pod会先启动init container,在环境初始化完毕后,启动productpage和istio-proxy,待两个container都就绪后将pod状态置为running。

init容器解析

我们来看一下yaml中init的代码

initContainers:
  - args:
    - istio-iptables
    - -p
    - "15001"
    - -z
    - "15006"
    - -u
    - "1337"
    - -m
    - REDIRECT
    - -i
    - '*'
    - -x
    - ""
    - -b
    - '*'
    - -d
    - 15090,15021,15020
    - --run-validation
    - --skip-rule-apply
    env:
    - name: DNS_AGENT
    image: rancher/istio-proxyv2:1.7.3
    imagePullPolicy: Always
    name: istio-validation
    resources:
      limits:
        cpu: "2"
        memory: 1Gi
      requests:
        cpu: 10m
        memory: 10Mi
    securityContext:
      allowPrivilegeEscalation: false
      capabilities:
        drop:
        - ALL
      privileged: false
      readOnlyRootFilesystem: true
      runAsGroup: 1337
      runAsNonRoot: true
      runAsUser: 1337
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: bookinfo-productpage-token-88mvv
      readOnly: true

可以看到关键的istio-iptables的操作,查看一下对应的文档:

istio-iptables [flags]
  -p: 指定重定向所有 TCP 流量的 sidecar 端口(默认为 $ENVOY_PORT = 15001)
  -m: 指定入站连接重定向到 sidecar 的模式,“REDIRECT” 或 “TPROXY”(默认为 $ISTIO_INBOUND_INTERCEPTION_MODE)
  -b: 逗号分隔的入站端口列表,其流量将重定向到 Envoy(可选)。使用通配符 “*” 表示重定向所有端口。为空时表示禁用所有入站重定向(默认为 $ISTIO_INBOUND_PORTS)
  -d: 指定要从重定向到 sidecar 中排除的入站端口列表(可选),以逗号格式分隔。使用通配符“*” 表示重定向所有入站流量(默认为 $ISTIO_LOCAL_EXCLUDE_PORTS)
  -o:逗号分隔的出站端口列表,不包括重定向到 Envoy 的端口。
  -i: 指定重定向到 sidecar 的 IP 地址范围(可选),以逗号分隔的 CIDR 格式列表。使用通配符 “*” 表示重定向所有出站流量。空列表将禁用所有出站重定向(默认为 $ISTIO_SERVICE_CIDR)
  -x: 指定将从重定向中排除的 IP 地址范围,以逗号分隔的 CIDR 格式列表。使用通配符 “*” 表示重定向所有出站流量(默认为 $ISTIO_SERVICE_EXCLUDE_CIDR)。
  -k:逗号分隔的虚拟接口列表,其入站流量(来自虚拟机的)将被视为出站流量。
  -g:指定不应用重定向的用户的 GID。(默认值与 -u param 相同)
  -u:指定不应用重定向的用户的 UID。通常情况下,这是代理容器的 UID(默认值是 1337,即 istio-proxy 的 UID)。
  -z: 所有进入 pod/VM 的 TCP 流量应被重定向到的端口(默认 $INBOUND_CAPTURE_PORT = 15006)。

该容器存在的意义就是让 sidecar 代理可以拦截所有的进出 pod 的流量,15090 端口(Mixer 使用)和 15092 端口(Ingress Gateway)除外的所有入站(inbound)流量重定向到 15006 端口(sidecar),再拦截应用容器的出站(outbound)流量经过 sidecar 处理(通过 15001 端口监听)后再出站。关于 Istio 中端口用途请参考 Istio 官方文档。
总结一下,init容器的作用是:

  • 将应用容器的所有流量都转发到 sidecar 的 15006 端口。
  • 使用 istio-proxy 用户身份运行, UID 为 1337,即 sidecar 所处的用户空间,这也是 istio-proxy 容器默认使用的用户,见 YAML 配置中的 runAsUser 字段。
  • 使用默认的 REDIRECT 模式来重定向流量。
  • 将所有出站流量都重定向到 sidecar 代理(通过 15001 端口)。

init容器的iptables注入解析

为了查看init容器配置的iptables规则,我们需要有root权限,kubectl不支持root,我们登录到对应的机器上用docker去查看。

su - root #输入root密码
docker top `docker ps|grep "istio-proxy_productpage"|cut -d " " -f1`


拿到对应的PID

nsenter -n --target 31565
iptables -t nat -L -v


图上是iptables中nat配置的路由规则,由此我们可以看出istio init的一些修改

  • PREROUTING 链:用于目标地址转换(DNAT),将所有入站 TCP 流量跳转到 ISTIO_INBOUND 链上。
  • OUTPUT 链:将所有出站数据包跳转到 ISTIO_OUTPUT 链上。
  • INPUT 链和POSTROUTING 链未做处理
  • ISTIO_INBOUND 链:将所有入站流量重定向到 ISTIO_IN_REDIRECT 链上,目的地为 15090(mixer 使用)和 15020(Ingress gateway 使用,用于 Pilot 健康检查)端口的流量除外,发送到以上两个端口的流量将返回 iptables 规则链的调用点,即 PREROUTING 链的后继 POSTROUTING。
  • ISTIO_IN_REDIRECT 链:将所有的入站流量跳转到本地的 15006 端口,至此成功的拦截了流量到 sidecar 中。
  • ISTIO_OUTPUT 链:选择需要重定向到 Envoy(即本地) 的出站流量,所有非 localhost 的流量全部转发到 ISTIO_REDIRECT。为了避免流量在该 Pod 中无限循环,所有到 istio-proxy 用户空间的流量都返回到它的调用点中的下一条规则,本例中即 OUTPUT 链,因为跳出 ISTIO_OUTPUT 规则之后就进入下一条链 POSTROUTING。如果目的地非 localhost 就跳转到 ISTIO_REDIRECT;如果流量是来自 istio-proxy 用户空间的,那么就跳出该链,返回它的调用链继续执行下一条规则(OUTPUT 的下一条规则,无需对流量进行处理);所有的非 istio-proxy 用户空间的目的地是 localhost 的流量就跳转到 ISTIO_REDIRECT。
  • ISTIO_REDIRECT 链:将所有流量重定向到 Sidecar(即本地) 的 15001 端口。

小结

由此我们看到了sidecar对流量劫持的全过程,init通过iptables执行流量的劫持,而proxy并不在这一过程中,它的工作是执行控制平面下发的规则。

参考