一个请求的旅程:数据中心网络全景
这篇文章的价值:大多数后端工程师知道请求从浏览器发出、到服务器处理、再返回响应。但中间经过了什么?PoP 节点、骨干网光纤、Clos 交换矩阵、eBPF 四层负载均衡、VXLAN 容器网络——这些基础设施决定了你的系统能做到什么、不能做到什么。这篇文章追踪一个请求从用户手机到数据中心服务器的完整旅程,沿途拆解每一层的关键技术。
全局视角:三层架构
任何大规模互联网公司的网络基础设施都是三层结构:
| 层 | 职责 | 例子 |
|---|---|---|
| Edge / PoP | 用户流量入口,TLS 终止,地理路由 | 东京 PoP 接收日本用户的请求 |
| Backbone | 数据中心之间的高速私有光纤,运行 SR/MPLS | 新加坡 DC ↔ 美国 DC,~120ms RTT |
| DC Internal | Clos 交换矩阵连接 DC 内所有服务器 | Leaf-Spine-Core 三层交换 |
一个东京用户打开某个 App:手机 DNS 解析到最近的东京 PoP IP → PoP 终止 TLS,静态内容直接从 CDN 返回 → 动态请求走骨干网(SR/MPLS)到新加坡 DC → 进入 DC 后经过 Katran(L4 LB)→ Proxygen(L7 LB)→ 应用服务器 → 如果需要美国 DC 的数据,再走一次跨洋骨干网 → 响应通过 DSR 直接返回用户,绕过 L4 LB。
接下来沿着这条路径,逐层拆解。
DC 内部:Clos 架构
一台大型交换机无法扩展到数十万台服务器。Clos 拓扑用大量便宜的小交换机组合成一个无阻塞、可水平扩展的网络。
三层结构
┌─────────┐ ┌─────────┐
│ Core 1 │ │ Core 2 │ ← Layer 3: 连接 Pod
└────┬┬───┘ └───┬┬────┘
╱╱ ││ ╲╲ ╱╱││
┌───────╱╱─────┘│ ╲╲─╱╱──┘│
│ ╱╱ │ ╲╱╱ │
┌──────┼────╱╱─────────┼──────╱╲─────┼──────────┐
│ Pod A│ ╱╱ │ ╱╱ ╲╲ │ Pod B │
│ ▼ ▼ ▼ ▼ ▼ ▼ │
│ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐
│ │Spine 1 │ │Spine 2 │ │Spine 3 │ │Spine 4 │ ← Layer 2
│ └┬──┬──┬─┘ └┬──┬──┬─┘ └┬──┬──┬─┘ └┬──┬──┬─┘
│ │ │ │ │ │ │ │ │ │ │ │ │
│ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼
│ ┌──┐┌──┐┌──┐ ┌──┐┌──┐┌──┐ ┌──┐┌──┐┌──┐ ┌──┐┌──┐┌──┐
│ │L1││L2││L3│ │L1││L2││L3│ │L1││L2││L3│ │L1││L2││L3│ ← Layer 1: Leaf (ToR)
│ └┬─┘└┬─┘└┬─┘ └┬─┘└┬─┘└┬─┘ └┬─┘└┬─┘└┬─┘ └┬─┘└┬─┘└┬─┘
│ │ │ │ │ │ │ │ │ │ │ │ │
│ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ← 服务器机架
└────────────────────────────┘ └────────────────────────┘
| 层 | 名称 | 职责 |
|---|---|---|
| Layer 1 | Leaf (ToR) | 机架顶部交换机,直连服务器。每个机架一台 |
| Layer 2 | Spine | 连接同一 Pod 内所有 Leaf,全网状连接 |
| Layer 3 | Core | 连接不同 Pod,跨 Pod 流量经过 Core |
流量路径的跳数:
- 同机架:Leaf 本地交换,0 跳
- 同 Pod 不同机架:Leaf → Spine → Leaf,2 跳
- 跨 Pod:Leaf → Spine → Core → Spine → Leaf,4 跳
Pod:可重复的构建单元
Pod 是一组 Leaf + 对应的 Spine,是 DC 扩展的基本单位。要扩容?加一个 Pod,连到 Core 就行。
用 64 端口交换机算一下规模:
Leaf:32 端口朝下连服务器 → 每机架 32 台服务器
Pod: 32 个 Leaf → 32 × 32 = 1,024 台服务器
DC: 32 个 Pod → 32 × 1,024 = 32,768 台服务器
需要更多?加一层(5-stage Clos / Super Spine),可以支撑数十万台服务器。大型互联网公司的 F16 Fabric 就是这种多级 Clos 设计。
ECMP:等价多路径
Clos 架构的核心设计原则。当 Leaf 1 发包到 Leaf 4,有多条等价路径经过不同的 Spine。交换机对五元组(源 IP、目的 IP、源端口、目的端口、协议)做哈希,均匀分发到所有路径。
Leaf 1 ──发包──► 要到 Leaf 4,有 4 条等价路径:
Leaf 1 ─── Spine A ─── Leaf 4 ← hash(五元组) = 0
Leaf 1 ─── Spine B ─── Leaf 4 ← hash(五元组) = 1
Leaf 1 ─── Spine C ─── Leaf 4 ← hash(五元组) = 2
Leaf 1 ─── Spine D ─── Leaf 4 ← hash(五元组) = 3
流量均匀分散 → 无瓶颈 → Spine 挂一台自动重分布
ECMP 的意义:
- 无瓶颈:流量均匀分散,没有单链路过载
- 容错:一台 Spine 挂了,流量自动重分布到剩余 Spine
- 线性扩展:需要更多带宽?加 Spine 就行
Oversubscription:超卖比
Leaf 交换机的下行带宽(面向服务器)和上行带宽(面向 Spine)的比值。
| 比例 | 含义 | 场景 |
|---|---|---|
| 1:1 | 无阻塞,所有服务器可以同时全速通信 | 延迟敏感(DB 集群、ML 训练) |
| 3:1 | 只有 1/3 的服务器带宽可以同时上行 | 通用计算(统计上够用) |
| 4:1+ | 更高超卖,便宜但有风险 | 冷存储、批处理 |
实际影响:设计服务时,把频繁通信的服务放在同一个机架或 Pod 里,避免超卖瓶颈。这就是为什么 HDFS 把一个副本放在同机架、另一个放在跨机架。
骨干网:Segment Routing
数据中心之间通过私有骨干网连接。传统 MPLS 需要每个中间路由器维护转发状态(通过 LDP/RSVP)。Segment Routing(SR)是它的进化:把完整路径编码在包头里,中间路由器只需要读取下一条指令。
MPLS 类比:坐出租车,每个路口都要问路
SR 类比: 出发前拿到完整 GPS 导航,每个路口只看下一步指令
MPLS:每个路由器都要维护转发状态
┌───┐ ┌───┐ ┌───┐ ┌───┐
│ A │───►│ B │───►│ C │───►│ D │ B,C 各自查 LDP 表决定下一跳
└───┘ └───┘ └───┘ └───┘
SR:路径编码在包头,中间路由器只执行指令
┌───┐ ┌───┐ ┌───┐ ┌───┐
│ A │───►│ B │───►│ C │───►│ D │
└───┘ └───┘ └───┘ └───┘
包头: [B, C, D] [C, D] [D] 每一跳弹出一个 SID
两种 Segment ID:
- Node SID:走最短路径到某个路由器——"去路由器 D"
- Adjacency SID:走某条特定物理链路——"走 B→C 这条线"
SRv6 是各大超大规模数据中心在推的方向。128 位的 SID 可以同时编码目的地和操作(比如"解封装"、"查 VPN 表"),比 SR-MPLS 的 20 位标签灵活得多。
负载均衡:L4 + L7 两层
数据中心里有两种本质不同的"负载均衡"协同工作。很多人把它们混为一谈,但它们解决的问题完全不同:
| ECMP(网络层) | 应用负载均衡器 | |
|---|---|---|
| 回答的问题 | 这个包走哪条物理路径? | 这个请求由哪台服务器处理? |
| 决策依据 | 五元组哈希 | 健康检查、负载、Session、URL、Header |
| 应用感知 | 零 | 完全感知 |
| 所在层 | L3/L4(交换机) | L4/L7(软件) |
Katran:基于 eBPF 的 L4 负载均衡器
Katran 不是一个独立设备——它是一个 eBPF/XDP 程序,直接跑在每台 L7 LB 服务器的网卡上。
用户请求
│
▼
┌───────────────┐
│ DC Border │
│ Router │
└──┬─────┬────┬─┘
│ │ │ ← ECMP + Anycast
▼ ▼ ▼
┌──────┐ ┌──────┐ ┌──────┐
│Server│ │Server│ │Server│ 每台都跑 Katran
│ A │ │ B │ │ C │
└──┬───┘ └──┬───┘ └──┬───┘
┌───────────┤ │ │
│ │ │ │
▼ ▼ ▼ ▼
┌─────────┐ ┌─────────────────────────────┐
│本机处理 │ │ 封装转发到其他后端服务器 │
│Proxygen │ │ │
│ (L7 LB) │ │ ┌──────┐┌──────┐┌──────┐ │
└─────────┘ │ │App 1 ││App 2 ││App 3 │ │
│ └──┬───┘└──┬───┘└──┬───┘ │
└─────┼───────┼───────┼────────┘
│ │ │
└───────┼───────┘
│
▼
DSR:响应直接回用户
(绕过 Katran)
三个关键设计决策:
- XDP/eBPF:包在网卡驱动层处理,不经过内核网络栈。单机可以达到数百万 PPS
- 一致性哈希:同一个 TCP 连接始终映射到同一个后端。后端变化时,只有小部分连接需要重映射
- DSR(Direct Server Return):响应从后端服务器直接返回用户,绕过 Katran。因为响应比请求大得多(想想一个 HTTP 请求 vs 一个带图片的页面),DSR 避免了 LB 成为带宽瓶颈
| 硬件 LB (F5) | LVS/IPVS | Katran (eBPF) | |
|---|---|---|---|
| 性能 | 高但有上限 | 中高 | 数百万 PPS |
| 成本 | $100K+ / 台 | 免费 | 免费 |
| 单点故障 | 是 | 看部署方式 | 否(每台服务器都跑) |
| 扩展方式 | 买更多设备 | 加服务器 | 加服务器 |
Overlay 网络:容器怎么通信
你的代码跑在容器里,容器有虚拟 IP,物理网络不认识这些 IP。Overlay 网络桥接这个鸿沟。
Underlay vs Overlay
| 层 | 是什么 | IP | 设备 |
|---|---|---|---|
| Underlay | Clos 物理交换矩阵 | 192.168.x.x(宿主机 IP) | Leaf, Spine, Core |
| Overlay | 容器虚拟网络 | 10.244.x.x(Pod IP) | veth, bridge, VXLAN |
Linux 虚拟网络设备
- veth pair:一根虚拟网线连接两个网络命名空间。Docker/K8s 创建一对:一端进容器(eth0),一端留在宿主机连到 bridge
- bridge:宿主机上的虚拟二层交换机。同主机的容器通过它通信,不经过物理网络
- VXLAN device (VTEP):容器要访问另一台主机上的容器时,VXLAN 设备把原始帧封装在 UDP 包(端口 4789)里,用宿主机 IP 作为外层地址。物理网络正常用 ECMP 路由,目的主机的 VXLAN 设备解封装后交给目标容器
一个包的完整旅程:跨主机容器通信
Host 1 (192.168.1.10) Host 2 (192.168.2.20)
┌──────────────────────────┐ ┌──────────────────────────┐
│ │ │ │
│ ┌──────────────────┐ │ │ ┌──────────────────┐ │
│ │ Container A │ │ │ │ Container B │ │
│ │ IP: 10.244.1.5 │ │ │ │ IP: 10.244.3.8 │ │
│ │ │ eth0 │ │ │ │ eth0 │ │ │
│ └──────┼─────────────┘ │ │ └────────────┼──────┘ │
│ │ veth pair │ │ veth pair │ │
│ ▼ │ │ ▼ │
│ ┌─────────────┐ │ │ ┌─────────────┐ │
│ │ Bridge │ │ │ │ Bridge │ │
│ │ (cni0) │ │ │ │ (cni0) │ │
│ └──────┬──────┘ │ │ └──────┬──────┘ │
│ │ 目的地不在本机 │ │ 解封装后转发│ │
│ ▼ │ │ ▲ │
│ ┌─────────────┐ │ │ ┌─────────────┐ │
│ │ VXLAN (VTEP)│ │ │ │ VXLAN (VTEP)│ │
│ │ 封装 UDP:4789│ │ │ │ 解封装 │ │
│ └──────┬──────┘ │ │ └──────┬──────┘ │
│ │ │ │ ▲ │
│ ▼ │ │ │ │
│ ┌─────────────┐ │ │ ┌─────────────┐ │
│ │ 物理网卡 │ │ │ │ 物理网卡 │ │
│ └──────┬──────┘ │ │ └──────┬──────┘ │
└─────────┼──────────────────┘ └──────────────────┼────────┘
│ ▲
│ Clos 物理网络 │
│ ┌──────────────────────────┐ │
└────►│ Leaf → Spine → Leaf │──────────────────────┘
│ (ECMP 基于外层 IP 路由) │
└──────────────────────────┘
为什么用 VXLAN 而不是 VLAN?VLAN 只支持 4,096 个 ID(12 位)。VXLAN 用 24 位 VNI,支持 1600 万个虚拟网络——多租户 DC 环境必需的。
K8s CNI 插件对比
| CNI | 方式 | 性能 | 复杂度 |
|---|---|---|---|
| Flannel (VXLAN) | VXLAN 封装 | 好(有封装开销) | 简单 |
| Calico (IPIP) | IP-in-IP 封装 | 更好(更轻的封装) | 中等 |
| Calico (BGP) | 直接路由,无封装 | 最好(原生路由) | 需要支持 BGP 的交换机 |
| Cilium (eBPF) | eBPF,绕过 bridge/iptables | 优秀 | 中等 |
超大规模数据中心大概率用的是类似 Calico BGP 的直接路由方案——物理交换机知道容器子网的路由,不需要封装,性能最好,但需要网络基础设施的深度集成。
DC 内部 vs 公网:两个完全不同的世界
这是最反直觉的部分。教科书上的 TCP/IP 是为公网设计的——高延迟、高丢包、未知带宽。DC 内部是一个完全不同的环境,需要在每一层做不同的优化。
| 属性 | 公网 | DC 内部 |
|---|---|---|
| RTT | 10-200ms | 10-100μs(低 1000 倍) |
| 丢包率 | 1-5% | <0.001% |
| 带宽 | 未知、可变 | 已知(25G/100G/400G) |
| 跳数 | 10-20 | 2-4 |
| 信任 | 不可信 | 完全可控 |
传输层的差异
| 公网做法 | DC 做法 | 为什么 |
|---|---|---|
| TCP:丢包就砍半窗口 | DCTCP:基于 ECN 的细粒度速率调整 | 丢包信号太粗暴;ECN 提供早期、按比例的拥塞反馈 |
| 每个请求建 TCP 连接 | 连接池 + gRPC/HTTP2 多路复用 | 百万级服务间调用,握手开销不可接受 |
| 什么都用 TCP | ML 训练用 RDMA/RoCE | GPU 梯度交换需要 <2μs 延迟,绕过 CPU |
内核绕过技术
内核网络栈通过上下文切换和协议处理增加延迟。DC 工作负载直接绕过它:
| 技术 | 原理 | 用途 |
|---|---|---|
| DPDK | 用户态包处理,完全绕过内核 | 高性能 LB、NFV |
| RDMA/RoCE | 网卡直接读写远端内存,CPU 不参与 | GPU 训练、分布式存储 |
| XDP/eBPF | 在网卡驱动层拦截包(内核最早的 hook 点) | Katran LB、Cilium |
| io_uring | 异步 I/O,减少系统调用开销 | 高性能存储引擎 |
Incast:DC 特有的拥塞模式
一个请求扇出到 N 台服务器(比如分布式查询),N 台同时响应,瞬间压垮请求方 Leaf 交换机的缓冲区。结果:丢包、TCP 重传、延迟飙升。
解法:
- ECN:交换机缓冲区到达阈值(比如 30%)时标记包,发送方在丢包发生前就降速
- DCTCP:根据 ECN 标记包的比例按比例降速,而不是二元的"砍半"
- 应用层错开:给响应时间加 jitter,避免同步爆发
完整的请求路径
现在把所有层串起来:
┌──────────┐
│ 用户手机 │ 东京
│ │
└────┬─────┘
│ DNS → Anycast IP
▼
┌──────────┐
│ PoP 东京 │ TLS 终止
│ │ 静态内容 → CDN 直接返回
└────┬─────┘
│ 动态请求
▼
═══════════════════════ 骨干网 (SR/MPLS, 私有光纤, ~120ms)
│
▼
┌──────────────────────────────────────────────────────┐
│ 新加坡 DC │
│ │
│ ┌──────────────┐ │
│ │ Border Router │ ECMP + Anycast │
│ └──────┬───────┘ │
│ ▼ │
│ ┌──────────────┐ │
│ │ Katran │ L4 LB (XDP/eBPF) │
│ │ 一致性哈希 │ │
│ └──────┬───────┘ │
│ ▼ │
│ ┌──────────────┐ │
│ │ Proxygen │ L7 LB (TLS, URL路由, 健康检查) │
│ └──────┬───────┘ │
│ ▼ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ App Server │──────►│ 其他服务 │ │
│ │ (容器) │ VXLAN │ (跨Pod/跨主机) │ │
│ └──────┬───────┘ └──────────────┘ │
│ │ │
└─────────┼────────────────────────────────────────────┘
│
│ DSR:响应直接返回用户,绕过 Katran
▼
┌──────────┐
│ 用户手机 │
└──────────┘
从用户发出请求到收到响应,经过了 DNS、CDN、PoP、骨干网、边界路由、ECMP、eBPF 四层 LB、七层 LB、容器 overlay 网络——每一层都在解决一个特定的问题,每一层的设计都受限于物理定律(光速、带宽、缓冲区大小)。
总结
几个值得记住的关键直觉:
- Clos + ECMP:用便宜的小交换机组合成大网络,通过哈希均匀分发流量。DC 网络的物理基础
- Katran + DSR:eBPF 在网卡层做负载均衡,响应绕过 LB 直接回用户。解决了传统 LB 的带宽瓶颈和单点故障
- DC RTT 比公网低 1000 倍:这个数量级差异改变了所有假设。公网的 TCP 拥塞控制、连接管理、安全模型在 DC 内部都不适用,需要专门的方案(DCTCP、RDMA、Zero Trust)
- Overlay 网络是胶水:容器的虚拟 IP 通过 VXLAN/BGP 映射到物理网络。理解这层封装/解封装,就理解了为什么跨主机容器通信比同主机慢
- Oversubscription 决定了服务部署策略:频繁通信的服务放同一个机架或 Pod,不是"优化",而是避免超卖瓶颈的必要措施