默认bridge网络不支持稳定主机名解析且IP易变,MongoDB副本集要求成员地址长期固定可解析,故必须用自定义桥接网络显式配置ipam子网、网关及各容器固定ipv4_address,并在rs.initiate()中使用IP而非服务名注册。

直接用 docker-compose.yml 定义桥接网络 + 副本集容器是可行的,但必须手动指定 IP、禁用默认 DNS 解析干扰、并确保启动顺序和健康检查到位——否则副本集初始化会因节点互相解析失败或连接超时而卡住。
为什么不能只靠默认 bridge 网络跑 MongoDB 副本集
默认 bridge 网络不支持容器名自动解析(web 能 ping 通 db 是因为 Docker Compose 自动创建了项目级自定义网桥,不是默认 bridge)。而 MongoDB 副本集成员必须用**可稳定解析的主机名或固定 IP** 注册,否则 rs.initiate() 会拒绝加入或后续心跳失败。
- 默认
bridge下,容器只能靠 IP 通信,且 IP 每次重启可能变 -
docker0网桥的子网(如172.17.0.0/16)可能与你本地网络冲突,导致 mongos 或客户端连不上 - 没有
ipam配置时,无法为每个 MongoDB 实例分配固定 IP,而副本集要求成员地址长期稳定
如何用 docker-compose 定义带固定 IP 的桥接网络
关键是在 networks 块中显式声明 ipam,并为每个服务绑定 ipv4_address。不要省略 gateway,否则某些宿主机(尤其是 CentOS/RHEL)上容器可能无法访问外网。
- 子网选
172.21.0.0/16这类冷门段,避开172.17.0.0/16(默认docker0)和常见局域网段(如192.168.1.0/24) - 网关必须设为子网第一个可用地址,例如
172.21.0.1 - 每个 MongoDB 服务在
networks下加ipv4_address,值必须落在subnet范围内且不重复 -
driver: bridge可省略,但显式写出更清晰
networks:
mongo-net:
driver: bridge
ipam:
config:
- subnet: 172.21.0.0/16
gateway: 172.21.0.1
如何让三个 MongoDB 容器用固定 IP 组成副本集
每个容器必须:① 启动时监听 0.0.0.0:27017;② 以自身固定 IP 注册进副本集;③ 等待其他节点就绪再执行 rs.initiate() 或 rs.add()。不能依赖服务名(如 mongo1),因为副本集配置里写的是 IP。
- 用
command覆盖默认启动,调用 shell 脚本做「等待 + 初始化」逻辑 - 第一个节点(primary)执行
rs.initiate(),传入含全部三个host:port的配置对象 - 后两个节点执行
rs.add()加入,不能自己调rs.initiate() - 所有容器的
hostname必须设为对应 IP(或用--add-host),否则mongod日志里会报getaddrinfo failed
示例节选(mongo1 服务):
mongo1:
image: mongo:6
hostname: mongo1
command: >
bash -c '
echo "Waiting for mongo2 and mongo3...";
until nc -z mongo2 27017 && nc -z mongo3 27017; do sleep 2; done;
mongod --bind_ip_all --replSet rs0 --port 27017 &
sleep 5;
mongo --host 172.21.0.10 --eval "
rs.initiate({
_id: \"rs0\",
members: [
{ _id: 0, host: \"172.21.0.10:27017\" },
{ _id: 1, host: \"172.21.0.11:27017\" },
{ _id: 2, host: \"172.21.0.12:27017\" }
]
})
";
wait'
networks:
mongo-net:
ipv4_address: 172.21.0.10
容易被忽略的坑:Docker DNS 与 MongoDB 主机名注册冲突
Docker 自动给容器注入 /etc/hosts 和 DNS 记录,但 MongoDB 副本集成员注册时用的是你传给 rs.initiate() 的字符串。如果这里写 mongo1:27017,而宿主机或容器内又没配好 mongo1 解析,副本集状态就会卡在 STARTUP2 或反复断连。
- 最稳做法:全部用
ipv4_address(如172.21.0.10)注册,别用服务名 - 如果坚持用服务名,必须在每个容器的
extra_hosts里硬编码映射,例如:- "mongo1:172.21.0.10" - 启动后立刻进容器执行
mongo --host 172.21.0.10 --eval "rs.status().members",确认所有成员stateStr是PRIMARY或SECONDARY,而不是UNKNOWN
IP 地址池本身不是难点,难的是让 MongoDB 进程真正信任并使用这些 IP —— 它不看 Docker 网络配置,只认你喂给它的字符串。











