Istio Sidecar:流量劫持原理与 Envoy 工作机制

​ 要有效运用 Istio 管理复杂的微服务通信,理解其数据平面的流量捕获与路由机制是首要前提,而 Envoy Sidecar 在这一机制中扮演着核心角色。本文将详细阐述 Istio 数据平面的关键运作原理,重点分析网络流量在 Envoy 代理内部以及 Pod 之间的具体流转路径,并深入解析流量劫持这一关键技术的实现方式。掌握这些基础原理,是工程师深入理解并熟练应用 Istio 的基础。

一、Envoy Sidecar 是如何注入的

1. Istio 是如何实现注入的

​ Istio 或者其他类似需要进行 Pod 注入的业务,会通过 Kubernetes Mutating Admission Webhook 进行注入。 ​ Kubernetes Webhook 的主要作用就是在 API Server 处理请求(比如创建、更新、删除资源)之前,截获这些请求,并根据配置进行检查 (Validate) 或修改 (Mutate)。这就像给 Kubernetes API Server 添加了定制化的处理流程。 整个流程大致是这样的:

2 Istio 注入后的 Pod 结构

原始 Pod yaml

apiVersion: v1
kind: Pod
metadata:
  name: "testpod"
  labels:
    app: "testpod"
spec:
  containers:
  - name: testpod
    image: nginx

注入后的 Pod:

apiVersion: v1
kind: Pod
metadata:
  name: testpod-simplified-istio-zh # Pod 名称
  namespace: istio # 假设 Istio 在此命名空间或正在监视此命名空间
  labels:
    app: testpod # 您的应用程序标签
    # 常见的 Istio 标签 (通常由 Istio 注入或用户设置)
    security.istio.io/tlsMode: istio
    service.istio.io/canonical-name: testpod
    service.istio.io/canonical-revision: latest
  annotations:
    # 此注解通常用于指定注入的 Istio 修订版本
    istio.io/rev: default
spec:
  # serviceAccountName 对 Istio 的身份特性很重要
  serviceAccountName: default
  containers:
  - name: testpod 
    image: nginx 
  # 2. Istio 代理边车容器
  - name: istio-proxy
    image: docker.io/istio/proxyv2:1.25.2 # 使用特定的 Istio 版本
    imagePullPolicy: IfNotPresent
    args:
    - proxy
    - sidecar
    - --domain
    - $(POD_NAMESPACE).svc.cluster.local
    - --proxyLogLevel=warning # 基本日志配置
    - --log_output_level=default:info
  # 3. Istio Init 容器 (用于网络设置)
  initContainers:
  - name: istio-init
    image: docker.io/istio/proxyv2:1.25.2 # 应与代理版本匹配
    imagePullPolicy: IfNotPresent
    args:
    - istio-iptables
    - -p # Envoy 的入站端口
    - "15001"
    - -z # Envoy 的出站捕获端口
    - "15006"
    - -u # Envoy 运行所用的用户 ID
    - "1337"
    - -m # 拦截模式
    - REDIRECT
    - -i # 包括所有 IP 范围
    - '*'
    - -b # 包括所有重定向到 Envoy 的端口 (通配符)
    - '*'
    - -d # 从重定向中排除特定的入站端口
    - 15090,15021,15020
    - --log_output_level=default:info

上面的 Pod yaml 为了简洁,清理掉了下面的内容:

其实可以看出来,istio 注入了两个容器,一个容器是 istio-init,它会执行一个 iptables 规则劫持 Pod 流量,一个是 istio-proxy,即数据平面 envoy 的真正业务容器。

二、Envoy Sidecar 如何接管 Pod 进出流量?

​ 当一个 Pod 启动时,容器引擎首先会启动一个名为 “Pause” 的特殊容器。这个 Pause 容器的主要作用是建立并持有 Pod 的网络命名空间(Network Namespace)。随后启动的 Pod 内所有其他业务容器以及 Istio Sidecar 容器(如 istio-proxy)都会共享这同一个 Pause 容器的网络命名空间。

​ 在业务容器启动之前,如果 Pod 被注入了 Istio Sidecar,会先运行一个名为 istio-init 的初始化容器。istio-init 容器的关键任务是修改 Pod 网络命名空间内的 iptables 规则。通过这些 iptables 规则,从而实现 Pod 所有出入的流量,都被拦截至 Envoy Sidecar

查看 istio 拦截使用的 iptables 规则。

测试用的 yaml 。

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
  namespace: istio # 你可以根据需要修改命名空间,例如 "istio"
  labels:
    app: test-pod
spec:
  containers:
  - name: test-pod
    #image: nicolaka/netshoot # 包含了 iptables 和许多其他网络工具
    image: ubuntu:20.04
    command: ["/bin/sh", "-c", "sleep infinity"] # 保持容器运行,以便你可以 exec进去
    securityContext:
      privileged: true
      restartPolicy: Never # 或者 OnFailure,适用于调试 Pod
      
# apt-get update
# apt-get -y install iptables

进入容器,使用 iptables-save 查看 iptables 状况。

# 
# Generated by iptables-save v1.8.4 on Thu May 22 09:34:48 2025
*nat
:PREROUTING ACCEPT [41:1372]
:INPUT ACCEPT [41:1372]
:OUTPUT ACCEPT [40:3126]
:POSTROUTING ACCEPT [43:3306]
:ISTIO_INBOUND - [0:0]
:ISTIO_IN_REDIRECT - [0:0]
:ISTIO_OUTPUT - [0:0]
:ISTIO_REDIRECT - [0:0]
# 入方向的所有流量都要进入 ISTIO_INBOUND 。
-A PREROUTING -p tcp -j ISTIO_INBOUND
# 出方向所有流量都需要进入 ISTIO_OUTPUT 。
-A OUTPUT -j ISTIO_OUTPUT
# 下面这些目标的端口,直接放行,因为这些端口就是 envoy sidecar 使用的。
# 注释[1]
-A ISTIO_INBOUND -p tcp -m tcp --dport 15008 -j RETURN
-A ISTIO_INBOUND -p tcp -m tcp --dport 15090 -j RETURN
-A ISTIO_INBOUND -p tcp -m tcp --dport 15021 -j RETURN
-A ISTIO_INBOUND -p tcp -m tcp --dport 15020 -j RETURN
# 其他非 dest 为 envoy sidecar 的到 ISTIO_IN_REDIRECT ,即所有流量都转给 15006
-A ISTIO_INBOUND -p tcp -j ISTIO_IN_REDIRECT
-A ISTIO_IN_REDIRECT -p tcp -j REDIRECT --to-ports 15006
# output方向,source 为 127.0.0.6,请求的为 127.0.0.1(localhost) 的话,直接放行。
# 127.0.0.6 这个 IP 地址通常被 Envoy sidecar 代理自身使用
-A ISTIO_OUTPUT -s 127.0.0.6/32 -o lo -j RETURN
# 由 Envoy 代理自己 (UID 1337) 发起的、目标不是 127.0.0.1、也不是发往特殊端口 15008、但却要通过环回接口 lo 发送出去的 TCP 流量转给  Envoy Sidecar 入流量管理端口。
# 作用: Pod 自己请求自己的 Service 的时候,Dest 会为 Pod 自身,然后到达 ISTIO_OUTPUT 链,就会触发下面的规则,被送回 Envoy Sidecar 入流量管理端口。
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -o lo -p tcp -m tcp ! --dport 15008 -m owner --uid-owner 1337 -j ISTIO_IN_REDIRECT
# 如果目标网卡是 lo,但是 uid 非 1337 ,即 pod 自己访问 127.0.0.1 。就直接放行
-A ISTIO_OUTPUT -o lo -m owner ! --uid-owner 1337 -j RETURN
# 如果源头是 envoy sidecar 发起的 ,就直接放行。
# 这个是真正的网络出口,即 envoy sidecar 处理过的流量就将直接放行。
-A ISTIO_OUTPUT -m owner --uid-owner 1337 -j RETURN
# GID 再来一遍。
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -o lo -p tcp -m tcp ! --dport 15008 -m owner --gid-owner 1337 -j ISTIO_IN_REDIRECT
-A ISTIO_OUTPUT -o lo -m owner ! --gid-owner 1337 -j RETURN
-A ISTIO_OUTPUT -m owner --gid-owner 1337 -j RETURN
# 目标是 127.0.0.1 的直接放行
-A ISTIO_OUTPUT -d 127.0.0.1/32 -j RETURN
# 其他所有的都转给 15001 。
-A ISTIO_OUTPUT -j ISTIO_REDIRECT
-A ISTIO_REDIRECT -p tcp -j REDIRECT --to-ports 15001
COMMIT
# Completed on Thu May 22 09:34:48 2025

image-20250522233016777

流量路径图解:

总结:

  1. 入方向的TCP流量:除了发往Envoy Sidecar自身特定服务端口的流量被直接放行外,其余所有流量都被重定向至Envoy的入站处理端口15006。
  2. 出方向的TCP流量:127.0.0.1 (除了一些特殊的)相关的一些流量直接放行,其他所有其他由应用发起的出站TCP流量则被重定向至Envoy的出站处理端口15001。

简化后的版本:

image-20250522232558886

可以再回顾一下 init 容器的配置

  initContainers:
  - name: istio-init
    image: docker.io/istio/proxyv2:1.25.2 # 应与代理版本匹配
    imagePullPolicy: IfNotPresent
    args:
    - istio-iptables
    - -p # Envoy 的入站端口
    - "15001"
    - -z # Envoy 的出站捕获端口
    - "15006"
    - -u # Envoy 运行所用的用户 ID
    - "1337"
    - -m # 拦截模式
    - REDIRECT
    - -i # 包括所有 IP 范围
    - '*'
    - -b # 包括所有重定向到 Envoy 的端口 (通配符)
    - '*'
    - -d # 从重定向中排除特定的入站端口
    - 15090,15021,15020
    - --log_output_level=default:info

可以发现,iptables 中所定义的规则,基本是通过几个参数决定的。

​ 总而言之,通过 istio-init 初始化容器对 iptables 的巧妙配置,使得 Pod 的网络流量得以被透明地拦截并导向 Envoy Sidecar。这种底层的流量劫持与转发机制,正是 Istio 实现其丰富的流量管理、安全策略和遥测功能的关键所在。

三、Envoy Sidecar 在 Istio 中的工作机制与流量规则解析

当 Envoy Sidecar 启动时,它会利用 init 容器提供的配置信息,连接到 Istiod (Pilot) 的 xDS 服务端,并以订阅模式与 Istiod (Pilot) 进行交互,从而获取动态配置。

理解 Istio 的流量管理,首先需要区分其与传统 Kubernetes Service 代理模式的差异。Kubernetes Service 主要依赖 kube-proxy 实现一种反向代理的负载均衡,流量在节点级别被路由。而 Istio 的规则实现,则巧妙地通过注入到应用 Pod 中的 Envoy Sidecar,以正向代理的模式进行。

具体来说,Envoy Sidecar 对进出其所在 Pod 的流量进行拦截和处理:

  1. 入站流量 (Ingress):当外部流量进入 Pod 时,会先被该 Pod 内的 Envoy Sidecar 拦截。通常情况下,对于入站流量,Sidecar 主要执行安全策略(如 mTLS认证、授权)、收集遥测数据等,然后将流量直接转发给 Pod 内的业务容器。Envoy Sidecar 本身不执行核心业务逻辑。
  2. 出站流量 (Egress):当业务容器尝试访问其他服务(无论是集群内部还是外部)时,发出的流量同样会被其 Pod 内的 Envoy Sidecar 拦截。此时,所有高级流量治理功能,如动态路由(基于VirtualService)、负载均衡、熔断、超时、重试等,均由这个出口处 (egress) 的 Envoy Sidecar 根据从 Istiod 获取的规则来执行。

image-20250523022526427

我们可以对比一下流量路径的变化:

这种基于出口端 Sidecar 控制的架构模式,会产生一个重要的影响,即 Istio 高级流量规则的生效范围:

核心在于:Istio 的高级流量治理能力主要在流量发起的出口端(Egress Sidecar)得到实现和强制执行。

四、Ingress Gateway 的作用和工作原理

Istio Ingress Gateway 的网络原理相对更为集中和清晰。在功能定位上,它与传统的 Kubernetes Ingress 控制器有相似之处,都扮演着集群流量入口的角色,负责管理外部访问。然而,与服务网格内部 Pod 间通信依赖的 Envoy Sidecar 不同,Ingress Gateway 本身就是一个独立的 Envoy Proxy 实例,它以一个或多个专用 Pod 的形式运行,而不是作为 Sidecar 注入到某个业务应用的 Pod 中。

这个 Gateway Pod 的核心职责是作为外部流量进入服务网格的统一入口。因此,当外部客户端尝试访问网格内的服务时,典型的流量路径如下:

  1. 外部请求:用户或外部系统发起请求。
  2. 负载均衡器 (Load Balancer):流量首先到达云提供商或本地环境部署的外部负载均衡器。这个负载均衡器将流量导向集群中的一个或多个 Ingress Gateway 节点。
  3. NodePort/LoadBalancer Service: Kubernetes Service (通常是 LoadBalancer 类型或 NodePort 类型) 接收来自外部 LB 的流量,并将其路由到后端的 Istio Ingress Gateway Pod。
  4. Istio Ingress Gateway Pod (Envoy Proxy):Gateway Pod 内的 Envoy Proxy 接收流量。此时,Envoy 会根据GatewayVirtualService (或 HTTPRoute 等 Gateway API 资源) 的配置进行处理,例如:
    • TLS 卸载
    • 基于主机名或路径的路由
    • 请求头操作
    • 应用其他 Istio 策略(如授权、速率限制等)
  5. 目标业务 Pod: 经过 Ingress Gateway 处理和路由后,流量最终被转发到集群内部相应业务服务的 Pod。

简而言之,Ingress Gateway 充当了网格的“看门人”,集中管理所有入站连接,并通过其内部运行的 Envoy 实例,根据声明式配置将外部流量安全、智能地导向内部服务。

五、Egress Gateway:集群出口流量的统一管理与安全控制

​ Istio Egress Gateway 作为一组专用的 Envoy 代理 Pod(非应用 Pod 内的 Sidecar),用于集中管控从服务网格发往外部的出口流量。其核心目标是增强安全性、统一实施策略并简化网络连接。

  1. 为何需要 Egress Gateway 管理出口流量?

管控出口流量主要出于以下目的:

  1. Egress Gateway 流量路径示例

Istio Egress Gateway 通过提供一个专用的出口流量控制点,极大地增强了服务网格的安全性、可管理性和网络策略的灵活性,是构建企业级服务网格时管理外部依赖的重要组件。

总结

​ 通过上述分析,清晰揭示了Istio数据平面的核心在于每个业务Pod中注入的Envoy Sidecar。此Sidecar代理通过巧妙配置网络规则,实现了对应用流量的无感知拦截与全面管理。无论是进入应用的请求,还是应用发出的调用,都会经过Envoy的处理和转发。理解Envoy的工作模式、流量的精确导向机制以及底层的劫持技术原理,将使我们能更深刻地把握Istio流量管理的本质,为后续的高阶应用与故障排查奠定坚实基础。