容器跨主机网络通信
在微服务时代,容器化部署已成为主流。在分布式部署环境中,如何实现跨主机的容器通信始终是架构设计中必须解决的重要问题。本文将从 Overlay 网络的基本概念出发,介绍 Flannel 后端实现方案,并重点解析 Flannel UDP 模式的工作原理及其性能瓶颈。
一、Overlay Network 概述
1.1 基本定义
Overlay 网络是一种逻辑上构建出的虚拟网络,能够将分布在不同物理宿主机上的容器虚拟到同一个局域网络中。
-
特点:
-
不改变底层物理网络拓扑
-
提供容器间透明、互联互通的网络环境
-
简化了传统网络配置流程
-
1.2 应用场景与优缺点
- 优势:
- 部署简单:在现有物理网络上建立虚拟网络,无需专门构建专用网络设施
- 灵活扩展:容器无论运行在哪台宿主机上,都能处于同一逻辑网络内
- 局限性:
- 封装开销:数据包在传输过程中需要经过额外封装与解封装,可能增加延迟和 CPU 负载
- 性能瓶颈:在高并发或大流量场景下,多层封装容易成为整体性能的瓶颈
二、Flannel 后端实现方案
Flannel 是 CoreOS 推出的容器网络解决方案,它为 Docker、Kubernetes 等容器管理系统提供了便捷的网络互联功能。Flannel 本身是一个网络抽象框架,支持多种后端实现方式,主要包括以下三种模式:
2.1 VXLAN 模式
- 原理: 利用 VXLAN 协议在物理网络上构建虚拟二层广播域,将数据包封装在内核态完成。
- 优势: 内核态封装提高了性能,同时降低了用户态与内核态切换的开销。
2.2 host-gw 模式
- 原理: 直接利用宿主机的路由表实现容器间路由,避免额外的封装过程。
- 优势: 性能较高,适合网络拓扑环境稳定、配置较为简单的场景;缺点则是对底层网络要求较高。
2.3 UDP 模式
- 原理: 在用户态通过 UDP 封装实现跨主机容器通信。
- 优势与不足: 实现简单、易于理解,但因封装与解封装均在用户态执行,引入了较高的性能开销。
- 历史地位: 虽然实际生产环境中已逐步被 VXLAN 模式取代,但 UDP 模式是理解容器跨主通信原理的最简单案例。
三、Flannel UDP 模式剖析
通过两台宿主机的示例,介绍 UDP 模式下的具体实现过程和关键技术细节。
3.1 例子:两台宿主机环境配置
如下场景:
- Node 1:
- 分配子网:
100.96.1.0/24 - docker0 网桥 IP:
100.96.1.1/24 - 某容器 container-1 IP:
100.96.1.2
- 分配子网:
- Node 2:
- 分配子网:
100.96.2.0/24 - docker0 网桥 IP:
100.96.2.1/24 - 某容器 container-2 IP:
100.96.2.3
- 分配子网:
当 container-1(IP:100.96.1.2)向 container-2(IP:100.96.2.3)发送数据包时,数据包的源 IP 为 100.96.1.2,目标 IP 为 100.96.2.3,此时由于目标地址不在 Node 1 本地子网内,数据包将按照自定义路由规则转发到 Flannel 构建的隧道。
3.2 宿主机上构建的路由规则
Flannel 会在每台宿主机上创建自定义路由规则,确保跨主机数据包能正确经由 TUN 设备传输。以 Node 1 为例,其 ip route 输出:
default via 10.168.0.1 dev eth0
100.96.0.0/16 dev flannel0 proto kernel scope link src 100.96.1.0
100.96.1.0/24 dev docker0 proto kernel scope link src 100.96.1.1
10.168.0.0/24 dev eth0 proto kernel scope link src 10.168.0.2
- 解析:
- 对于目标在本地子网(docker0 网桥所在的 100.96.1.0/24)的数据包,直接从 docker0 转发;
- 对于其它目标(例如 100.96.2.3),匹配到 100.96.0.0/16 的路由规则后,被引导到 flannel0(TUN 设备)处理。
3.3 flannel0 TUN 设备
flannel0 是一个 TUN 类型的虚拟网络设备,在整个数据传输过程中扮演关键角色:
- 工作机制:
- 内核到用户态: 当数据包通过路由进入 flannel0 后,会从内核传递给运行在用户态的 flanneld 进程。
- 用户态到内核: flanneld 对数据包进行 UDP 封装后,将数据再次注入 flannel0,交由内核网络栈根据路由转发。
- 优缺点:
- 提供了灵活性,方便用户态程序处理 IP 包;
- 但引起了多次用户态与内核态间的数据拷贝与切换,从而导致性能下降。
3.4 Flannel 子网及 Etcd 配置
在 Flannel 管理下,每台宿主机会被分配一个独立的子网,这些配置信息存储于 Etcd 中,例如:
$ etcdctl ls /coreos.com/network/subnets
/coreos.com/network/subnets/100.96.1.0-24
/coreos.com/network/subnets/100.96.2.0-24
-
映射关系:
每个子网不仅定义了容器 IP 地址的范围,还与对应宿主机的物理网络 IP 建立映射。 -
配置要求:
必须保证 Docker 网桥(docker0)的 IP 地址与 Flannel 分配的子网一致,通常通过 Docker Daemon 的--bip参数配置,如:$ FLANNEL_SUBNET=100.96.1.1/24 $ dockerd --bip=$FLANNEL_SUBNET ...
3.5 跨主机容器通信流程
上面的各个环节,容器跨主机通信的整个过程可以归纳为以下步骤:
- 数据包生成与发送:
container-1 生成一个 IP 包(源:100.96.1.2,目标:100.96.2.3),经过 docker0 网桥进入宿主机 Node 1 的内核网络栈。 - 本地路由决策:
内核根据自定义路由规则判断数据包目标不在本地 docker0 子网,而应交由 flannel0 处理。 - 进入 flanneld 用户态处理:
flannel0 将数据包传递给 flanneld 进程,flanneld 根据目标 IP 查询 Etcd 获取 container-2 所在宿主机的公网 IP(例如 10.168.0.3)。 - UDP 封装与发送:
flanneld 将 IP 包封装进 UDP 包内,通过宿主机的物理接口(如 eth0)发送至目标宿主机的 8285 端口。 - 目标宿主机解封与注入:
在 Node 2 上,flanneld 监听 8285 端口,接收到 UDP 包后解封,提取出原始 IP 包,并将其注入本地 flannel0。 - 最终数据传递:
Node 2 内核根据其路由规则,将 IP 包转发到 docker0 网桥,最终传递至 container-2,实现跨主机通信。
四、Flannel UDP 模式原理与性能问题
4.1 UDP 封装回顾
在 UDP 模式中,flanneld 利用 UDP 数据包作为传输载体,将原始 IP 包封装后发送到目标宿主机,并在对端进行解封,恢复出原始数据包。这种方式设计直观,但由于所有封装与解封操作均在用户态完成,必然带来性能上的损耗。
4.2 用户态与内核态多次切换
整个传输流程中存在至少三次用户态与内核态的切换:
- 第一次:
容器通过 docker0 将数据包送入内核态。 - 第二次:
数据包从内核态经由 flannel0 进入 flanneld 用户态进行处理。 - 第三次:
flanneld 封装 UDP 包后,再将数据包注入内核,由物理接口输出。
每一次切换都带来 CPU 上下文切换和数据拷贝的成本,进而严重影响了整体传输性能,特别是在高流量场景下这一问题尤为突出。
4.3 性能瓶颈及优化建议
性能瓶颈
- 上下文切换开销:
用户态与内核态之间频繁切换导致的 CPU 消耗明显增加。 - 数据拷贝成本:
每次数据拷贝都可能引发延迟,影响吞吐量和响应速度。 - 用户态处理局限:
用户态封装无法充分利用内核网络栈的优化机制,整体效率较低。
优化建议
- 内核态封装:
考虑采用 VXLAN 模式,通过内核态封装数据包可降低上下文切换的代价。 - 减少拷贝次数:
利用零拷贝技术优化数据传输路径,减少数据拷贝带来的延迟。 - 硬件加速:
在支持的环境下,引入硬件转发或卸载机制,进一步提高数据转发效率。

浙公网安备 33010602011771号