目录
Please enable Javascript to view the contents

K8s网络-流量路径全解析(Pod/Service/Ingress/Egress)

 ·  ☕ 6 分钟

系列导航

本系列从 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、不经过路由:

1
2
# 容器A 中访问容器B
curl http://localhost:9090

这是唯一不走 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-VXLANPodA → veth → flannel.1(封装 VXLAN)→ 宿主机 eth0 → 物理网络 → 目标宿主机 eth0 → flannel.1(解封装)→ veth → PodB
Calico-BGPPodA → veth → 宿主机路由表(BGP 路由:PodB 子网 → 节点2 IP)→ eth0 → 物理网络 → 节点2 eth0 → 路由表 → veth → PodB
Calico-IPIPPodA → veth → tunl0(封装 IPIP)→ 宿主机 eth0 → 物理网络 → 目标宿主机 eth0 → tunl0(解封装)→ veth → PodB
Cilium-eBPFPodA → veth → 宿主机 lxc_* → TC eBPF(封装/路由/策略)→ eth0 → 物理网络 → 目标 eth0 → TC eBPF(解封装)→ lxc_* → PodB
Cilium 差异:不经过独立的路由表或 VTEP 设备。跨节点 Pod 通信的封装/路由/策略全部压缩在 TC 钩子上的一个 eBPF 程序中完成,没有额外的 flannel.1 / tunl0 / vxlan.calico 设备。

关键技术点回顾(详见 ①篇):

  1. IPAM 给 Pod 分配唯一 IP
  2. 跨节点路由信息由 CNI 插件维护
  3. 数据包到达机制:封装(Overlay)或直接路由

3. Pod ↔ Service

Service 是 K8S 的抽象层,提供稳定的虚拟 IP 和端口。Pod IP 会变,Service IP 不变。

3.1 Service 的流量路径

1
2
3
4
客户端 Pod → Service ClusterIP:Port
  → iptables/ipvs 规则(kube-proxy 写入)
  → DNAT:目标 IP = Service IP 改为后端 Pod IP
  → 后续走 Pod ↔ Pod 路径(§2)

Service 本身不是网络设备,只是一个虚拟 IP,由 kube-proxy 在每台节点上写入 iptables 或 ipvs 规则实现流量劫持和负载均衡。

Cilium 差异:不经过 kube-proxy。Service → Endpoint 映射直接写入 eBPF Map,TC 钩子上的 eBPF 程序做 O(1) 哈希查找完成 DNAT,消除 iptables 规则链遍历和 conntrack 全局锁。

3.2 iptables 模式 vs IPVS 模式

维度iptablesIPVS
转发机制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:

1
外部客户端 → node1:30001 → iptables/ipvs DNAT → Service → 后端 Pod

不推荐直接用于生产——端口管理不便、无法做 TLS 终结、缺少高级负载均衡能力。

4.2 LoadBalancer

云环境(AWS、GCP、Azure)或私有方案(MetalLB)为 Service 分配外部 IP:

1
外部客户端 → LoadBalancer IP:Port → 后端节点 NodePort → Service → Pod

4.3 Ingress

L7 反向代理,通过域名和路径路由到不同 Service:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 传统方案
外部请求 → Ingress Controller (nginx/traefik)
  → 根据 Host/Path 规则
  → 匹配的 Service:Port
  → iptables/ipvs DNAT
  → 后端 Pod

# Cilium 方案(Gateway API)
外部请求 → Cilium eBPF L7 代理(envoy 内嵌)
  → 根据 Host/Path 规则
  → eBPF Map 直接查找后端 Pod
  → 后端 Pod

Cilium 差异: Gateway API 原生支持(K8S v1.0 2023.10 GA),Ingress Controller 不再是独立 Pod——L7 路由集成进 Cilium 的 eBPF 数据路径,比传统方案少一跳 Pod 间转发。

Ingress 典型配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
spec:
  rules:
  - host: api.example.com
    http:
      paths:
      - path: /users
        pathType: Prefix
        backend:
          service:
            name: user-service
            port:
              number: 8080

4.4 ServiceType 总览

ServiceType外部可达典型场景实现方式
ClusterIP集群内互通默认类型,仅分配内网 IP
NodePort开发测试每节点开端口,直连节点 IP:Port
LoadBalancer生产环境云 LB 或 MetalLB 分配公网 IP
ExternalName逻辑可达DNS CNAME返回 CNAME 记录,无代理

5. Pod → 集群外(Egress 出站流量)

Pod 访问外网时,源 IP 需要被替换为宿主机 IP(SNAT):

1
2
3
Pod (172.17.0.5) → eth0 → docker0/cni0
  → 宿主机 iptables SNAT(源 IP 改为宿主机 IP)
  → 宿主机 eth0 → 外网

关键规则(iptables 视角):

1
iptables -t nat -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
  • 源地址来自 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 记录:

1
2
3
4
Pod → DNS 查询: my-service.my-ns.svc.cluster.local
  → CoreDNS 返回 Service ClusterIP
  → Pod 向 ClusterIP 发起请求
  → kube-proxy iptables/ipvs 把流量 DNAT 到后端 Pod

DNS 记录命名格式:

1
service-name.namespace.svc.cluster.local

6.2 环境变量

Kubelet 在 Pod 启动前,为每个已存在的 Service 注入环境变量:

1
2
REDIS_SERVICE_HOST=10.96.100.5
REDIS_SERVICE_PORT=6379

限制: Service 必须在 Pod 之前创建才会注入。Pod 启动后新增的 Service,已有 Pod 不会获得环境变量。因此生产环境依赖 DNS。


参考链接

分享

Hex
作者
Hex
CloudNative Developer