Docker Compose 部署 MongoDB 副本集需满足:三服务名与 host 字段一致、共用 replSet 名、keyFile 属主999权限400、数据目录隔离、mongo1 等待端口就绪后执行 rs.initiate()。

docker-compose.yml 文件必须显式定义三个独立服务、共用同一副本集名称、且每个节点能互相解析主机名——否则 rs.initiate() 会失败或节点无法加入。
三节点服务名必须与副本集配置中的 host 字段严格一致
副本集初始化时,members 数组里每个 host 值(如 "mongo1:27017")必须能被其他容器通过 DNS 解析。Docker Compose 默认为每个 service 创建同名网络别名,所以 container_name 和 service 名称要统一,避免混用别名导致连接超时。
- 错误写法:
service: mongodb1+container_name: mongo1→ 其他容器只能通过mongodb1访问,但初始化脚本写了mongo1:27017,解析失败 - 正确写法:service 名、
container_name、host字段三者都用mongo1(或都用mongo2等) - 所有节点的
--replSet参数值必须完全相同,比如都用rs0,大小写和拼写不能差一个字符
keyFile 权限和属主必须是 999:999 且权限为 400
MongoDB 官方镜像以 UID 999 用户(mongodb 用户)运行 mongod 进程。若 keyFile 属主不是 999 或权限大于 400,启动时直接报错:ERROR: keyfile must be readable only by owner 或 bad file。
- 生成密钥后必须立刻执行:
chmod 400 ./mongodb.key && chown 999:999 ./mongodb.key - 挂载进容器时,路径要确保可读(推荐挂载为只读:
./mongodb.key:/mongodb.key:ro) - 不要在
entrypoint里用touch或重定向生成 keyFile —— 多个容器并发写会冲突,且权限难保证
初始化脚本必须等主节点就绪后再执行 rs.initiate()
即使三个容器同时启动,mongo1 的 mongod 进程也未必已监听 27017 端口。直接在 entrypoint 里调 mongo 命令连不上,就会跳过初始化,集群永远不生效。
- 推荐做法:在
mongo1的entrypoint中加等待循环,例如:until mongo --host localhost:27017 --eval "db.runCommand({ping:1})" >/dev/null 2>&1; do sleep 2; done - 初始化脚本(如
/init-replica.js)应只由mongo1执行一次,且必须指定认证数据库:mongo -u root -p password --authenticationDatabase admin /init-replica.js - 脚本内判断是否已初始化:
if (rs.status().ok !== 1) { rs.initiate(...) },防止重复执行报错
数据卷路径不能复用,且需提前创建宿主机目录
三个节点必须使用隔离的数据目录,否则启动时因 WiredTiger 锁文件冲突而崩溃,日志里常见 Lock file appears to be held by another process。
- 每个 service 的
volumes必须指向不同宿主机路径,例如:./data/mongo1:/data/db、./data/mongo2:/data/db - 宿主机目录需手动创建并赋权:
mkdir -p ./data/{mongo1,mongo2,mongo3} && chown -R 999:999 ./data - 不要用匿名卷(如
volume-data:)—— 无法保证多节点数据持久隔离,调试时难以定位问题
rs.status() 就永远显示 startup 或 REMOVED。


















