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”,还要确认数据确实复制到了从节点。推荐按以下顺序验证:
- 在主节点写入一条测试数据:
use testdb; db.sync_check.insertOne({name:"repl_test", ts:new Date()})。 - 在从节点执行
rs.secondaryOk()允许读取,再查询db.sync_check.find(),应能看到同一条记录。 - 执行
rs.printReplicationInfo()查看主节点 oplog 时间窗口;执行rs.printSecondaryReplicationInfo()查看各从节点相对主节点的延迟(secsBehindPrimary)。 - 执行
db.serverStatus().repl或rs.status().members,确认optime与lastCommittedOpTime持续推进。
判断同步是否健康的实用标准:从节点 stateStr 为 SECONDARY、health 为 1、secsBehindPrimary 接近 0 且不持续增长、从节点能读到主节点刚写入的数据。若延迟持续增大,应检查网络带宽、从节点磁盘 IO 以及是否有大事务或全量同步正在进行。
五、常见初始化异常与排查方向
初始化阶段最容易出现三类问题。第一类是 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() 演练主从切换,并结合监控持续观察复制延迟与节点健康状态。