Docker 跨主机网络终极指南:Overlay 网络与 Consul 实战
在之前的文章中,我们探讨了 Docker 的 bridge 和 macvlan 网络。bridge 适用于单主机,而 macvlan 虽然能实现跨主机并提供高性能,但在 IP 管理和宿主机通信方面存在挑战。那么,有没有一种更通用、更易于管理的 Docker 原生跨主机网络方案呢?
答案是肯定的!这就是我们今天要深入探讨的主角:Overlay 网络。Overlay 网络是 Docker(尤其是在 Swarm 模式下)推荐的跨主机容器通信方式。它能够在现有的 IP 网络(物理或虚拟)之上创建一个虚拟的二层网络,让连接到同一个 Overlay 网络的容器(即使它们分布在不同的物理主机上)感觉就像在同一个局域网内,可以轻松地互相通信。
与 macvlan 不同,Overlay 网络不直接依赖物理网络拓扑,IP 地址管理也由 Docker 自动完成。听起来是不是很吸引人?让我们通过实战来揭开它的面纱,并了解其工作原理和注意事项。
Overlay 网络是如何工作的?
理解 Overlay 网络的核心在于两点:
- 封装 (Encapsulation): 当一个容器通过 Overlay 网络向另一个主机上的容器发送数据时,Docker Daemon 会将原始的以太网帧封装在一个 UDP 包里(通常使用 VXLAN 协议)。这个 UDP 包使用宿主机的 IP 地址作为源和目的地址,在底层的物理网络上传输。目标主机的 Docker Daemon 收到 UDP 包后,解开封装,并将原始的以太网帧交给目标容器。这就好像在现有公路(物理网络)上修建了一条专用的空中隧道(VXLAN)。
- 分布式状态存储 (Key-Value Store): 为了让不同主机上的 Docker Daemon 知道哪个容器在哪里(哪个容器 IP 对应哪个宿主机 IP),它们需要一个共享信息的地方。这就是分布式键值存储系统(如 Consul, Etcd, ZooKeeper)的作用。Docker Daemon 会将网络、端点(容器)、IP 地址等信息注册到这个存储中,并监视其变化。当你在一个节点创建 Overlay 网络或启动/停止容器时,信息会同步到 K/V store,其他节点就能感知到这些变化。
简单来说:Overlay 网络 = VXLAN 封装 + 分布式状态同步。
(注意:在 Docker Swarm 模式下,Overlay 网络管理是内置的,通常不需要手动配置外部 K/V store,Swarm Manager 会处理状态同步。但本文将演示使用外部 Consul 的传统方式,这有助于理解底层机制。)
实战:使用 Consul 搭建 Overlay 网络
前提条件:
- 至少两台安装了 Docker 的 Linux 主机(例如
docker101- 10.0.0.101,docker102- 10.0.0.102)。 - 这些主机之间网络互通(能够互相 PING 通宿主机 IP)。
- 确保防火墙允许相关端口通信(Consul 端口如 8500, 8300-8302,以及 Docker Overlay 网络使用的 UDP 端口,通常是 4789 for VXLAN)。
步骤 1:部署 Consul 服务 (作为 Key-Value Store)
我们在其中一台主机(例如 docker101)上部署 Consul 服务。为了简单起见,我们先部署单节点的 Consul。
# 在 docker101 上执行
# (可选) 如果你没有 consul:1.15.4 镜像,可以先加载
# wget http://<your-repo>/oldboyedu-consul-1.15.4.tar.gz
# docker load -i oldboyedu-consul-1.15.4.tar.gz
# 或者直接拉取
# docker pull consul:1.15.4
# 运行 Consul 容器
# --network host: 让 Consul 直接使用宿主机的网络,方便其他节点访问其端口
# -e CONSUL_BIND_INTERFACE=ens33: 让 Consul 绑定到指定的宿主机网卡 IP 上
# (请根据你的实际网卡名称修改 ens33, 如 eth0)
docker run -d \
--name dev-consul \
--network host \
--restart always \
-e CONSUL_BIND_INTERFACE=ens33 \
consul:1.15.4 agent -dev -client 0.0.0.0 -log-level info
解释与生产建议:
--network host: 在演示环境中简化了端口暴露,Consul 会监听在宿主机的0.0.0.0:8500(HTTP API/UI),0.0.0.0:8600(DNS) 等端口。生产环境警告:--network host会降低容器的网络隔离性。生产中通常建议使用bridge网络并明确映射所需端口 (-p 8500:8500等),并考虑 Consul 的安全配置(ACLs, TLS)。agent -dev: 启动开发模式的 Consul agent,数据非持久化,适合快速测试。生产环境绝不能使用-dev模式! 生产环境需要部署高可用的 Consul 集群(至少3个 Server 节点),并进行数据持久化。-client 0.0.0.0: 允许从任何 IP 访问 Consul 的 HTTP API 和 UI,方便测试。生产环境应限制访问来源。CONSUL_BIND_INTERFACE=ens33: Consul 会自动获取ens33网卡的 IP 地址用于集群通信和广播。确保该网卡 IP 是其他 Docker 节点可以访问到的。
验证 Consul 是否运行:
# 在 docker101 上执行
docker ps # 应该能看到 dev-consul 容器
ss -ntl | grep 8500 # 应该能看到监听在 8500 端口
你也可以在浏览器中访问 http://10.0.0.101:8500 查看 Consul 的 Web UI。
(可选) Consul 集群模式:
如果你想体验 Consul 集群(更接近生产),可以在其他节点上启动 agent 并加入第一个节点:
# 在其他节点 (e.g., docker102) 执行:
# docker run -d --restart always -e CONSUL_BIND_INTERFACE=eth0 consul:1.15.4 agent -dev -join=10.0.0.101
# 在 Consul UI 或使用 `docker exec dev-consul consul members` 可以查看集群成员。
注意: 即使只使用单节点 Consul 进行演示,也能实现 Docker Overlay 网络的基本功能。
步骤 2:配置 Docker Daemon 使用 Consul
现在,我们需要告诉所有参与 Overlay 网络的 Docker 主机去使用我们部署的 Consul 服务。这需要修改 Docker 的配置文件 /etc/docker/daemon.json 并重启 Docker 服务。
- 在
docker101上:
# 编辑或创建 /etc/docker/daemon.json
sudo nano /etc/docker/daemon.json
添加以下内容:
{
"cluster-store": "consul://10.0.0.101:8500",
"cluster-advertise": "ens33:2375"
}
- 在
docker102上:
# 编辑或创建 /etc/docker/daemon.json
sudo nano /etc/docker/daemon.json
添加以下内容:
{
"cluster-store": "consul://10.0.0.101:8500",
"cluster-advertise": "ens33:2375"
}
参数解释:
"cluster-store": "consul://10.0.0.101:8500": 核心配置! 指定 Docker 使用哪个 Key-Value store 来同步集群状态。这里指向我们部署的 Consul 服务的地址和端口。"cluster-advertise": "ens33:2375": 声明本 Docker Daemon 用于集群通信的地址和端口。其他节点会通过这个地址来建立 VXLAN 隧道等连接。ens33会被 Docker 解析为该网卡的 IP 地址。端口2375是一个示例,可以是其他未被占用的端口,用于 Docker 引擎间的潜在通信(虽然 Overlay 主要依赖 K/V store 和 VXLAN 端口)。确保所有节点上的cluster-advertise指向的是各自节点的可达 IP/接口和端口。 你的原始示例中用了不同端口(6666, 8888),这也可以,只要端口不冲突且格式正确即可。使用接口名(如ens33)通常更健壮,因为它会自动适应 IP 变化。
重启 Docker 服务 使配置生效(在所有修改了配置的节点上执行):
sudo systemctl restart docker
步骤 3:验证 Docker 节点是否注册到 Consul
重启 Docker 后,稍等片刻,然后检查 Consul UI 或通过 API 查看 Docker 是否注册了节点信息。
访问 Consul UI: http://10.0.0.101:8500/ui/dc1/kv/
你应该能在 Key/Value 存储下看到类似 docker/nodes/ 的路径,里面包含了已连接的 Docker 节点信息。
步骤 4:创建 Overlay 网络
现在,激动人心的时刻!在任意一个配置好 cluster-store 的 Docker 节点上创建一个 overlay 类型的网络。
# 在 docker101 上执行
docker network create \
-d overlay \
--subnet 172.30.0.0/16 \
--gateway 172.30.0.254 \
oldboyedu-overlay
参数解释:
-d overlay: 指定使用overlay网络驱动。--subnet,--gateway: 定义这个虚拟网络的 IP 地址空间和网关。这个网段应该是你内部分配的,不要与你的物理网络或其他 Docker 网络冲突。oldboyedu-overlay: 网络名称。
查看网络列表:
# 在 docker101 上执行
docker network ls
# 你会看到 oldboyedu-overlay 网络,Scope 是 global
神奇之处: 现在,切换到 docker102 节点,执行 docker network ls:
# 在 docker102 上执行
docker network ls
# 你会发现 oldboyedu-overlay 网络也出现在这里了!
这就是 cluster-store (Consul) 的作用:网络定义被存储在 Consul 中,其他连接到同一个 Consul 的 Docker Daemon 会自动同步并创建这个网络。
你也可以使用 docker network inspect oldboyedu-overlay 在任一节点查看网络详情,结果应该是一致的。
步骤 5 & 6:在不同主机上启动容器并测试通信
- 在
docker102上启动一个容器c1:
# 在 docker102 上执行
# 使用 --rm 会在退出时自动删除容器,方便测试
# 使用 -it 可以交互式进入容器 shell
docker run --rm -it --name c1 \
--network oldboyedu-overlay \
registry.cn-hangzhou.aliyuncs.com/yinzhengjie-k8s/apps:v1 sh
# 在容器 c1 的 shell 中查看 IP
ifconfig eth0
# 你会看到一个类似 172.30.x.y 的 IP 地址,这是 Docker 自动分配的
# 注意:通常还会有一个 eth1 连接到 docker_gwbridge (见后文)
- 在
docker101上启动另一个容器c2:
# 在 docker101 上执行
docker run --rm -it --name c2 \
--network oldboyedu-overlay \
registry.cn-hangzhou.aliyuncs.com/yinzhengjie-k8s/apps:v1 sh
# 在容器 c2 的 shell 中查看 IP
ifconfig eth0
# 应该分配了另一个 172.30.x.z 的 IP
- 测试跨主机通信:
在容器 c2 (docker101) 中 ping 容器 c1 (docker102) 的 IP 地址:
# 在 c2 的 shell 中执行
ping 172.30.x.y -c 3 # 将 x.y 替换为 c1 的实际 IP
# 应该能够 PING 通!
你也可以反过来在 c1 中 ping c2。
- 测试访问外网:
在任一容器中尝试访问外部网站:
# 在 c1 或 c2 的 shell 中执行
ping baidu.com -c 3
# 通常也应该能够 PING 通!
为什么能访问外网?
当你创建一个 overlay 网络时,Docker 还会自动创建一个名为 docker_gwbridge 的 bridge 网络(如果它不存在的话)。连接到 overlay 网络的容器,除了拥有 overlay 网络的接口(如 eth0),还会拥有一个连接到 docker_gwbridge 的接口(如 eth1)。docker_gwbridge 负责处理 Overlay 网络容器与外部世界(包括互联网)的通信,它会进行 NAT 转换,类似于默认的 bridge 网络。
步骤 7:(可选) 抓包观察 VXLAN
如果你想亲眼看到 VXLAN 封装,可以在宿主机上抓取 docker_gwbridge 或物理网卡的包,过滤 UDP 端口 4789(VXLAN 默认端口)。当容器间跨主机通信时,你会看到源/目的 IP 是宿主机 IP、协议是 UDP、端口是 4789 的数据包。
# 在 docker101 上执行 (当 c2 ping c1 时)
# sudo tcpdump -i ens33 -nn udp port 4789
可能遇到的问题
-
Error response from daemon: endpoint with name c1 already exists...
- 原因: 你试图在整个 Docker 集群(通过 Consul 连接的所有节点)中创建一个已经存在的容器名称
c1。Overlay 网络中的容器名称需要在集群范围内唯一(或者说,在连接到该网络的容器中唯一)。 - 解决: 为新容器选择一个不同的名称 (
--name c3)。
- 原因: 你试图在整个 Docker 集群(通过 Consul 连接的所有节点)中创建一个已经存在的容器名称
-
容器无法跨主机通信 / Overlay 网络无法同步
- 检查:
- 所有相关节点的 Docker Daemon 都配置了相同的
cluster-store指向运行中的 Consul? - Docker 服务都已重启?
- 节点间的网络连接是否通畅(宿主机 IP 互 PING)?
- 防火墙是否阻止了 Consul 端口 (8500 等) 或 VXLAN UDP 端口 (4789)?
- Consul 服务本身是否正常运行?
cluster-advertise配置是否正确指向了各节点的可达 IP/接口?
- 所有相关节点的 Docker Daemon 都配置了相同的
- 检查:
课堂练习:使用 Overlay 部署跨主机 WordPress
现在,让我们应用所学知识,将 WordPress 应用(wp 容器)和其数据库(db-wp 容器)部署在不同的 Docker 主机上,并通过 Overlay 网络连接。
# 1. 确保 Consul 运行,并且 docker101 和 docker102 的 Docker Daemon 已配置并重启
# 2. 在任一节点创建 WordPress 专用的 Overlay 网络
docker network create \
-d overlay \
--subnet 172.23.0.0/16 \
--gateway 172.23.0.254 \
oldboyedu-wp-overlay
# 3. 在 docker102 上部署 MySQL 数据库容器
docker run -d --name db-wp \
-e MYSQL_DATABASE=wordpress \
-e MYSQL_USER=linux92 \
-e MYSQL_PASSWORD=123456 \
-e MYSQL_ROOT_PASSWORD="a_very_strong_password" \ # 生产环境务必设置强密码!
-v oldboyedu-wp-db:/var/lib/mysql \ # 持久化数据库数据
--network oldboyedu-wp-overlay \ # 连接到 Overlay 网络
--restart always \
mysql:8.3.0-oracle
# 4. 在 docker101 上部署 WordPress 应用容器
docker run -d --name wp \
# 关键:直接使用数据库容器的名称! Overlay 网络自带 DNS 解析!
-e WORDPRESS_DB_HOST="db-wp" \
-e WORDPRESS_DB_USER="linux92" \
-e WORDPRESS_DB_PASSWORD="123456" \
-e WORDPRESS_DB_NAME="wordpress" \
-p 88:80 \ # 将宿主机的 88 端口映射到容器的 80 端口,以便从外部访问
-v oldboyedu-wp-files:/var/www/html/ \ # 持久化网站文件
--network oldboyedu-wp-overlay \ # 连接到 Overlay 网络
--restart always \
wordpress
验证:
在浏览器中访问 http://10.0.0.101:88(WordPress 所在主机的 IP 和映射的端口)。你应该能看到 WordPress 的安装界面,它已经通过 Overlay 网络成功连接到了位于 docker102 上的 db-wp 数据库容器!
核心优势体现: 注意 WORDPRESS_DB_HOST="db-wp"。我们不再需要关心数据库容器的 IP 地址是多少,只需要使用它的容器名称。这是 Overlay 网络(以及 Docker 的自定义 bridge 网络)提供的内置 DNS 服务发现功能,极大简化了多容器应用的配置。
Macvlan vs. Overlay 对比总结
| 特性 | Macvlan | Overlay |
|---|---|---|
| 跨主机通信 | ✓ (直接物理网络通信) | ✓ (通过 VXLAN 封装) |
| 性能 | 极高 (接近物理网卡) | 较高 (有 VXLAN 封装/解封装开销,但通常不大) |
| IP 地址管理 | 手动 或 依赖外部 DHCP (痛点) | 自动 (Docker IPAM 管理) |
| 网络集成 | 紧密 (容器是物理网络成员) | 松散 (创建虚拟网络,不依赖物理拓扑) |
| 配置复杂度 | 相对简单 (加载模块, 创建网络) | 需要 K/V Store (或 Swarm 模式) |
| 宿主机通信 | 默认不行 | 可以 (通过 docker_gwbridge) |
| 外网访问 | 默认不行 (需额外配置或连接 bridge) | 默认可以 (通过 docker_gwbridge) |
| 安全性 | 依赖物理网络防火墙 | 可选加密 (--opt encrypted), 网络隔离性更好 |
| 适用场景 | 需极高性能、容器需暴露在物理网络 | 通用跨主机场景、微服务、集群环境 |
Docker 网络不足与发展方向
你提到的“docker 网络不足”主要指原生 Docker(非 Swarm 或 K8s)在管理动态 IP 方面的问题:
- IP 地址可能变化: 使用
overlay或bridge网络时,容器重启后,Docker 可能会为其分配一个新的 IP 地址。 - 依赖 IP 的配置脆弱: 如果应用配置文件硬编码了其他容器的 IP 地址,一旦 IP 变化,服务就会中断。
解决方案/发展方向:
- 服务发现 (Service Discovery): 这正是 Overlay 网络(和自定义 Bridge 网络)内置 DNS 的价值所在!始终通过容器/服务名称来连接,而不是 IP 地址。 这是现代容器化应用设计的核心原则。
- 容器编排工具 (Orchestration): 对于生产环境,强烈建议使用 Docker Swarm 或 Kubernetes。
- Docker Swarm: 是 Docker 官方的编排工具,原生集成 Overlay 网络管理(无需外部 Consul),提供服务抽象、负载均衡、滚动更新、服务发现等功能,极大简化了多容器应用的部署和管理。
- Kubernetes (K8s): 是目前业界标准的容器编排平台,拥有更强大的功能和生态。K8s 使用 CNI (Container Network Interface) 插件(如 Calico, Flannel, Cilium 等)来实现网络,其中很多也使用了 Overlay 技术 (VXLAN, Geneve) 或 BGP 等其他技术,并提供更完善的服务发现 (CoreDNS)、网络策略 (NetworkPolicy) 等高级功能。
简单来说,虽然我们今天学习了如何手动配置 Overlay 网络,但在真实的生产环境中,你会更多地依赖 Docker Swarm 或 Kubernetes 来为你处理这些底层的网络配置和 IP 管理问题。
结论
Docker Overlay 网络是实现容器跨主机通信的强大而灵活的解决方案。通过 VXLAN 封装和分布式状态存储 (如 Consul),它创建了一个独立于物理网络的虚拟网络层,简化了 IP 管理,并提供了内置的服务发现能力。
虽然 macvlan 在特定高性能场景下有优势,但 Overlay 网络通常是更通用、更易于管理的选择,尤其是在需要部署分布式应用或微服务时。
掌握 Overlay 网络是理解现代容器编排(如 Swarm 和 Kubernetes)网络模型的重要一步。希望这篇详细的实战指南能帮助你自信地在多主机环境中连接你的容器!
如果你在实践中遇到任何问题,或者有关于 Docker 网络的心得体会,欢迎在评论区分享交流!
浙公网安备 33010602011771号