【最后203篇系列】033 Mongo副本集修复过程
今天又经历了一次修复Mongo副本集的过程,我觉得得写下来。
大约在3年前搭建了Mongo集群(Mongo5),然后我使用ip代理的方式让集群的机器可以公网访问。ip代理机器一般只能租3年,所以到期就会更换,此时就会出现问题:集群的机器不能通信,访问失败
首先,肯定是要登录到终端。
docker exec -it Mongo_Rep_Container
use admin
db.auth("user","password")
在查询db.isMaster()
大概会是这样的: Does not have a valid replica set config
{"topologyVersion" : {"processId" : ObjectId("68a282f5dd03af7c246cded7"),"counter" : NumberLong(1)},"ismaster" : false,"secondary" : false,"info" : "Does not have a valid replica set config",
...
}
此时整个集群已经属于不可用状态, node1, node2和node3都已经失联了。
Step1 将某个节点升级为PRIMARY
此时,先选一个节点重新初始化集群,副本集已经初始化过,所以直接 rs.initiate() 会报 “AlreadyInitialized”
rs.initiate({_id : "xxx",members: [{ _id : 0, host : "YOURIP:THE_PORT",priority:1 }]})
强制执行又会报一个错,这是因为如果节点存在过,即使重新初始化也不可以改变NodeID
mymeta:SECONDARY> rs.reconfig(cfg, {force: true})
{"ok" : 0,"errmsg" : "New and old configurations both have members with host of IP:PORT but in the new configuration the _id field is MemberId(0) and in the old configuration it is MemberId(12) for replica set mymeta","code" : 103,"codeName" : "NewReplicaSetConfigurationIncompatible"
}
保持NodeID不变,重新强制初始化
cfg = rs.conf()
cfg.members = [{_id: 12, // 保持原来的 _idhost: "IP:PORT",priority: 1,votes: 1}
]rs.reconfig(cfg, {force:true})
此时节点会完成变化,正式转为PRIMARY节点
rs.status()
{"set" : "REPLICA NAME",
...},"members" : [{"_id" : 12,"name" : "IP:PORT","health" : 1,"state" : 1,"stateStr" : "PRIMARY",mymeta:PRIMARY>
Step2 将其他节点加入
我之前的三个节点,其中一个已经重新成为主节点,且网络中只有一个节点。一个节点被我弄坏了(被大模型骗的),另一个除了失联,别的没问题。
处理弄坏的节点
先把对应的数据部分删除(/data/...replica
)
重新初始化项目:在opt下方配置和秘钥,在data下放文件夹
# 1 在data目录创建文件夹proc_path=/opt/MongoReplica/
data_path=/data/MongoReplica/sh ${proc_path}/create_shard_folder.sh ${data_path}/replica# 修改秘钥文件
chown 999 ${proc_path}replica/mongo.key
chmod 600 ${proc_path}replica/mongo.key
其中create_shard_folder.sh
的目的是创建副本文件夹并修改权限。
#!bin/bashmkdir -p $1mkdir $1/dbmkdir $1/logtouch $1/log/mongod.logchmod -R 777 $1
启动容器
image_name="MONGOIMAGE"
port=PORTdocker run -d \--restart=always \--name='CONTAINERNAME' \-v /etc/localtime:/etc/localtime \-v /etc/timezone:/etc/timezone\-e "LANG=C.UTF-8"\-m 2g \-v ${proc_path}replica/mongo.key:/etc/mongo.key \-v ${data_path}/replica:/home/mongod \-v ${proc_path}replica/mongod.conf:/etc/mongod.conf \-p ${port}:27017 \${image_name} \-f /etc/mongod.conf
在PRIMARY节点执行:
rs.add("IP:PORT2")
rs.status()"_id" : 13,"name" : "IP:PORT2","health" : 1,"state" : 5,"stateStr" : "STARTUP2",
...
到这里"stateStr" : "STARTUP2",
节点已经在同步数据了
处理没坏的节点
这个过程很顺利,并没有发生问题。当然最坏的情况也就是重新同步一次。
日志问题
因为集群是依靠oplog来进行同步的,这个日志会随着时间越来越大。一些所谓的控制方法我都试过,似乎不太灵光。然后之前我做好过轮转日志,但是只会轮转主节点,不会作用在从节点,这就比较麻烦。
毕竟如果集群搭建起来,总不能隔三差五的去删节点,加节点吧。
这次也解决了这个问题:主节点用Rotate,从节点用SIGUSR1
SIGUSR1 是什么
SIGUSR1 是 Unix/Linux 系统里的一个 用户自定义信号(Signal)
MongoDB 对这个信号有特殊处理:
当 mongod 进程收到 SIGUSR1 时,会 安全地关闭当前日志文件并重新打开日志
不会影响数据库的读写,也不会触发重启
换句话说,这是 MongoDB 官方推荐的 日志轮转方式,尤其适合 Secondary 或通过 Docker 启动的节点。
直接执行命令可以测试
副本日志清理
docker exec -it CONTAINER pgrep mongod // 一般是1
docker exec -it CONTAINER kill -USR1 1
考虑到是低频操作,而且节点可能会随着选举变化
import subprocess
import pymongo# -------------------------------
# 配置
# -------------------------------
container_name = "CONTAINER"
mongo_host = "127.0.0.1"
mongo_port = PORT
mongo_user = "fff"
mongo_pwd = "ff"
auth_db = "admin"# -------------------------------
# 1️⃣ 连接 MongoDB
# -------------------------------
client = pymongo.MongoClient(host=mongo_host,port=mongo_port,username=mongo_user,password=mongo_pwd,authSource=auth_db,authMechanism='SCRAM-SHA-1'
)db = client['admin']# -------------------------------
# 2️⃣ 判断是否主节点
# -------------------------------
ismaster = db.command("isMaster")
if ismaster.get("ismaster", False):# 主节点,用 logRotate 命令db.command({'logRotate': 1})print("Primary 节点日志轮转完成")
else:# 从节点,用 SIGUSR1# 找 PIDpid_cmd = f"docker exec -i {container_name} pgrep mongod"pid = subprocess.check_output(pid_cmd, shell=True).decode().strip()kill_cmd = f"docker exec -i {container_name} kill -USR1 {pid}"subprocess.run(kill_cmd, shell=True)print(f"Secondary 节点日志轮转完成,发送 SIGUSR1 给 PID {pid}")
然后日志可以被轮转,另外再写一个简单的sh脚本,把对应目录下包含日期的文件取出来,然后再删除。
clear_mongo_rep.sh
#!/bin/bash
LOG_DIR="/data/aprojects/MongoReplica/replica/log"# 删除所有带日期的日志文件(只保留 mongod.log)
find "$LOG_DIR" -type f -name "mongod.log.2*" -deleteecho "✅ 已清理所有历史日志文件"
Mongo5集群作为一个基础部件,以后应该也不太改了。以后有需要还可以搭建Mongo8集群。