Docker容器跨主机访问配置

背景

使用容器化的软件搭建多节点集群时,不可避免要解决容器之间的网络通信问题,Docker容器默认的网络设置只允许同一宿主机上的容器间通信,想要让两个不同主机上的容器能够通信,通常有以下几种做法:

  1. 使用Host模式,容器复用宿主机IP通信,这是最容易的方式,不需要任何配置,而且网络性能最好,但无网络隔离性。
  2. 暴露端口,容器暴露需要监听的端口,容器之间相互访问宿主机IP+端口,这就涉及宿主机IP和容器IP映射的问题,对于一些稍复杂的软件,考虑不周就无法访问。
  3. Overlay网络,利用Docker Swarm通过VXLAN隧道在物理网络之上构建虚拟二层网络,使容器IP跨节点直通,但这引入其他网络插件依赖,并且有部分用户环境中会禁用VXLAN协议。

本文尝试使用自定义网桥,仅通过主机静态路由,使不同主机的容器网段互通。


基本原理

两台宿主机:

  • 主机A
    • 宿主机IP:192.168.0.1
    • 容器网段:10.0.1.0/24
  • 主机B
    • 宿主机IP:192.168.0.2
    • 容器网段:10.0.2.0/24

两个宿主机上的Docker容器使用不同网段避免了IP冲突。
在宿主机定义路由规则:

  1. 所有目的地址为10.0.1.0/24的包都被转发到主机A上
  2. 所有目的地址为10.0.2.0/24的包都被转发到主机B上

数据包在两个容器间的传递过程如下:
主机A上容器数据包首先发给docker0,然后通过查找主机A上的路由得知需要将数据包发给主机B,数据包到达主机B后再转发给主机B的docker0,最后将数据包转到主机B的容器中。


配置步骤

1. 创建Docker自定义网络

在每台主机上创建不重叠的子网:

# 在主机A上创建网络
docker network create --subnet 10.0.1.0/24 netA

# 在主机B上创建网络
docker network create --subnet 10.0.2.0/24 netB

2. 开启宿主机IP转发

在两台宿主机上启用内核IP转发:

sysctl -w net.ipv4.ip_forward=1
# 永久生效(可选)
echo "net.ipv4.ip_forward=1" | tee -a /etc/sysctl.conf

3. 添加静态路由

  • 在主机A上
    指向主机B的容器网段(10.0.2.0/24)的流量发送到主机B的IP:

    ip route add 10.0.2.0/24 via 192.168.0.2
  • 在主机B上
    指向主机A的容器网段(10.0.1.0/24)的流量发送到主机A的IP:

    ip route add 10.0.1.0/24 via 192.168.0.1

4. 配置防火墙规则

允许宿主机间转发流量(以iptables为例):

# 在主机A和主机B上执行
# 允许宿主机(通过物理网卡 eth0)与 Docker 容器网络(通过虚拟网桥 docker0)之间的双向流量转发
iptables -A FORWARD -i eth0 -o docker0 -j ACCEPT
iptables -A FORWARD -i docker0 -o eth0 -j ACCEPT
# 启用SNAT(可选,解决非对称路由)
iptables -t nat -A POSTROUTING -s 10.0.1.0/24 ! -d 10.0.1.0/24 -j MASQUERADE

注意

  • 替换 eth0 为主机实际网卡名(如 ens33)。
  • 替换 docker0 为创建netA/netB容器子网网卡名(如 br-e981899c7a90)。
  • 若使用 firewalld/ufw,需放行容器网段和宿主机间通信。

5. 测试容器互通

  • 在主机A启动容器:
    docker run --rm -it --net netA ubuntu:24.04 /bin/bash
  • 在主机B启动容器:
    docker run --rm -it --net netB ubuntu:24.04 /bin/bash
  • 在容器中相互访问:
    ping 10.0.1.2

故障排查

  1. 检查路由规则
    ip route show  # 确认静态路由存在
  2. 检查防火墙
    iptables -L -v -n  # 查看FORWARD链是否放行
  3. 抓包诊断
    tcpdump -i eth0 host 192.168.0.1  # 在主机A抓包观察流量