Docker部署MongoDB 7副本集后客户端无法连接完整集群问题
问题:Docker部署MongoDB三节点副本集后,客户端无法连接整个副本集
场景描述
在基于Arch的EndeavourOS 2023.05.28系统本地,使用Portainer Community 2.19.4管理Docker容器,部署了带用户名密码认证和密钥文件的MongoDB 7.0.5三节点副本集(1主2从),对应的docker-compose配置如下:
version: '3.8' services: mongo1: image: mongo:7 volumes: - /home/user/ducker/mongo-three-replica/rep1/data:/data/db - /home/user/ducker/mongo/rep.key:/opt/keyfile/mongo-keyfile environment: MONGO_INITDB_ROOT_USERNAME: your_username MONGO_INITDB_ROOT_PASSWORD: your_password command: "--replSet rs0 --keyFile /opt/keyfile/mongo-keyfile" ports: - "27017:27017" networks: - mongo-cluster mongo2: image: mongo:7 volumes: - /home/user/ducker/mongo-three-replica/rep2/data:/data/db - /home/user/ducker/mongo/rep.key:/opt/keyfile/mongo-keyfile environment: MONGO_INITDB_ROOT_USERNAME: your_username MONGO_INITDB_ROOT_PASSWORD: your_password command: "--replSet rs0 --keyFile /opt/keyfile/mongo-keyfile" ports: - "27018:27017" networks: - mongo-cluster mongo3: image: mongo:7 volumes: - /home/user/ducker/mongo-three-replica/rep3/data:/data/db - /home/user/ducker/mongo/rep.key:/opt/keyfile/mongo-keyfile environment: MONGO_INITDB_ROOT_USERNAME: your_username MONGO_INITDB_ROOT_PASSWORD: your_password command: "--replSet rs0 --keyFile /opt/keyfile/mongo-keyfile" ports: - "27019:27017" networks: - mongo-cluster rs-init: image: mongo:7 depends_on: - mongo1 - mongo2 - mongo3 networks: - mongo-cluster command: > bash -c "until mongosh --host mongo1:27017 --username your_username --password your_password --eval 'print(\"waiting for mongo1\")'; do sleep 2; done && until mongosh --host mongo2:27017 --username your_username --password your_password --eval 'print(\"waiting for mongo2\")'; do sleep 2; done && until mongosh --host mongo3:27017 --username your_username --password your_password --eval 'print(\"waiting for mongo3\")'; do sleep 2; done && mongosh --host mongo1:27017 --username your_username --password your_password --eval ' rs.initiate({ _id: \"rs0\", members: [ { _id: 0, host: \"mongo1:27017\" }, { _id: 1, host: \"mongo2:27017\" }, { _id: 2, host: \"mongo3:27017\" } ] })'" restart: "no" networks: mongo-cluster: driver: bridge
所有MongoDB容器处于同一bridge模式的mongo-cluster网络中,容器运行无报错,但客户端连接出现问题:
- 使用连接字符串
mongodb://your_username:your_password@localhost:27017,localhost:27018,localhost:27019/?replicaSet=rs0时,提示getaddrinfo ENOTFOUND mongo2(节点名随机变化); - 使用连接字符串
mongodb://your_username:your_password@localhost:27017/?replicaSet=rs0&authSource=admin&directConnection=true可单节点连接,执行rs.status()显示副本集状态正常:
{ set: 'rs0', date: 2024-01-19T04:37:25.215Z, myState: 1, term: Long('1'), syncSourceHost: '', syncSourceId: -1, heartbeatIntervalMillis: Long('2000'), majorityVoteCount: 2, writeMajorityCount: 2, votingMembersCount: 3, writableVotingMembersCount: 3, optimes: { lastCommittedOpTime: { ts: Timestamp({ t: 1705639038, i: 1 }), t: Long('1') }, lastCommittedWallTime: 2024-01-19T04:37:18.999Z, readConcernMajorityOpTime: { ts: Timestamp({ t: 1705639038, i: 1 }), t: Long('1') }, appliedOpTime: { ts: Timestamp({ t: 1705639038, i: 1 }), t: Long('1') }, durableOpTime: { ts: Timestamp({ t: 1705639038, i: 1 }), t: Long('1') }, lastAppliedWallTime: 2024-01-19T04:37:18.999Z, lastDurableWallTime: 2024-01-19T04:37:18.999Z }, lastStableRecoveryTimestamp: Timestamp({ t: 1705639038, i: 1 }), electionCandidateMetrics: { lastElectionReason: 'electionTimeout', lastElectionDate: 2024-01-19T03:46:28.896Z, electionTerm: Long('1'), lastCommittedOpTimeAtElection: { ts: Timestamp({ t: 1705635978, i: 1 }), t: Long('-1') }, lastSeenOpTimeAtElection: { ts: Timestamp({ t: 1705635978, i: 1 }), t: Long('-1') }, numVotesNeeded: 2, priorityAtElection: 1, electionTimeoutMillis: Long('10000'), numCatchUpOps: Long('0'), newTermStartDate: 2024-01-19T03:46:28.933Z, wMajorityWriteAvailabilityDate: 2024-01-19T03:46:29.458Z }, members: [ { _id: 0, name: 'mongo1:27017', health: 1, state: 1, stateStr: 'PRIMARY', uptime: 3068, optime: [Object], optimeDate: 2024-01-19T04:37:18.000Z, lastAppliedWallTime: 2024-01-19T04:37:18.999Z, lastDurableWallTime: 2024-01-19T04:37:18.999Z, syncSourceHost: '', syncSourceId: -1, infoMessage: '', electionTime: Timestamp({ t: 1705635988, i: 1 }), electionDate: 2024-01-19T03:46:28.000Z, configVersion: 1, configTerm: 1, self: true, lastHeartbeatMessage: '' }, { _id: 1, name: 'mongo2:27017', health: 1, state: 2, stateStr: 'SECONDARY', uptime: 3066, optime: [Object], optimeDurable: [Object], optimeDate: 2024-01-19T04:37:18.000Z, optimeDurableDate: 2024-01-19T04:37:18.000Z, lastAppliedWallTime: 2024-01-19T04:37:18.999Z, lastDurableWallTime: 2024-01-19T04:37:18.999Z, lastHeartbeat: 2024-01-19T04:37:24.921Z, lastHeartbeatRecv: 2024-01-19T04:37:23.916Z, pingMs: Long('0'), lastHeartbeatMessage: '', syncSourceHost: 'mongo1:27017', syncSourceId: 0, infoMessage: '', configVersion: 1, configTerm: 1 }, { _id: 2, name: 'mongo3:27017', health: 1, state: 2, stateStr: 'SECONDARY', uptime: 3066, optime: [Object], optimeDurable: [Object], optimeDate: 2024-01-19T04:37:18.000Z, optimeDurableDate: 2024-01-19T04:37:18.000Z, lastAppliedWallTime: 2024-01-19T04:37:18.999Z, lastDurableWallTime: 2024-01-19T04:37:18.999Z, lastHeartbeat: 2024-01-19T04:37:24.921Z, lastHeartbeatRecv: 2024-01-19T04:37:23.916Z, pingMs: Long('0'), lastHeartbeatMessage: '', syncSourceHost: 'mongo1:27017', syncSourceId: 0, infoMessage: '', configVersion: 1, configTerm: 1 } ], ok: 1, '$clusterTime': { clusterTime: Timestamp({ t: 1705639038, i: 1 }), signature: { hash: Binary.createFromBase64('dQbmbbD3p1ac17YJFU75jyIg1mU=', 0), keyId: Long('7325650787340648454') } }, operationTime: Timestamp({ t: 1705639038, i: 1 }) }
问题根源
副本集初始化时,节点名称配置的是Docker内部服务名(mongo1:27017、mongo2:27017、mongo3:27017),这些名称仅在Docker的mongo-cluster网络内可解析。当宿主机上的客户端连接到主节点后,主节点会返回副本集成员的内部地址,客户端无法解析这些地址,因此出现getaddrinfo ENOTFOUND错误。
修复方案
1. 重新配置副本集成员地址
通过单节点连接到主节点后,执行以下命令修改副本集配置,将成员地址替换为宿主机可访问的localhost加映射端口:
rs.reconfig({ _id: "rs0", members: [ { _id: 0, host: "localhost:27017" }, { _id: 1, host: "localhost:27018" }, { _id: 2, host: "localhost:27019" } ] })
注意:
rs.reconfig()会触发副本集重新选举主节点,生产环境需在业务低峰期操作,避免影响服务。
2. 修改docker-compose初始化脚本(可选,用于后续部署)
如果需要后续部署自动使用正确的地址,修改rs-init服务中的初始化命令,将成员host改为宿主机可访问地址:
rs.initiate({ _id: "rs0", members: [ { _id: 0, host: "localhost:27017" }, { _id: 1, host: "localhost:27018" }, { _id: 2, host: "localhost:27019" } ] })
3. 客户端连接字符串
修改完成后,使用以下连接字符串即可正常连接整个副本集:
mongodb://your_username:your_password@localhost:27017,localhost:27018,localhost:27019/?replicaSet=rs0&authSource=admin
额外注意事项
- 确保MongoDB容器内密钥文件
/opt/keyfile/mongo-keyfile的权限为600,所属用户组为mongodb:mongodb,否则会导致副本集节点间通信失败。 - 如果是远程客户端连接,需要将
localhost替换为宿主机的公网IP或域名,并确保宿主机防火墙开放了27017、27018、27019端口。
内容的提问来源于stack exchange,提问作者Lost_In_Library
相关产品推荐
相关产品推荐

