系列导航
本系列从 Pod 网络连通入手,逐步展开到 K8s 网络全景。
① 概念 → ② Flannel → ③ Calico → ④ 流量路径 → ⑤ Cilium → ⑥ 对比 → ⑦ 排障 ‖ ⑧ 开发 → ⑨ 多网卡 → ⑩ AI 演进
| 顺序 | 文章 | 定位 |
|---|---|---|
| ① | 概念与入门 | 基础——主机网络、Docker 网络、CNI 标准、方案分类树 |
| ② | Flannel 详解 | CNI 实现——Overlay 封装(UDP/VXLAN/HostGW)与抓包 |
| ③ | Calico 详解 | CNI 实现——三层路由(BGP/IPIP)+ NetworkPolicy |
| ④ | 本篇 - 流量路径全解析 | 全貌——Pod/Service/Ingress/Egress,揭示 CNI 的边界 |
| ⑤ | Cilium 详解 | 超越 CNI——eBPF 统一 Pod + Service + L7 + Hubble |
| ⑥ | 插件对比与选型 | 选型——5 插件横向对比 + 决策树 |
| ⑦ | 排障思路与常用命令 | 运维——工具链 + 场景排查 + 性能 |
| — | 核心路径 ↑ | 扩展展望 ↓ |
| ⑧ | CNI 插件开发指南 | 扩展——基于 CNI 规范开发自定义插件 |
| ⑨ | 多网卡方案详解 | 进阶——Multus + SR-IOV/ipvlan 多网口实战 |
| ⑩ | AI 时代网络演进 | 展望——GPU 网络、eBPF 加速、未来方向 |
前面几篇讲「网络怎么搭」,本篇换一个视角——从不同通信维度分析流量实际怎么走。以下描述的是传统方案(Flannel / Calico + kube-proxy)的路径。Cilium 用 eBPF 在内核态替代了其中多个组件,各节末尾标注差异。
1. 容器 ↔ 容器(同 Pod 内)
Pod 内所有容器共享同一个 network namespace,共用一个 IP 和 MAC 地址。
Pod (172.17.0.2) ├─ 容器A :8080 └─ 容器B :9090
容器间通过 localhost 直接通信,不经过 CNI、不经过路由:
| |
这是唯一不走 CNI 的通信路径。Pod 内容器共享 IP,端口不能冲突。
2. Pod ↔ Pod
2.1 同节点 Pod 通信
基于 ①篇 §3 的 Docker 单机网络逻辑——容器路由表 + veth pair + 网桥 CAM 转发:
PodA eth0 ──vethA── docker0/cni0 ──vethB── PodB eth0
数据包全程在同一台宿主机内核栈中流转,不走物理网卡。
2.2 跨节点 Pod 通信
这是 CNI 插件的核心战场。根据插件不同,数据包走不同的路径:
| CNI 模式 | 路径 |
|---|---|
| Flannel-VXLAN | PodA → veth → flannel.1(封装 VXLAN)→ 宿主机 eth0 → 物理网络 → 目标宿主机 eth0 → flannel.1(解封装)→ veth → PodB |
| Calico-BGP | PodA → veth → 宿主机路由表(BGP 路由:PodB 子网 → 节点2 IP)→ eth0 → 物理网络 → 节点2 eth0 → 路由表 → veth → PodB |
| Calico-IPIP | PodA → veth → tunl0(封装 IPIP)→ 宿主机 eth0 → 物理网络 → 目标宿主机 eth0 → tunl0(解封装)→ veth → PodB |
| Cilium-eBPF | PodA → veth → 宿主机 lxc_* → TC eBPF(封装/路由/策略)→ eth0 → 物理网络 → 目标 eth0 → TC eBPF(解封装)→ lxc_* → PodB |
flannel.1 / tunl0 / vxlan.calico 设备。关键技术点回顾(详见 ①篇):
- IPAM 给 Pod 分配唯一 IP
- 跨节点路由信息由 CNI 插件维护
- 数据包到达机制:封装(Overlay)或直接路由
3. Pod ↔ Service
Service 是 K8S 的抽象层,提供稳定的虚拟 IP 和端口。Pod IP 会变,Service IP 不变。
3.1 Service 的流量路径
| |
Service 本身不是网络设备,只是一个虚拟 IP,由 kube-proxy 在每台节点上写入 iptables 或 ipvs 规则实现流量劫持和负载均衡。
3.2 iptables 模式 vs IPVS 模式
| 维度 | iptables | IPVS |
|---|---|---|
| 转发机制 | Netfilter NAT 规则链 | Linux 内核 L4 负载均衡 |
| 负载均衡算法 | 随机(statistic 模块,probability) | 多种:rr、lc、dh、sh 等 |
| 性能 | 规则数 >1000 时性能下降明显 | 哈希表查找,O(1),大规模规则性能好 |
| 内核态切换 | 需要经过 Netfilter 钩子 | 在内核空间直接处理 |
| 默认模式 | K8S < 1.11 默认 | K8S ≥ 1.11 默认(需加载 ip_vs 内核模块) |
建议: 生产环境优先用 IPVS。大规模集群(>1000 Service)iptables 规则数量级增长会明显影响延迟。
Cilium 场景: 上述两种都依赖 kube-proxy。如果用 Cilium,kube-proxy 被完全替换——Service 映射存在 eBPF Map 中,匹配复杂度 O(1),无 iptables 遍历、无 conntrack 锁争用。
3.3 流量路径示意
客户端 Pod kube-proxy 后端 Pod
│ │ │
│ 1. 目标 IP = Svc:ClusterIP│ │
│ │ │
│ 2. 内核匹配 iptables/ipvs│ │
│ 规则:DNAT 到后端 Pod IP │ │
│ │ │
│ 3. 目标 IP 改写为 Pod IP │ │
│ ────────────────────────────────────────────────────────► │
│ │ │
│ 4. 走 CNI Pod ↔ Pod 路径 │ │
│ │ │
│ 5. 回复包返回 SNAT │ │
│ ◄──────────────────────────────────────────────────────── │4. 集群外 → Service(外部入站流量)
4.1 NodePort
在每个节点上开放指定端口(30000-32767),外部流量到达节点端口后转发给 Service:
| |
不推荐直接用于生产——端口管理不便、无法做 TLS 终结、缺少高级负载均衡能力。
4.2 LoadBalancer
云环境(AWS、GCP、Azure)或私有方案(MetalLB)为 Service 分配外部 IP:
| |
4.3 Ingress
L7 反向代理,通过域名和路径路由到不同 Service:
| |
Cilium 差异: Gateway API 原生支持(K8S v1.0 2023.10 GA),Ingress Controller 不再是独立 Pod——L7 路由集成进 Cilium 的 eBPF 数据路径,比传统方案少一跳 Pod 间转发。
Ingress 典型配置:
| |
4.4 ServiceType 总览
| ServiceType | 外部可达 | 典型场景 | 实现方式 |
|---|---|---|---|
| ClusterIP | 否 | 集群内互通 | 默认类型,仅分配内网 IP |
| NodePort | 是 | 开发测试 | 每节点开端口,直连节点 IP:Port |
| LoadBalancer | 是 | 生产环境 | 云 LB 或 MetalLB 分配公网 IP |
| ExternalName | 逻辑可达 | DNS CNAME | 返回 CNAME 记录,无代理 |
5. Pod → 集群外(Egress 出站流量)
Pod 访问外网时,源 IP 需要被替换为宿主机 IP(SNAT):
| |
关键规则(iptables 视角):
| |
- 源地址来自 Pod 子网,出接口不是
docker0/cni0,就执行 SNAT - 回包到达宿主机时,内核根据连接跟踪表(conntrack)做反向 DNAT,送回 Pod
适用场景: 大部分 K8S 集群默认配好 SNAT。如果 Pod 直接使用宿主机网络(
hostNetwork: true),则 Pod 的源 IP 就是宿主机 IP,不需要 SNAT。
Cilium 差异: Cilium 的 eBPF 程序在 TC 钩子完成 SNAT,不经过 iptables
MASQUERADE规则。conntrack 也是 eBPF 独立实现,无全局锁。
6. 服务发现
Pod 需要知道要访问哪个 Service,K8S 提供两种机制:
6.1 DNS(推荐)
CoreDNS(或 kube-dns)作为集群内 DNS 服务,watch API Server 中的 Service 变化,自动创建 DNS 记录:
| |
DNS 记录命名格式:
| |
6.2 环境变量
Kubelet 在 Pod 启动前,为每个已存在的 Service 注入环境变量:
| |
限制: Service 必须在 Pod 之前创建才会注入。Pod 启动后新增的 Service,已有 Pod 不会获得环境变量。因此生产环境依赖 DNS。