坑爹的 MongoDB副本部署经历

官方文档:

https://www.mongodb.com/zh-cn/docs/v8.0/tutorial/deploy-replica-set/

其实过程不复杂, 但是存在一些权限的问题. 而且 mongo的错误提示并不友好, 甚至还有来自官方的坑. 过程可分为 3 步:

  1. 生成keyFile
  2. 编写docker-compose.yml并启动容器
  3. 查看 mongo 副本模式状态

mongodb 版本: 8.0.14

生成 keyFile

这步很关键, 因为8.x 版本开启副本模式强制依赖密钥文件. 否则你会得到下面的报错:

BadValue: security.keyFile is required when authorization is enabled with replica sets

坑爹的官方文档里当然不会和你讲这一步的必要性. 生成的命令很简单:

# 生成密钥文件(权限必须为 600 或更严格)
openssl rand -base64 756 > mongo-keyfile
chmod 600 mongo-keyfile
chown 101:101 mongo-keyfile

其中 chmodchown 必须要执行, 否则启动阶段会出现

error opening file: /data/mongo-keyfile: bad file

原因有二:

  • mongodb 会检查keyFile 权限是否为600, 否则会出现权限错误. 因此不能粗暴的把权限设置为777
  • 要求权限为600-rw------- , 因此文件 owner 需要和镜像中保持一致.

那么镜像中使用的是什么用户呢? 第一反应去看看 mongod 的 dockerfile 怎么写的:

...
FROM ubuntu:noble

# add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added
RUN set -eux; \
	groupadd --gid 999 --system mongodb; \
	useradd --uid 999 --system --gid mongodb --home-dir /data/db mongodb; \
	mkdir -p /data/db /data/configdb; \
	chown -R mongodb:mongodb /data/db /data/configdb
...

dockerfile 链接 mongo dockerfile

原来是UID=999, GID=999, 那为什么上面的命令是101:101 呢? 是不是写错了 ?
是的, 坑爹的 dockerfile 又误导了我, 折腾了半天最后打印当前的用户的时候发现根本不是999 . 我突然释怀的笑了, 哈哈哈

编写 dockerfile & 启动容器

先看看目录结构:

tree .
.
├── docker-compose.yml
├── mongo-keyfile
└── scripts
    └── init-replica.js

dockerfile

version: '3.8'

services:
  # MongoDB 主节点
  mongo1:
    image: mongodb/mongodb-community-server:8.0.14
    container_name: mongo1
    restart: always
    environment:
      - MONGODB_INITDB_ROOT_USERNAME=admin
      - MONGODB_INITDB_ROOT_PASSWORD=password
    ports:
      - "27017:27017"
    # Mongodb UserID:GroupID
    #user: "101:101"  # 使用宿主机当前用户的 UID/GID
    volumes:
      - mongo1_data:/data/db
      - ./scripts:/scripts
      - ./mongo-keyfile:/data/mongo-keyfile
    networks:
      - mongo_cluster
    # command: /bin/bash -c 'id && id mongodb && ls -ld /data/db && touch /data/db/test && ls -l /data/db && mongod --replSet rs0 --bind_ip_all --port 27017 --keyFile /data/mongo-keyfile'
    command: mongod --replSet rs0 --bind_ip_all --port 27017 --keyFile /data/mongo-keyfile

  # MongoDB 从节点 1
  mongo2:
    image: mongodb/mongodb-community-server:8.0.14
    container_name: mongo2
    restart: always
    environment:
      - MONGODB_INITDB_ROOT_USERNAME=admin
      - MONGODB_INITDB_ROOT_PASSWORD=password
    ports:
      - "27018:27017"
    volumes:
      - mongo2_data:/data/db
      - ./mongo-keyfile:/data/mongo-keyfile
    networks:
      - mongo_cluster
    command: mongod --replSet rs0 --bind_ip_all --port 27017 --keyFile /data/mongo-keyfile

  # MongoDB 从节点 2
  mongo3:
    image: mongodb/mongodb-community-server:8.0.14
    container_name: mongo3
    restart: always
    environment:
      - MONGODB_INITDB_ROOT_USERNAME=admin
      - MONGODB_INITDB_ROOT_PASSWORD=password
    ports:
      - "27019:27017"
    volumes:
      - mongo3_data:/data/db
      - ./mongo-keyfile:/data/mongo-keyfile
    networks:
      - mongo_cluster
    command: mongod --replSet rs0 --bind_ip_all --port 27017 --keyFile /data/mongo-keyfile

  # 初始化副本集的临时容器
  mongo-init-replica:
    image: mongodb/mongodb-community-server:8.0.14
    container_name: mongo-init-replica
    volumes:
      - ./scripts:/scripts
    depends_on:
      - mongo1
      - mongo2
      - mongo3
    networks:
      - mongo_cluster
    command: >
      bash -c "
        sleep 10 &&
        mongosh --host mongo1:27017 -u admin -p password --authenticationDatabase admin /scripts/init-replica.js
      "

networks:
  mongo_cluster:
    driver: bridge

volumes:
  mongo1_data:
  mongo2_data:
  mongo3_data:

初始化脚本

rs.initiate({
  _id: "rs0",
  members: [
    { _id: 0, host: "mongo1:27017" },
    { _id: 1, host: "mongo2:27017" },
    { _id: 2, host: "mongo3:27017" }
  ]
});

// 等待副本集初始化完成
sleep(2000);

// 查看副本集状态
rs.status();

启动容器

docker-compose up -d

验证

  1. 进入主节点容器
# 进入主节点容器
docker exec -it mongo1 mongosh -u admin -p password --authenticationDatabase admin

# 在 mongo shell 中查看状态
rs.status()
  1. 验证主从关系:
# 在 mongo shell 中执行
rs0 [direct: primary] test> rs.isMaster()
{
  topologyVersion: {
    processId: ObjectId('68d53800e2f75911db84c2e0'),
    counter: Long('6')
  },
  hosts: [ 'mongo1:27017', 'mongo2:27017', 'mongo3:27017' ],
  setName: 'rs0',
  setVersion: 1,
  ismaster: true,
  secondary: false,
  primary: 'mongo1:27017',
  me: 'mongo1:27017',
  electionId: ObjectId('7fffffff0000000000000002'),
  lastWrite: {
    opTime: { ts: Timestamp({ t: 1758807369, i: 1 }), t: Long('2') },
    lastWriteDate: ISODate('2025-09-25T13:36:09.000Z'),
    majorityOpTime: { ts: Timestamp({ t: 1758807369, i: 1 }), t: Long('2') },
    majorityWriteDate: ISODate('2025-09-25T13:36:09.000Z')
  },
  maxBsonObjectSize: 16777216,
  maxMessageSizeBytes: 48000000,
  maxWriteBatchSize: 100000,
  localTime: ISODate('2025-09-25T13:36:09.953Z'),
  logicalSessionTimeoutMinutes: 30,
  connectionId: 67,
  minWireVersion: 0,
  maxWireVersion: 25,
  readOnly: false,
  ok: 1,
  '$clusterTime': {
    clusterTime: Timestamp({ t: 1758807369, i: 1 }),
    signature: {
      hash: Binary.createFromBase64('dw26T3C3/4wkdyQo3IDDZPDNc9E=', 0),
      keyId: Long('7554004955699347462')
    }
  },
  operationTime: Timestamp({ t: 1758807369, i: 1 }),
  isWritablePrimary: true
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值