Docker Swarm全局模式下MongoDB副本集搭建及初始化问题求助
看起来你卡在了Swarm全局服务下Mongo副本集的初始化环节,而且不想依赖宿主机IP——这其实是Swarm服务发现和Mongo副本集网络配置的典型问题,我来一步步帮你解决:
1. 先搞定基础网络配置(核心前提)
首先得给Mongo服务创建一个自定义Overlay网络,Swarm默认的ingress网络虽然能用,但自定义网络的服务发现更稳定,还能让容器之间直接通过DNS通信:
docker network create --driver overlay --attachable mongo-overlay
接着重新部署你的全局Mongo服务,把它加入这个网络,同时一定要让Mongo绑定所有IP(不然只会监听localhost,容器间根本连不上):
docker service create \ --name mongo1 \ --mode global \ --network mongo-overlay \ --env MONGO_INITDB_ROOT_USERNAME=admin \ --env MONGO_INITDB_ROOT_PASSWORD=yourpassword \ mongo:latest \ mongod --bind_ip_all --replSet rs0
这里的--bind_ip_all是关键,它让Mongo监听容器内的所有网卡,确保其他节点的Mongo容器能通过Swarm内部网络访问它。
2. 获取Swarm内部的Mongo实例地址
全局服务的每个实例在Swarm里都有唯一的可解析DNS名称,你可以先查看所有任务ID:
docker service ps mongo1
输出会类似这样:
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS abc123 mongo1.1 mongo:latest node1 Running Running 5 minutes ago def456 mongo1.2 mongo:latest node2 Running Running 5 minutes ago ghi789 mongo1.3 mongo:latest node3 Running Running 5 minutes ago
在同一个overlay网络里,你直接用mongo1.1、mongo1.2、mongo1.3就能访问对应节点的容器,Swarm的DNS服务会自动解析这些名称到容器的内部IP。
你可以进入其中一个容器验证下连通性:
docker exec -it $(docker ps -q --filter name=mongo1.1) nslookup mongo1.2
如果能正常解析到IP,说明网络没问题。
3. 用Swarm内部地址初始化副本集
随便选一个Mongo容器进入Mongo Shell,比如node1上的实例:
docker exec -it $(docker ps -q --filter name=mongo1.1) mongosh -u admin -p yourpassword
然后执行副本集初始化命令,注意这里用的是Swarm内部的实例地址,完全不用宿主机IP:
rs.initiate({ _id: "rs0", members: [ { _id: 0, host: "mongo1.1:27017" }, { _id: 1, host: "mongo1.2:27017" }, { _id: 2, host: "mongo1.3:27017" } ] })
执行完后输入rs.status()查看状态,要是所有节点都显示PRIMARY或SECONDARY,就说明初始化成功了。
4. 解决端口异常问题
你提到的“mongo container port is no”应该是指端口无法访问的问题,这里要注意几个点:
- 副本集内部通信不用映射宿主机端口,依赖overlay网络的27017端口就行,映射宿主机端口反而容易引发冲突或混淆。
- 确保Mongo启动参数没限制端口,默认就是27017,加上
--bind_ip_all后,容器内的27017端口会对整个overlay网络开放。 - 检查Swarm节点之间的防火墙,要确保overlay网络所需的端口(默认4789 UDP、7946 TCP/UDP)是开放的,不然跨节点的容器没法通信。
额外优化:用节点主机名做Mongo实例地址(可选)
如果你觉得任务ID不够直观,可以在部署服务时把容器主机名设为宿主机的主机名,这样副本集地址更易读:
docker service create \ --name mongo1 \ --mode global \ --network mongo-overlay \ --hostname="{{.Node.Hostname}}" \ --env MONGO_INITDB_ROOT_USERNAME=admin \ --env MONGO_INITDB_ROOT_PASSWORD=yourpassword \ mongo:latest \ mongod --bind_ip_all --replSet rs0
这样每个容器的主机名就是node1、node2、node3,初始化时直接用这些名称就行:
rs.initiate({ _id: "rs0", members: [ { _id: 0, host: "node1:27017" }, { _id: 1, host: "node2:27017" }, { _id: 2, host: "node3:27017" } ] })
Swarm默认的DNS服务支持解析节点主机名,你可以在容器里用nslookup node1验证。
内容的提问来源于stack exchange,提问作者nitesh

