10 KiB
写 Docker 的朋友大概都经历过一个阶段:在单机上玩得飞起 docker run 用得滚瓜烂熟。但当业务扩展,需要两台服务器上的容器直接“对话”时,问题就来了。
默认的 bridge 网络只能管自家一亩三分地,跨主机通信怎么办?难道只能靠繁琐的端口映射和复杂的 iptables 转发?
当然不是。Docker 早已为我们准备了三把利剑:Overlay、Macvlan 和 IPvlan。
Docker官网的几种网络模式
今天这篇文章,我们就从原理到实战,彻底把这三者讲清楚,顺便带你避开那些文档里不常说的“坑”。
一、 为什么我们需要跨主机通信?
在默认的 bridge 模式下,Docker 容器通过 NAT(网络地址转换)访问外部。外部要访问容器,必须通过端口映射。
痛点很明显:
- 端口管理地狱:几十个微服务,端口号记不住,还容易冲突。
- 性能损耗:NAT 转换在大流量下是 CPU 杀手。
- IP 不可见:容器没有真实局域网 IP,传统的监控、服务发现工具无法直接通过 IP 管理容器。
我们想要的是:容器像虚拟机一样,拥有局域网内的独立 IP,不仅跨主机能通,还能和物理机直接互联。
二、 方案一:Overlay 网络 (Docker Swarm 的默认选择)
如果你用过 Docker Swarm 或 K8s (Flannel/Calico),对 Overlay 应该不陌生。
1. 原理核心:VXLAN 隧道
Overlay 网络是在现有的物理网络(Underlay)之上,构建了一个虚拟网络。
- 封装:当主机 A 的容器发包给主机 B 的容器时,Docker 会把原始数据包封装在一个 VXLAN 包(UDP 4789端口)里。
- 传输:这个 UDP 包像普通数据一样在物理网络传输。
- 解封:到达主机 B 后,Docker 拆开 UDP 包,拿出原始数据,塞给目标容器。
通俗理解: 就像寄快递。原本的信件(容器数据)被装进了一个快递盒(VXLAN 封装),快递员(物理网络)只看快递单(物理 IP),不管盒子里装啥。到了目的地,拆盒取信。
2. 实战测试
Overlay 需要依赖键值存储(KV Store)来同步网络信息,最简单的办法是使用 Docker Swarm 模式(无需安装额外组件),早期的Docker版本(17.03前)配置中也可使用外部组件如consul、etcd等同步信息
环境准备:
- Node 1 (Manager): 172.21.14.5
- Node 2 (Worker): 172.21.14.6
- Docker环境 (我的 Docker version 26.1.5+dfsg1, build a72d7cd , 带docker swarm)
核心端口要求(Swarm 集群必需)
- 2377/tcp:Swarm 集群管理通信
- 7946/tcp/udp:节点间通信(覆盖网络发现)
- 4789/udp:Overlay 网络数据传输(VXLAN)
Step 1: 初始化 Swarm (node1) , 加入Swarm集群(node2)
# node1上 Manager
docker swarm init --advertise-addr 172.21.14.5
# 执行后会给出一个 token 命令,复制该命令到 Node 2 执行即可加入集群 ,也会打开了相应的端口
# node2上 Worker
docker swarm join --token SWMTKN-1-028glf3487mka3134oot1z2rxfmbisqj7jaqan9gayvmwjjcpl-e5hpu73a6fvegdm5xi10otl15 172.21.14.5:2377
# node1上 Manager
docker node ls
运行docker swarm init后
执行完后已查看到一个节点
加入docker
已经有两个节点了
**Step 2: 创建 Overlay 网络,**Swarm 会自动同步到node2 工作节点
docker network create -d overlay --attachable my-overlay
注意:加上 --attachable 才能让独立的容器(非 Swarm Service)也能使用这个网络。
Step 3: 跨主机验证 在 Node 1 运行容器:
docker run -itd --name app1 --network my-overlay alpine
在 Node 2 运行容器:
docker run -itd --name app2 --network my-overlay alpine
进入 Node 1 的容器 ping Node 2 的容器:
docker exec -it app1 ping app2
node1容器
node2容器测试
结果: 跨主机 Ping 通!且支持 DNS 解析(直接 Ping 容器名)。
Step 4: 查看验证通信原理(vxlan)
# 通过查看详情检查是vxlan
docker network inspect my-overlay
# 主机tcpdump抓包通信过程,之前说到的4789端口
tcpdump -i eth0 port 4789 -c 10 -nn
docker network inspect查看
tcpdump 抓取vxlan 4789端口数据包
Step 5: 使用service进行验证
# node1上创建service1 并进行验证,并调度到node1节点
docker service create --name service1 --network my-overlay --replicas 3 --constraint node.hostname==iZt4n4moivyl2vcl14obxcZ nginx:alpine
# 查看service
docker service ls
# 查看容器
docker ps
# node1上创建service2 并进行验证,并调度到node2节点
docker service create --name service2 --network my-overlay --replicas 3 --constraint node.hostname==iZt4n4moivyl2vcl14obxdZ nginx:alpine
# 查看service
docker service ls
# 在node2上查看容器,看调度情况
docker ps
创建两个service进行验证
在node1上验证service2的连通性和nginx服务
三、 方案二:Macvlan (性能之王)
在验证了overlay后,看到官方还有IPvlan 、Macvlan,也想着来验证下。(云服务器测试可能不通)
官方network
Overlay 虽然方便,但 VXLAN 封包解包有 CPU 损耗。如果你的应用对网络延迟极度敏感(如高频交易、VoIP),Macvlan 是更好的选择。
1. 原理核心:子接口与独立 MAC
Macvlan 允许你在物理网卡(如 eth0)上创建多个“子接口”,每个子接口都有独立的 MAC 地址和 IP 地址。
- 从交换机来看,你的服务器仿佛变身成了接了 N 台设备的“交换机”。
- 容器直接通过 Layer 2(数据链路层)通信,无需 NAT,性能几乎等同于物理机。
2. 实战测试
前提: 你的物理网卡需要支持(大部分都支持),且交换机开启混杂模式(Promiscuous Mode,如果在 VMware 虚拟机里测试,记得在 vSwitch 安全设置里开启“混杂模式”)。
Step 1: 创建 Macvlan 网络 (两台主机都要执行) 假设物理网段是 △192.168.1.0/24△,网关 △192.168.1.1△,我们划出一部分 IP 给容器用。
# 创建macvlan网络
docker network create -d macvlan \
--subnet=172.18.200.0/24 \
--gateway=172.18.200.254 \
-o parent=enp3s0 macvlan-net
9460256b68801cf46f5c21dff59c7a79d9a757010c5d6cc0414e5a1474d5433b
Step 2: 运行容器
# 创建一个172.18.200.222的容器
docker run -d --name app \
--network macvlan-net \
--ip=172.18.200.222 \
nginx:1.23-alpine
运行macvlan网络和容器
Step 3: 验证 容器互 Ping、容器 Ping 外部物理机均可通, 这个过程可能需要一点儿时间。
局域网访问macvlan ip
在物理机上进行验证
四、 方案三:IPvlan (云环境救星)
Macvlan 有个缺点:每个容器都要一个新的 MAC 地址。很多公有云厂商(AWS, 阿里云等)的虚拟交换机有限制,如果不认识这个 MAC 地址,直接丢弃数据包。这就导致 Macvlan 在云上往往跑不通。
IPvlan 应运而生。
1. 原理核心:共享 MAC
IPvlan 也有 L2 和 L3 两种模式,最常用的是 L2。
- 所有容器共享宿主机物理网卡的 MAC 地址。
- 虽然 MAC 一样,但 IP 不同,Linux 内核根据 IP 地址进行流量分发。
2. 实战测试 (L2 模式)
该命令需要 Linux 内核 >= 4.2。
Step 1: 创建 IPvlan 网络 语法几乎和 Macvlan 一模一样,只是驱动变了。我仍然在
# 创建一个ipvlan网络,创建之前要先删除之前的Macvlan(因为网络相同冲突了)
docker network create -d ipvlan \
--subnet=172.18.200.0/24 \
--gateway=172.18.200.254 \
-o parent=enp3s0 \
-o ipvlan_mode=l2 \
ipvlan-net
Step 2: 运行与验证
# 运行一个ipvlan-net 容器
docker run -d --name app \
--network ipvlan-net \
--ip=172.18.200.222 nginx:1.23-alpine
本机验证ip及网络
五、 总结:我该选哪个?
为了方便大家决策,我做了一张对比表:
| 特性 | Overlay | Macvlan | IPvlan (L2) |
|---|---|---|---|
| 应用场景 | 容器编排 (Swarm/K8s) | 传统应用迁移、高性能需求 | 公有云环境、MAC受限环境 |
| 配置难度 | 简单 (Swarm原生) | 中等 (需规划网段) | 中等 |
| 性能 | 有损耗 (VXLAN封装) | 极高 (接近物理机) | 极高 |
| 网络层级 | 纯虚拟网络 | 物理网络二层打通 | 物理网络二层打通 |
| MAC地址 | 虚拟 MAC | 每个容器独立 MAC | 共享宿主机 MAC |
| 最大缺点 | 只有 Docker 内部互通 | 容易耗尽交换机 MAC 表 | 旧内核不支持 |
极简决策树:
- 如果在云服务器上跑,且不能申请辅助网卡 -> 选 Overlay。
- 如果是自建机房,追求极致性能 -> 选 Macvlan。
- 如果是自建机房,但交换机限制了 MAC 地址数量 -> 选 IPvlan。
最后一步:
看完原理和实验,建议你找两台虚拟机(VirtualBox 或 VMWare),按照文章里的命令敲一遍。网络这东西,纸上得来终觉浅,绝知此事要 Ping 通!
















