第26章 Sidecar 与流量劫持:Istio init-container 的 iptables 原理
学习目标
- 理解 sidecar 模式:每 Pod 注入一个 Envoy 接管全部进出流量
- 走通 Istio 的流量劫持全链路:init-container 用 iptables 把流量 REDIRECT 到 Envoy
- 看懂 Istio 的端口模型(15001/15006/15090/15021…)与防环路机制
- 了解 sidecarless(Ambient mesh)与 CNI 注入的演进
前置知识
- 第11章 透明代理(iptables REDIRECT、SO_ORIGINAL_DST、防环路)——本章是它在 K8s 的落地
- 第15章 Envoy、第05章 mTLS、apiGateway · 服务网格
原理
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) |
| 15090 | Envoy Prometheus 指标 |
| 15000 | Envoy admin(config_dump 等,第15章) |
| 15020 | Pilot 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 没注入 sidecar | namespace 没标签 / Pod 没重建 | 打 istio-injection=enabled 并 restart |
| init-container 失败 | 缺 NET_ADMIN / PSP 限制 | 放开权限,或改用 istio-cni |
| 流量没被劫持 | iptables 没生效 / hostNetwork Pod | 查 ISTIO 链;hostNetwork Pod 不注入 |
| 启动瞬间请求失败 | 业务容器先于 Envoy ready | 配 holdApplicationUntilProxyStarts |
| 调用一直 503 | mTLS 模式不匹配(一边 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 新标准。