4. 副本集初始化部署实战

副本集部署的核心,是把三个(或更多)独立的 mongod 进程组织成一个具备自动选主与数据复制能力的集群。本节以三节点副本集为例,采用“配置文件启动 + rs.initiate() 初始化 + 状态验证”的方式完成一次可复现的部署。示例使用 MongoDB 6.0,操作系统为 Linux,三个节点分别监听 27017、27018、27019,副本集名称为 rs0。所有路径、端口与主机名均为教学示例,实际部署时需替换为真实环境参数。

一、部署前的目录与配置文件准备

生产环境不建议用命令行长参数启动 mongod,因为参数难以维护、容易遗漏,且不利于 systemd 托管。推荐为每个实例准备独立的数据目录、日志目录和配置文件。示例目录结构如下:

/data/mongodb/
├── rs0-1/
│   ├── data/
│   ├── log/
│   └── mongod.conf
├── rs0-2/
│   ├── data/
│   ├── log/
│   └── mongod.conf
└── rs0-3/
    ├── data/
    ├── log/
    └── mongod.conf

以 rs0-1 为例,配置文件 mongod.conf 内容如下。其中 replication.replSetName 必须三个节点完全一致,否则无法加入同一副本集;bindIp 在生产中应填写内网地址而非 0.0.0.0;keyFile 用于内部认证,同一副本集内所有节点必须使用相同密钥文件。

storage:
  dbPath: /data/mongodb/rs0-1/data
  journal:
    enabled: true
systemLog:
  destination: file
  path: /data/mongodb/rs0-1/log/mongod.log
  logAppend: true
net:
  port: 27017
  bindIp: 127.0.0.1
replication:
  replSetName: rs0
security:
  keyFile: /data/mongodb/keyfile
processManagement:
  fork: true

rs0-2 与 rs0-3 的配置只需修改 dbPath、systemLog.path 和 net.port(分别为 27018、27019),其余保持一致。密钥文件需提前生成并设置权限:

openssl rand -base64 756 > /data/mongodb/keyfile
chmod 400 /data/mongodb/keyfile
chown mongod:mongod /data/mongodb/keyfile

二、启动三个 mongod 实例

使用配置文件方式启动,命令简洁且便于脚本化:

mongod -f /data/mongodb/rs0-1/mongod.conf
mongod -f /data/mongodb/rs0-2/mongod.conf
mongod -f /data/mongodb/rs0-3/mongod.conf

启动后应检查进程与端口是否正常。可用 ps -ef | grep mongod 确认三个进程存在,用 ss -lntp | grep 2701 确认 27017、27018、27019 均处于 LISTEN 状态。若某实例未监听,优先查看对应 mongod.log 中的报错,常见原因包括 dbPath 权限不足、端口被占用、keyFile 权限过宽(MongoDB 要求 400 或 600)。

三、通过 rs.initiate() 初始化副本集

副本集初始化只需在一个节点上执行一次。使用 mongosh 连接 27017 节点:

mongosh --port 27017

在 mongosh 中执行初始化。推荐显式传入配置文档,明确 _id、成员列表与优先级,而不是依赖默认配置:

rs.initiate({
  _id: "rs0",
  members: [
    { _id: 0, host: "127.0.0.1:27017", priority: 2 },
    { _id: 1, host: "127.0.0.1:27018", priority: 1 },
    { _id: 2, host: "127.0.0.1:27019", priority: 1 }
  ]
})

返回 { ok: 1 } 表示初始化命令被接受。此时副本集进入选主阶段,通常几秒内完成。执行 rs.status() 可查看状态,重点关注三个字段:myState(当前节点状态,1 表示 PRIMARY)、members[].stateStr(各成员状态,PRIMARY/SECONDARY)、members[].health(1 为健康)。若某成员长时间处于 STARTUP2,通常是网络不通或 replSetName 不一致。

初始化完成后,mongosh 提示符会从 > 变为 rs0 [direct: primary]>,这是判断当前连接节点角色的直观信号。在 SECONDARY 节点上执行写操作会被拒绝,并提示 not primary,这属于正常行为。

四、验证主从同步状态

验证同步不能只看“节点是否 SECONDARY”,还要确认数据确实复制到了从节点。推荐按以下顺序验证:

  1. 在主节点写入一条测试数据:use testdb; db.sync_check.insertOne({name:"repl_test", ts:new Date()})。
  2. 在从节点执行 rs.secondaryOk() 允许读取,再查询 db.sync_check.find(),应能看到同一条记录。
  3. 执行 rs.printReplicationInfo() 查看主节点 oplog 时间窗口;执行 rs.printSecondaryReplicationInfo() 查看各从节点相对主节点的延迟(secsBehindPrimary)。
  4. 执行 db.serverStatus().repl 或 rs.status().members,确认 optime 与 lastCommittedOpTime 持续推进。

判断同步是否健康的实用标准:从节点 stateStr 为 SECONDARY、health 为 1、secsBehindPrimary 接近 0 且不持续增长、从节点能读到主节点刚写入的数据。若延迟持续增大,应检查网络带宽、从节点磁盘 IO 以及是否有大事务或全量同步正在进行。

三节点副本集 rs0 初始化与同步验证示意 mongosh rs.initiate() PRIMARY 127.0.0.1:27017 priority: 2 接受读写 SECONDARY 127.0.0.1:27018 priority: 1 SECONDARY 127.0.0.1:27019 priority: 1 初始化/写入 oplog 复制 oplog 复制 心跳/选举 同步验证要点 • stateStr = PRIMARY/SECONDARY • health = 1 • secsBehindPrimary ≈ 0 • 从节点可读到主节点写入 • optime 持续推进 • 写操作仅在 PRIMARY 成功 • 从节点写被拒绝属正常

五、常见初始化异常与排查方向

初始化阶段最容易出现三类问题。第一类是 rs.initiate() 返回 already initialized,说明该节点已属于某个副本集,需先确认是否重复执行,必要时清理 dbPath 后重新启动。第二类是成员长期处于 STARTUP2 或 REMOVED,通常由主机名解析失败、端口不通或 replSetName 不一致导致,可用 rs.status().members[].lastHeartbeatMessage 查看具体原因。第三类是初始化后无法选主,常见于节点数不足多数派、priority 配置冲突或 keyFile 不一致,此时应检查 rs.status().members[].stateStr 与日志中的选举信息。

生产部署建议:三个节点应分布在不同物理机或不同可用区;bindIp 使用内网地址;keyFile 权限必须为 400;初始化时显式指定 priority,避免默认优先级导致选主结果不符合预期。

完成上述步骤后,一个可用的三节点副本集即部署完成。后续可通过 rs.add() 扩容、通过 rs.stepDown() 演练主从切换,并结合监控持续观察复制延迟与节点健康状态。