这不是白皮书,也不是“十分钟学会复制集”。这是一次上线前夜的“实战日志”。两台 ECS:A 和 B,同一 VPC,只开内网。目标很朴素:A 为 Primary,B 为 Secondary,数据稳、切换可预期、故障能复盘。
00|开机前的三张小纸条(先决条件)
- 网:A 与 B 在同一 VPC/交换机,仅放行 27017 彼此互通;22 端口仅对跳板机开放。
- 时:两台机器时钟同步(chrony / ntp),否则复制与鉴权会出幺蛾子。
- 名:给 A/B 起固定主机名或内网域名,后面配置里直接写这个名(别写公网)。
01|分镜一:生成“暗号”(keyFile)
旁白:复制集要彼此信任,先做一把共同钥匙。
在 A 上:
sudo bash -c 'openssl rand -base64 756 > /etc/mongo-keyfile'
sudo chown mongod:mongod /etc/mongo-keyfile
sudo chmod 600 /etc/mongo-keyfile
# 复制到 B(走内网)
scp -o StrictHostKeyChecking=no /etc/mongo-keyfile root@B内网IP:/etc/mongo-keyfile
ssh root@B内网IP "chown mongod:mongod /etc/mongo-keyfile && chmod 600 /etc/mongo-keyfile"
口令文件权限必须是 600,否则 mongod 直接罢工。
02|分镜二:写两份不对称的
mongod.conf
道具:Ubuntu 22.04 / AlmaLinux 9 都行,MongoDB 6.x/7.x 社区版即可。
共性:都用 WiredTiger,绑定内网 IP,启用复制与 keyFile。
A(Primary 候选) /etc/mongod.conf:
storage:
dbPath: /var/lib/mongo
journal:
enabled: true
net:
bindIp: 127.0.0.1, A内网IP
port: 27017
security:
keyFile: /etc/mongo-keyfile
replication:
replSetName: rs-app
setParameter:
enableLocalhostAuthBypass: false
processManagement:
fork: false
B(Secondary) /etc/mongod.conf:
storage:
dbPath: /var/lib/mongo
journal:
enabled: true
net:
bindIp: 127.0.0.1, B内网IP
port: 27017
security:
keyFile: /etc/mongo-keyfile
replication:
replSetName: rs-app
setParameter:
enableLocalhostAuthBypass: false
processManagement:
fork: false
启动:
sudo systemctl enable --now mongod
sudo systemctl status mongod
03|分镜三:在 A 上“点火”并拉 B 入伙
对白:A 是队长,先开局;B 作为二当家,随后加入。
mongosh --host A内网IP
在 mongosh:
// 1) 初始化复制集(只有 A 自己,先站起来)
rs.initiate({
_id: "rs-app",
members: [
{ _id: 0, host: "A主机名或内网IP:27017", priority: 2, tags: {role: "primary"} }
],
settings: { electionTimeoutMillis: 5000 }
})
// 2) 加入 B 为副本
rs.add({ _id: 1, host: "B主机名或内网IP:27017", priority: 1, tags: {role: "secondary"} })
// 3) 创建管理用户(之后都用账号登录)
use admin
db.createUser({
user: "siteAdmin",
pwd: "一串强口令",
roles: [ { role: "root", db: "admin" } ]
})
小注:priority: 2 > 1 表示有条件时 A 更倾向当 Primary,但这不是“钉死”。
04|分镜四:三步验收(看、写、读)
看复制状态
rs.status() // 角色与心跳
rs.printReplicationInfo() // Oplog 大小/可回放时长
rs.printSlaveReplicationInfo() // 各节点延迟
写入数据(在 A)
use demo
for (let i = 0; i < 1000; i++) {
db.logs.insertOne({ i, ts: new Date(), from: "A" })
}
db.logs.countDocuments()
在 B 验收(只读)
mongosh --host B内网IP -u siteAdmin -p '密码' --authenticationDatabase admin
rs.secondaryOk() // 允许在副本上读
use demo
db.logs.countDocuments() // 应该与 A 一致
db.logs.find().sort({ts:-1}).limit(3)
05|分镜五:一次“温和”的主从切换彩排
场景 A(网络正常):在 A 上自愿让贤
rs.stepDown(60) // A 让出 60 秒
若两台机都彼此可达,B 会发起选举,通常能升为 Primary(两票都在,过半=2)。
场景 B(A 故障或断网):只剩 B 一票
- 两节点复制集的“过半”是 2。A 不在场时,B 只有 1 票,不会自动当主。
- 这正是双节点的硬约束:要高可用,建议加一个仲裁节点(Arbiter)。
06|加一笔:最轻量的高可用——仲裁节点(可选,但强烈建议)
如果允许新拉一台极小规格 ECS(同 VPC),把它命名为 C(Arbiter)。配置与前面相同,但 不存数据(默认即不存)。同样放置 keyFile、加入复制集:
// 在 A 或 B 上执行(Primary 节点)
rs.addArb("C主机名或内网IP:27017")
- 现在有 3 张票,过半为 2。任一台数据节点挂了,仍能自动选主。
- Arbiter 不存数据,不占用多少磁盘与内存,成本低;但也不要与数据节点共机。
07|写一致性与“卡顿错觉”(生产前必做取舍)
- 默认写关注 w:1,落到 Primary 就算成功;复制到 B 异步进行。低延迟,但遇到 A 故障可能丢最近一笔(已写 A、未到 B)。
- w:“majority” 要等过半(双节点=2)确认才返回。更安全,但更依赖内网质量(阿里云同可用区通常 <1ms,仍建议压测)。
应用端统一入口(示例 URI)
mongodb://A内网主机:27017,B内网主机:27017/app?replicaSet=rs-app&readPreference=primaryPreferred&w=majority&wtimeoutMS=2000
- 读:primaryPreferred,主忙时可读副。
- 写:majority + 2s 超时 —— 卡顿不无限制,超时自己兜底。
08|“灰盒”测试清单(上线前一口气做完)
- 杀主自愈:systemctl stop mongod(A)→ 观察 B 是否当主(有 Arbiter 才会自动)。
- 链路抖动:用 tc 注入 3–5ms 延迟/0.5% 丢包,观察写延迟(w:majority)。
- 磁盘告急:在 B 把磁盘占到 90%+,看复制是否告警/堆积。
- 权限回收:删除一个业务库写权限,确认应用侧报错清晰。
- 时钟偏移:手动让 B 偏移 2s,确认鉴权失败并能定位(修正 NTP)。
09|日常运维三把“小扳手”
- 看吞吐:mongostat –discover 1、mongotop 1
- 看慢语句:operationProfiling
operationProfiling:
mode: slowOp
slowOpThresholdMs: 100
- 看复制延迟:rs.printSlaveReplicationInfo()(延迟常态应 <200ms)
10|收尾:备份与回滚闸
- 快照优先:ECS 系统盘/数据盘开启自动快照(每日、保留 7–14 天)。
- 逻辑备份:非高峰执行 mongodump –archive=/data/backup/$(date +%F).gz –gzip –oplog。
- 演练恢复:至少做一次 mongorestore –archive=… –gzip 到临时环境,确认冷备可用。
11|常见“误伤名单”(踩过一次就不想再踩)
- 把 bindIp 设成 0.0.0.0 然后安全组也放大,血槽见底。
- keyFile 权限不是 600,启动报错却被忽略。
- 两节点没 Arbiter,却指望“自动主备切换”。
- Primary 上把 w:majority 打开,内网偶发抖动,应用侧间歇卡却不打点。
- 用公网连接复制集,跨 AZ/跨地域,复制延迟爆表。
12|尾声:把可预期当作承诺
两台机器搞复制集,不是为了“炫技”,而是让你的应用在凌晨三点也能坦然落笔。
A 习惯当主,B 乐于背书;如果你肯给它们添一个仲裁者,就能把“侥幸可用”升格为“可预期可用”。
上线不是一跃而就,而是有谱地演一遍又一遍。剧本写在这里,灯光该你打了。