第26章 Sidecar 与流量劫持:Istio init-container 的 iptables 原理

学习目标

  • 理解 sidecar 模式:每 Pod 注入一个 Envoy 接管全部进出流量
  • 走通 Istio 的流量劫持全链路:init-container 用 iptables 把流量 REDIRECT 到 Envoy
  • 看懂 Istio 的端口模型(15001/15006/15090/15021…)与防环路机制
  • 了解 sidecarless(Ambient mesh)与 CNI 注入的演进

前置知识


原理

sidecar 模式:给每个 Pod 配一个代理

Service Mesh 的核心做法:给每个业务 Pod 注入一个 Envoy 代理容器(sidecar),让 Pod 的所有进出流量都经过它。于是 mTLS、路由、重试、限流、遥测全在这个代理里做,业务代码零侵入。

        一个 Pod
  ┌───────────────────────────────────┐
  │  [业务容器]      [istio-proxy/Envoy] │
  │      │ 所有流量被劫持给 Envoy ↑     │
  │      └──── iptables REDIRECT ───────┘
  └───────────────────────────────────┘
   业务容器以为在直连,其实全经过了 sidecar

注入:自动 vs 手动

  • 自动注入:给 namespace 打标签 istio-injection=enabled,Istio 的 mutating webhook 在 Pod 创建时自动改 Pod spec,塞入 istio-init 和 istio-proxy 两个容器
  • 手动注入:istioctl kube-inject -f deploy.yaml 把注入后的 yaml 生成出来

注入后,一个原本 1 容器的 Pod 变成:1 个 init-container(istio-init)+ 业务容器 + 1 个 sidecar(istio-proxy)。

流量劫持全链路(呼应第11章)

这是 第11章透明代理 的终极落地。istio-init 这个 init-container 在 Pod 的网络命名空间里执行 iptables 规则(需要 NET_ADMIN 能力),把流量劫持到 Envoy:

出站(业务容器访问外部服务):
  app ──▶ [iptables OUTPUT REDIRECT → 15001] ──▶ Envoy ──mTLS──▶ 对端 Pod

入站(外部访问本 Pod):
  对端 ──▶ [iptables PREROUTING REDIRECT → 15006] ──▶ Envoy ──▶ app

防环路(第11章核心):
  Envoy 以 UID 1337 运行,iptables 用 -m owner --uid-owner 1337 -j RETURN 放过它自己,
  否则 Envoy 转发出去的流量又被劫持 → 无限环路

完整出站链路(两个 Pod 间):

  A-app → A的iptables(OUTPUT) → A-Envoy:15001
        → [选路由+负载均衡+mTLS加密]
        → B-Envoy:15006 → B的iptables(放行到app) → B-app
  回程对称返回。整条链路 app 无感,mTLS/遥测全自动。

Istio 端口模型(看到这些端口别懵)

istio-proxy 容器监听一组固定端口:

端口用途
15001出站流量被 REDIRECT 到这(outbound)
15006入站流量被 REDIRECT 到这(inbound)
15021健康检查(/healthz/ready)
15090Envoy Prometheus 指标
15000Envoy admin(config_dump 等,第15章)
15020Pilot agent 合并指标/agent

sidecar 的代价与演进

每 Pod 一个 Envoy 不是免费的:

  • 资源:每个 sidecar 几十 MB 内存 + CPU,规模大时累加可观
  • 延迟:每跳多两次代理(出站 + 入站各一次)
  • 启动顺序:init-container 先跑、业务容器要等 sidecar ready 才能出网(否则启动瞬间流量失败)
  • 生命周期:sidecar 与业务容器的优雅关闭顺序

演进方向:

  • Istio Ambient(sidecarless):用每节点一个 ztunnel(L4 mTLS)+ 按需 waypoint(L7 代理)取代每 Pod sidecar,大幅省资源(呼应 第11章 的 eBPF)
  • istio-cni:用 CNI 插件配 iptables 取代 init-container,免去业务 Pod 的 NET_ADMIN 特权

️ 实现 / 命令

实验一:开启注入并观察 Pod 多了什么

kubectl label namespace default istio-injection=enabled
kubectl apply -f deploy.yaml
kubectl rollout restart deploy/myapp        # 重建 Pod 触发注入

# 看 Pod:现在有 2 个容器(业务 + istio-proxy)+ 1 个 init
kubectl get pod -l app=myapp -o jsonpath='{.items[0].spec.containers[*].name}'
# myapp istio-proxy
kubectl get pod -l app=myapp -o jsonpath='{.items[0].spec.initContainers[*].name}'
# istio-init

实验二:看 init-container 配的 iptables(呼应第11章)

# 在 sidecar 容器里看 NAT 表(Istio 生成的 ISTIO_* 链)
kubectl exec <pod> -c istio-proxy -- iptables -t nat -S | grep ISTIO
# -A ISTIO_OUTPUT ... -j ISTIO_REDIRECT          出站重定向
# -A ISTIO_REDIRECT -p tcp -j REDIRECT --to-ports 15001
# -A ISTIO_INBOUND ... -j ISTIO_IN_REDIRECT      入站重定向到 15006
# -A ISTIO_OUTPUT -m owner --uid-owner 1337 -j RETURN   ← 放过 Envoy 自己,防环路

亲眼看到 --uid-owner 1337 -j RETURN,第11章 讲的"放过代理自己防环路"就坐实了。

实验三:确认 Envoy 接管了路由

# 看这个 sidecar 拿到的 Listener/Cluster(控制面 xDS 下发,第15章)
istioctl proxy-config listeners <pod>
istioctl proxy-config clusters  <pod>
# 能看到 15001/15006 的 Listener 和指向各 Service 的 Cluster

实验四:验证 mTLS 生效

# 强制 namespace 全 mTLS
kubectl apply -f - <<'EOF'
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata: { name: default, namespace: default }
spec: { mtls: { mode: STRICT } }
EOF
# 此后 Pod 间流量自动 mTLS 加密(第05章),抓包看到的是 TLS 而非明文
istioctl authn tls-check <pod>     # 查看 mTLS 状态

排错

现象根因解决
Pod 没注入 sidecarnamespace 没标签 / Pod 没重建打 istio-injection=enabled 并 restart
init-container 失败缺 NET_ADMIN / PSP 限制放开权限,或改用 istio-cni
流量没被劫持iptables 没生效 / hostNetwork Pod查 ISTIO 链;hostNetwork Pod 不注入
启动瞬间请求失败业务容器先于 Envoy ready配 holdApplicationUntilProxyStarts
调用一直 503mTLS 模式不匹配(一边 STRICT 一边明文)统一 PeerAuthentication(第29章)
无限环路/CPU 高防环路规则缺失确认 --uid-owner 1337 RETURN(第11章)

本章小结

  • sidecar 模式:每 Pod 注入 Envoy,劫持全部进出流量,业务零侵入。
  • 劫持靠 init-container 在 Pod netns 配 iptables:出站→15001、入站→15006,--uid-owner 1337 RETURN 防环路(第11章)。
  • 端口模型:15001/15006(流量)、15021(健康)、15090(指标)、15000(admin)。
  • 代价是资源/延迟/启动顺序;演进为 Ambient(ztunnel+waypoint) 与 istio-cni。

下一章 第27章 Ingress 与南北流量,从"东西流量的 sidecar"转到"南北流量的入口"——Ingress Controller 如何作为反向代理把外部流量送进集群,及 Gateway API 新标准。