修改服务器时间致MongoDB副本集故障:跨6个月跳转后27017无监听
问题背景
生产服务器上有一套每日执行的开票流程,耗时约4小时。当前部署的QA环境在同一台机器上搭建了3节点MongoDB副本集,测试目标为:用当日数据执行开票流程后,通过sudo timedatectl set-time ${TIME}修改服务器时间至次日重复执行,计划2天内完成一周流程测试。
故障现象
- 时间跳转幅度为1天或1个月时,系统无异常;
- 跳转6个月及以上时,MongoDB故障,27017端口不再处于LISTENING状态;
- 执行
netstat -nap | grep mongo得到以下输出:
netstat -nap | grep mongo (Not all processes could be identified, non-owned process info will not be shown, you would have to be root to see it all.) unix 2 [ ] STREAM 4107509 - /tmp/mongodb-27017.sock unix 2 [ ACC ] STREAM LISTENING 32418 - /tmp/mongodb-27012.sock unix 2 [ ACC ] STREAM LISTENING 32393 - /tmp/mongodb-27011.sock unix 2 [ ACC ] STREAM LISTENING 24202 - /tmp/mongodb-20000.sock unix 2 [ ACC ] STREAM LISTENING 33524 - /tmp/mongodb-27023.sock unix 2 [ ACC ] STREAM LISTENING 29140 - /tmp/mongodb-20002.sock unix 2 [ ACC ] STREAM LISTENING 26360 - /tmp/mongodb-20001.sock unix 2 [ ACC ] STREAM LISTENING 34090 - /tmp/mongodb-27013.sock unix 2 [ ACC ] STREAM LISTENING 34093 - /tmp/mongodb-27021.sock unix 2 [ ACC ] STREAM LISTENING 30073 - /tmp/mongodb-27022.sock
已尝试的无效操作
- 重启MongoDB进程
- 手动删除socket文件
- 重启机器
排查步骤
查看MongoDB核心日志
检查MongoDB日志文件(默认路径/var/log/mongodb/mongod.log,或自定义配置路径),搜索时间跳转后的报错信息。大幅时间变更可能触发副本集心跳时间戳校验、oplog时间戳冲突等内部逻辑报错。执行命令:tail -n 200 /var/log/mongodb/mongod.log检查副本集节点状态
通过正常监听的本地socket连接到副本集节点,执行rs.status()查看集群状态:mongo --socket /tmp/mongodb-27011.sock > rs.status()重点关注节点
stateStr、optime、optimeDate字段,确认是否存在时间戳不匹配、节点无法选举等异常。验证oplog时间范围
大幅时间跳转可能导致oplog时间戳超出MongoDB可处理范围,执行以下命令查看oplog的首尾时间:mongo --socket /tmp/mongodb-27011.sock > use local > db.oplog.rs.find().sort({$natural: -1}).limit(1).pretty() # 最新oplog条目 > db.oplog.rs.find().sort({$natural: 1}).limit(1).pretty() # 最早oplog条目核对返回的
ts字段对应时间,确认是否与跳转后的系统时间存在冲突。检查系统时间同步服务
若系统开启NTP服务,可能自动将时间同步回原时间,导致MongoDB异常。执行命令确认NTP状态:timedatectl status若
NTP service处于active状态,先停止同步:sudo systemctl stop systemd-timesyncd sudo timedatectl set-ntp false
解决方法
修复副本集时间戳冲突
如果日志显示oplog时间戳异常,需重新初始化副本集:- 停止所有MongoDB节点;
- 删除所有节点的
local数据库目录(默认路径/var/lib/mongodb/local); - 重新初始化副本集:
mongo --socket /tmp/mongodb-27011.sock > rs.initiate() > rs.add("localhost:27012") > rs.add("localhost:27013") - 重新导入测试数据或从备份恢复。
调整MongoDB时间校验参数
修改mongod.conf配置文件,添加时间校验放宽参数(适用于MongoDB 3.4及以上版本):setParameter: enableClockSkewAdjustment: true maxClockSkewSeconds: 864000 # 设置为10天,可根据测试需求调整修改后重启所有MongoDB节点。
替换大幅时间跳转的测试方案
避免直接修改系统时间,改用应用层时间模拟:- 在开票流程代码中注入自定义时间,覆盖系统时间获取逻辑;
- 使用Docker等容器化工具,为测试环境隔离独立的系统时间,不影响宿主机MongoDB运行。
内容的提问来源于stack exchange,提问作者Rodrigo

