1. MongoDB副本集概述

MongoDB副本集是MongoDB提供的高可用性解决方案,通过多个MongoDB实例的协同工作,实现数据的自动复制和故障转移。本文将详细介绍MongoDB副本集的运维实战经验,包括副本集部署、数据同步、故障转移、监控告警的完整解决方案。

1.1 核心功能

  1. 数据复制: 实现数据从主节点到从节点的自动复制
  2. 故障转移: 自动检测主节点故障并切换到从节点
  3. 读写分离: 主节点处理写操作,从节点处理读操作
  4. 数据一致性: 确保副本集内数据的一致性和完整性
  5. 高可用保障: 提供7x24小时不间断服务

1.2 技术架构

1
2
3
4
5
客户端应用 → MongoDB主节点 → MongoDB从节点
↓ ↓ ↓
写操作处理 → 数据同步 → 读操作处理
↓ ↓ ↓
副本集监控 → 故障检测 → 自动切换

2. 环境准备

2.1 系统环境检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#!/bin/bash
# check_mongodb_replica_env.sh - MongoDB副本集环境检查脚本
# @author 运维实战

# 日志函数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}

# 检查系统环境
check_system_environment() {
log "开始检查MongoDB副本集系统环境..."

# 检查操作系统
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
OS_VERSION=$(cat /etc/os-release | grep PRETTY_NAME | cut -d'"' -f2)
log "操作系统: $OS_VERSION"
else
log "错误: 不支持的操作系统 $OSTYPE"
exit 1
fi

# 检查系统资源
TOTAL_MEM=$(free -h | grep "Mem:" | awk '{print $2}')
AVAILABLE_MEM=$(free -h | grep "Mem:" | awk '{print $7}')
CPU_CORES=$(nproc)
DISK_SPACE=$(df -h / | tail -1 | awk '{print $4}')

log "系统资源检查:"
log " 总内存: $TOTAL_MEM"
log " 可用内存: $AVAILABLE_MEM"
log " CPU核心数: $CPU_CORES"
log " 可用磁盘空间: $DISK_SPACE"

# 检查网络连通性
check_network_connectivity
}

# 检查网络连通性
check_network_connectivity() {
log "检查网络连通性..."

# 检查副本集节点间网络
REPLICA_NODES=("192.168.1.10" "192.168.1.11" "192.168.1.12")

for node in "${REPLICA_NODES[@]}"; do
if ping -c 3 $node > /dev/null 2>&1; then
log "节点网络连通正常: $node"
else
log "警告: 节点网络连通异常: $node"
fi
done

# 检查端口可用性
check_port_availability
}

# 检查端口可用性
check_port_availability() {
log "检查MongoDB端口可用性..."

MONGODB_PORTS=(27017 27018 27019) # MongoDB服务端口

for port in "${MONGODB_PORTS[@]}"; do
if netstat -tlnp | grep ":$port " > /dev/null; then
log "警告: 端口 $port 已被占用"
else
log "端口 $port 可用"
fi
done
}

# 检查MongoDB安装
check_mongodb_installation() {
log "检查MongoDB安装状态..."

if command -v mongod > /dev/null 2>&1; then
MONGODB_VERSION=$(mongod --version | head -1)
log "MongoDB已安装: $MONGODB_VERSION"

# 检查MongoDB服务状态
if systemctl is-active --quiet mongod; then
log "MongoDB服务运行正常"
else
log "MongoDB服务未运行"
fi
else
log "MongoDB未安装,开始安装..."
install_mongodb
fi
}

# 安装MongoDB
install_mongodb() {
log "开始安装MongoDB..."

# 添加MongoDB官方仓库
wget -qO - https://www.mongodb.org/static/pgp/server-6.0.asc | sudo apt-key add -
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/6.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-6.0.list

# 更新包管理器
sudo apt-get update

# 安装MongoDB
sudo apt-get install -y mongodb-org

# 启动MongoDB服务
sudo systemctl start mongod
sudo systemctl enable mongod

log "MongoDB安装完成"
}

# 主函数
main() {
log "=== MongoDB副本集环境检查开始 ==="
check_system_environment
check_mongodb_installation
log "=== MongoDB副本集环境检查完成 ==="
}

# 执行主函数
main "$@"

2.2 MongoDB副本集配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
#!/bin/bash
# mongodb_replica_config.sh - MongoDB副本集配置脚本
# @author 运维实战

# 配置参数
REPLICA_SET_NAME="rs0"
PRIMARY_NODE="192.168.1.10:27017"
SECONDARY_NODES=("192.168.1.11:27017" "192.168.1.12:27017")
MONGODB_PORT=27017
MONGODB_DATA_DIR="/var/lib/mongodb"
MONGODB_LOG_DIR="/var/log/mongodb"

# 备份配置文件
backup_config_files() {
log "备份MongoDB配置文件..."

# 备份MongoDB配置
if [ -f "/etc/mongod.conf" ]; then
sudo cp /etc/mongod.conf /etc/mongod.conf.backup.$(date +%Y%m%d_%H%M%S)
log "MongoDB配置文件已备份"
fi
}

# 配置主节点
configure_primary_node() {
log "配置MongoDB主节点..."

cat > /etc/mongod.conf << EOF
# MongoDB主节点配置
storage:
dbPath: $MONGODB_DATA_DIR
journal:
enabled: true
wiredTiger:
engineConfig:
cacheSizeGB: 2
journalCompressor: snappy
directoryForIndexes: false
collectionConfig:
blockCompressor: snappy
indexConfig:
prefixCompression: true

systemLog:
destination: file
logAppend: true
path: $MONGODB_LOG_DIR/mongod.log
logRotate: reopen

net:
port: $MONGODB_PORT
bindIp: 0.0.0.0
maxIncomingConnections: 1000

processManagement:
timeZoneInfo: /usr/share/zoneinfo

replication:
replSetName: $REPLICA_SET_NAME

security:
authorization: enabled
keyFile: /etc/mongodb-keyfile

operationProfiling:
slowOpThresholdMs: 100
mode: slowOp

setParameter:
enableLocalhostAuthBypass: false
EOF

log "主节点配置完成"
}

# 配置从节点
configure_secondary_node() {
local node_ip=$1
log "配置MongoDB从节点: $node_ip..."

cat > /etc/mongod.conf << EOF
# MongoDB从节点配置
storage:
dbPath: $MONGODB_DATA_DIR
journal:
enabled: true
wiredTiger:
engineConfig:
cacheSizeGB: 2
journalCompressor: snappy
directoryForIndexes: false
collectionConfig:
blockCompressor: snappy
indexConfig:
prefixCompression: true

systemLog:
destination: file
logAppend: true
path: $MONGODB_LOG_DIR/mongod.log
logRotate: reopen

net:
port: $MONGODB_PORT
bindIp: 0.0.0.0
maxIncomingConnections: 1000

processManagement:
timeZoneInfo: /usr/share/zoneinfo

replication:
replSetName: $REPLICA_SET_NAME

security:
authorization: enabled
keyFile: /etc/mongodb-keyfile

operationProfiling:
slowOpThresholdMs: 100
mode: slowOp

setParameter:
enableLocalhostAuthBypass: false
EOF

log "从节点 $node_ip 配置完成"
}

# 创建认证密钥文件
create_keyfile() {
log "创建MongoDB认证密钥文件..."

# 生成随机密钥
openssl rand -base64 756 > /etc/mongodb-keyfile
chmod 600 /etc/mongodb-keyfile
chown mongodb:mongodb /etc/mongodb-keyfile

log "认证密钥文件创建完成"
}

# 创建管理员用户
create_admin_user() {
log "创建MongoDB管理员用户..."

# 等待MongoDB启动
sleep 10

# 创建管理员用户
mongo --eval "
use admin;
db.createUser({
user: 'admin',
pwd: 'admin123',
roles: [
{ role: 'userAdminAnyDatabase', db: 'admin' },
{ role: 'readWriteAnyDatabase', db: 'admin' },
{ role: 'dbAdminAnyDatabase', db: 'admin' },
{ role: 'clusterAdmin', db: 'admin' }
]
});
"

log "管理员用户创建完成"
}

# 初始化副本集
initialize_replica_set() {
log "初始化MongoDB副本集..."

# 等待所有节点启动
sleep 15

# 初始化副本集
mongo --eval "
rs.initiate({
_id: '$REPLICA_SET_NAME',
members: [
{ _id: 0, host: '$PRIMARY_NODE', priority: 2 },
{ _id: 1, host: '${SECONDARY_NODES[0]}', priority: 1 },
{ _id: 2, host: '${SECONDARY_NODES[1]}', priority: 1 }
]
});
"

log "副本集初始化完成"
}

# 启动服务
start_services() {
log "启动MongoDB服务..."

# 启动MongoDB服务
systemctl restart mongod
systemctl enable mongod

# 检查服务状态
sleep 5

if systemctl is-active --quiet mongod; then
log "MongoDB服务启动成功"
else
log "错误: MongoDB服务启动失败"
exit 1
fi
}

# 主函数
main() {
log "=== MongoDB副本集配置开始 ==="
backup_config_files
configure_primary_node
create_keyfile
start_services
create_admin_user
initialize_replica_set
log "=== MongoDB副本集配置完成 ==="
}

# 执行主函数
main "$@"

3. 副本集管理

3.1 副本集管理脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
#!/bin/bash
# mongodb_replica_manager.sh - MongoDB副本集管理脚本
# @author 运维实战

# 配置参数
REPLICA_SET_NAME="rs0"
PRIMARY_NODE="192.168.1.10:27017"
SECONDARY_NODES=("192.168.1.11:27017" "192.168.1.12:27017")
MONGODB_USER="admin"
MONGODB_PASSWORD="admin123"

# 检查副本集状态
check_replica_set_status() {
log "检查MongoDB副本集状态..."

# 检查副本集状态
mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --eval "
rs.status();
"

# 检查副本集配置
mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --eval "
rs.conf();
"

# 检查主节点
PRIMARY_STATUS=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.isMaster().ismaster;
")

if [ "$PRIMARY_STATUS" = "true" ]; then
log "主节点运行正常"
else
log "警告: 主节点状态异常"
fi

# 检查从节点
for node in "${SECONDARY_NODES[@]}"; do
SECONDARY_STATUS=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().members.find(m => m.name == '$node').stateStr;
")

if [ "$SECONDARY_STATUS" = "SECONDARY" ]; then
log "从节点 $node 运行正常"
else
log "警告: 从节点 $node 状态异常: $SECONDARY_STATUS"
fi
done
}

# 添加从节点
add_secondary_node() {
local new_node=$1
log "添加从节点: $new_node..."

# 添加从节点到副本集
mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --eval "
rs.add('$new_node');
"

# 等待节点同步
wait_for_sync_completion $new_node

log "从节点 $new_node 添加完成"
}

# 等待同步完成
wait_for_sync_completion() {
local node=$1
log "等待节点 $node 同步完成..."

local max_wait=300
local wait_time=0

while [ $wait_time -lt $max_wait ]; do
NODE_STATE=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().members.find(m => m.name == '$node').stateStr;
")

if [ "$NODE_STATE" = "SECONDARY" ]; then
log "节点 $node 同步完成"
break
fi

log "同步进行中,等待中... ($wait_time/$max_wait)"
sleep 10
wait_time=$((wait_time + 10))
done

if [ $wait_time -ge $max_wait ]; then
log "警告: 节点 $node 同步超时"
fi
}

# 移除从节点
remove_secondary_node() {
local node=$1
log "移除从节点: $node..."

# 从副本集中移除节点
mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --eval "
rs.remove('$node');
"

log "从节点 $node 移除完成"
}

# 手动故障转移
manual_failover() {
log "执行手动故障转移..."

# 获取当前主节点
CURRENT_PRIMARY=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().members.find(m => m.state == 1).name;
")

log "当前主节点: $CURRENT_PRIMARY"

# 执行故障转移
mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --eval "
rs.stepDown();
"

# 等待故障转移完成
sleep 10

# 检查新的主节点
NEW_PRIMARY=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().members.find(m => m.state == 1).name;
")

log "故障转移完成,新主节点: $NEW_PRIMARY"
}

# 检查数据一致性
check_data_consistency() {
log "检查副本集数据一致性..."

# 获取主节点数据库列表
PRIMARY_DBS=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
db.adminCommand('listDatabases').databases.forEach(function(d) { print(d.name); });
")

log "主节点数据库: $PRIMARY_DBS"

# 检查从节点数据库
for node in "${SECONDARY_NODES[@]}"; do
SECONDARY_DBS=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
db.adminCommand('listDatabases').databases.forEach(function(d) { print(d.name); });
" --host $node)

log "从节点 $node 数据库: $SECONDARY_DBS"

# 比较数据库列表
if [ "$PRIMARY_DBS" = "$SECONDARY_DBS" ]; then
log "节点 $node 数据一致性检查通过"
else
log "警告: 节点 $node 数据不一致"
fi
done
}

# 重新配置副本集
reconfigure_replica_set() {
log "重新配置副本集..."

# 获取当前配置
CURRENT_CONFIG=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.conf();
")

log "当前副本集配置: $CURRENT_CONFIG"

# 重新配置副本集
mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --eval "
cfg = rs.conf();
cfg.members[0].priority = 2;
cfg.members[1].priority = 1;
cfg.members[2].priority = 1;
rs.reconfig(cfg);
"

log "副本集重新配置完成"
}

# 主函数
main() {
case $1 in
"status")
check_replica_set_status
;;
"add")
add_secondary_node $2
;;
"remove")
remove_secondary_node $2
;;
"failover")
manual_failover
;;
"consistency")
check_data_consistency
;;
"reconfig")
reconfigure_replica_set
;;
*)
echo "用法: $0 {status|add|remove|failover|consistency|reconfig}"
echo " status - 检查副本集状态"
echo " add <node> - 添加从节点"
echo " remove <node> - 移除从节点"
echo " failover - 手动故障转移"
echo " consistency - 检查数据一致性"
echo " reconfig - 重新配置副本集"
;;
esac
}

# 执行主函数
main "$@"

3.2 数据同步监控脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
#!/bin/bash
# mongodb_sync_monitor.sh - MongoDB数据同步监控脚本
# @author 运维实战

# 监控配置
REPLICA_SET_NAME="rs0"
PRIMARY_NODE="192.168.1.10:27017"
SECONDARY_NODES=("192.168.1.11:27017" "192.168.1.12:27017")
MONGODB_USER="admin"
MONGODB_PASSWORD="admin123"
MONITOR_INTERVAL=30
LOG_FILE="/var/log/mongodb-sync-monitor.log"

# 记录监控数据
log_sync_data() {
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
local metric=$1
local value=$2
local node=$3

echo "[$timestamp] Node:$node Metric:$metric Value:$value" >> $LOG_FILE
}

# 监控副本集同步状态
monitor_replica_set_sync() {
log "开始监控MongoDB副本集同步状态..."

while true; do
# 监控主节点
PRIMARY_STATUS=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().members.find(m => m.state == 1).stateStr;
")

log_sync_data "primary_status" $PRIMARY_STATUS "primary"

# 监控从节点
for node in "${SECONDARY_NODES[@]}"; do
SECONDARY_STATUS=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().members.find(m => m.name == '$node').stateStr;
")

log_sync_data "secondary_status" $SECONDARY_STATUS $node

# 获取同步延迟
SYNC_LAG=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().members.find(m => m.name == '$node').optimeDate;
")

log_sync_data "sync_lag" $SYNC_LAG $node

# 获取复制延迟
REPL_LAG=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().members.find(m => m.name == '$node').replicationLag;
")

log_sync_data "repl_lag" $REPL_LAG $node
done

# 监控副本集整体状态
REPLICA_SET_STATUS=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().ok;
")

log_sync_data "replica_set_status" $REPLICA_SET_STATUS "replica_set"

sleep $MONITOR_INTERVAL
done
}

# 生成同步报告
generate_sync_report() {
local report_file="/var/log/mongodb-sync-report-$(date +%Y%m%d).txt"

log "生成MongoDB同步报告: $report_file"

cat > $report_file << EOF
MongoDB副本集同步报告
生成时间: $(date)
========================================

EOF

# 副本集状态
echo "副本集状态:" >> $report_file
echo "----------------------------------------" >> $report_file
mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --eval "rs.status()" >> $report_file
echo "" >> $report_file

# 副本集配置
echo "副本集配置:" >> $report_file
echo "----------------------------------------" >> $report_file
mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --eval "rs.conf()" >> $report_file
echo "" >> $report_file

# 同步统计
echo "同步统计:" >> $report_file
echo "----------------------------------------" >> $report_file

# 计算同步延迟
for node in "${SECONDARY_NODES[@]}"; do
SYNC_LAG=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().members.find(m => m.name == '$node').optimeDate;
")
echo "节点 $node 同步延迟: $SYNC_LAG" >> $report_file
done

# 数据库统计
PRIMARY_DBS=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
db.adminCommand('listDatabases').databases.forEach(function(d) { print(d.name); });
")

echo "主节点数据库: $PRIMARY_DBS" >> $report_file

log "同步报告生成完成: $report_file"
}

# 设置同步告警
setup_sync_alerts() {
log "设置MongoDB同步告警..."

# 创建告警脚本
cat > /opt/mongodb-sync-alert.sh << 'EOF'
#!/bin/bash
# MongoDB同步告警脚本

REPLICA_SET_NAME="rs0"
PRIMARY_NODE="192.168.1.10:27017"
SECONDARY_NODES=("192.168.1.11:27017" "192.168.1.12:27017")
MONGODB_USER="admin"
MONGODB_PASSWORD="admin123"

check_sync_alerts() {
# 检查主节点状态
PRIMARY_STATUS=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().members.find(m => m.state == 1).stateStr;
")

if [ "$PRIMARY_STATUS" != "PRIMARY" ]; then
echo "告警: 主节点状态异常 ($PRIMARY_STATUS)"
fi

# 检查从节点状态
for node in "${SECONDARY_NODES[@]}"; do
SECONDARY_STATUS=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().members.find(m => m.name == '$node').stateStr;
")

if [ "$SECONDARY_STATUS" != "SECONDARY" ]; then
echo "告警: 从节点 $node 状态异常 ($SECONDARY_STATUS)"
fi

# 检查同步延迟
SYNC_LAG=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().members.find(m => m.name == '$node').optimeDate;
")

if [ "$SYNC_LAG" != "null" ] && [ "$SYNC_LAG" != "undefined" ]; then
echo "告警: 从节点 $node 同步延迟过高 ($SYNC_LAG)"
fi
done

# 检查副本集整体状态
REPLICA_SET_STATUS=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().ok;
")

if [ "$REPLICA_SET_STATUS" != "1" ]; then
echo "告警: 副本集状态异常 ($REPLICA_SET_STATUS)"
fi
}

# 每5分钟检查一次
while true; do
check_sync_alerts
sleep 300
done
EOF

chmod +x /opt/mongodb-sync-alert.sh

# 启动告警服务
nohup /opt/mongodb-sync-alert.sh > /var/log/mongodb-sync-alert.log 2>&1 &

log "同步告警设置完成"
}

# 主函数
main() {
case $1 in
"monitor")
monitor_replica_set_sync
;;
"report")
generate_sync_report
;;
"alerts")
setup_sync_alerts
;;
*)
echo "用法: $0 {monitor|report|alerts}"
echo " monitor - 开始同步监控"
echo " report - 生成同步报告"
echo " alerts - 设置同步告警"
;;
esac
}

# 执行主函数
main "$@"

4. 故障转移管理

4.1 故障转移脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
#!/bin/bash
# mongodb_failover_manager.sh - MongoDB故障转移管理脚本
# @author 运维实战

# 配置参数
REPLICA_SET_NAME="rs0"
PRIMARY_NODE="192.168.1.10:27017"
SECONDARY_NODES=("192.168.1.11:27017" "192.168.1.12:27017")
MONGODB_USER="admin"
MONGODB_PASSWORD="admin123"

# 检查故障转移状态
check_failover_status() {
log "检查MongoDB故障转移状态..."

# 获取当前主节点
CURRENT_PRIMARY=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().members.find(m => m.state == 1).name;
")

log "当前主节点: $CURRENT_PRIMARY"

# 获取副本集状态
REPLICA_SET_STATUS=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().ok;
")

log "副本集状态: $REPLICA_SET_STATUS"

# 获取所有节点状态
mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --eval "
rs.status().members.forEach(function(member) {
print('节点: ' + member.name + ', 状态: ' + member.stateStr + ', 优先级: ' + member.priority);
});
"
}

# 手动故障转移
manual_failover() {
log "执行手动故障转移..."

# 获取当前主节点
CURRENT_PRIMARY=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().members.find(m => m.state == 1).name;
")

log "当前主节点: $CURRENT_PRIMARY"

# 执行故障转移
mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --eval "
rs.stepDown();
"

# 等待故障转移完成
wait_for_failover_completion $CURRENT_PRIMARY

log "手动故障转移完成"
}

# 等待故障转移完成
wait_for_failover_completion() {
local old_primary=$1
local max_wait=300
local wait_time=0

log "等待故障转移完成..."

while [ $wait_time -lt $max_wait ]; do
NEW_PRIMARY=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().members.find(m => m.state == 1).name;
")

if [ "$NEW_PRIMARY" != "$old_primary" ]; then
log "故障转移成功: $old_primary -> $NEW_PRIMARY"
break
fi

log "故障转移进行中,等待中... ($wait_time/$max_wait)"
sleep 10
wait_time=$((wait_time + 10))
done

if [ $wait_time -ge $max_wait ]; then
log "警告: 故障转移超时"
fi
}

# 测试故障转移
test_failover() {
log "测试MongoDB故障转移..."

# 记录测试开始时间
local test_start=$(date +%s)

# 获取当前主节点
CURRENT_PRIMARY=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().members.find(m => m.state == 1).name;
")

log "测试前主节点: $CURRENT_PRIMARY"

# 执行故障转移
manual_failover

# 记录测试结束时间
local test_end=$(date +%s)
local test_duration=$((test_end - test_start))

log "故障转移测试完成,耗时: ${test_duration}秒"

# 验证故障转移结果
verify_failover_result
}

# 验证故障转移结果
verify_failover_result() {
log "验证故障转移结果..."

# 检查新主节点状态
NEW_PRIMARY=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().members.find(m => m.state == 1).name;
")

log "新主节点: $NEW_PRIMARY"

# 检查新主节点连接
if mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --host $NEW_PRIMARY --eval "db.runCommand('ping')" > /dev/null 2>&1; then
log "新主节点 $NEW_PRIMARY 连接正常"
else
log "错误: 新主节点 $NEW_PRIMARY 连接失败"
fi

# 检查从节点状态
for node in "${SECONDARY_NODES[@]}"; do
if [ "$node" != "$NEW_PRIMARY" ]; then
SECONDARY_STATUS=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().members.find(m => m.name == '$node').stateStr;
")

if [ "$SECONDARY_STATUS" = "SECONDARY" ]; then
log "从节点 $node 状态正常"
else
log "警告: 从节点 $node 状态异常: $SECONDARY_STATUS"
fi
fi
done

# 检查数据一致性
check_data_consistency_after_failover
}

# 检查故障转移后数据一致性
check_data_consistency_after_failover() {
log "检查故障转移后数据一致性..."

NEW_PRIMARY=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().members.find(m => m.state == 1).name;
")

# 获取主节点数据库列表
PRIMARY_DBS=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
db.adminCommand('listDatabases').databases.forEach(function(d) { print(d.name); });
" --host $NEW_PRIMARY)

log "新主节点数据库: $PRIMARY_DBS"

# 检查从节点数据库
for node in "${SECONDARY_NODES[@]}"; do
if [ "$node" != "$NEW_PRIMARY" ]; then
SECONDARY_DBS=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
db.adminCommand('listDatabases').databases.forEach(function(d) { print(d.name); });
" --host $node)

log "从节点 $node 数据库: $SECONDARY_DBS"

if [ "$PRIMARY_DBS" = "$SECONDARY_DBS" ]; then
log "从节点 $node 数据一致性检查通过"
else
log "警告: 从节点 $node 数据不一致"
fi
fi
done
}

# 恢复原主节点
restore_original_primary() {
log "恢复原主节点..."

# 获取当前主节点
CURRENT_PRIMARY=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().members.find(m => m.state == 1).name;
")

# 获取原主节点(假设是第一个节点)
ORIGINAL_PRIMARY=$PRIMARY_NODE

log "当前主节点: $CURRENT_PRIMARY"
log "原主节点: $ORIGINAL_PRIMARY"

if [ "$CURRENT_PRIMARY" != "$ORIGINAL_PRIMARY" ]; then
# 执行故障转移回原主节点
mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --eval "
rs.stepDown();
"

log "故障转移回原主节点命令已发送"

# 等待恢复完成
wait_for_failover_completion $CURRENT_PRIMARY
else
log "当前主节点就是原主节点,无需恢复"
fi
}

# 主函数
main() {
case $1 in
"status")
check_failover_status
;;
"failover")
manual_failover
;;
"test")
test_failover
;;
"restore")
restore_original_primary
;;
*)
echo "用法: $0 {status|failover|test|restore}"
echo " status - 检查故障转移状态"
echo " failover - 手动故障转移"
echo " test - 测试故障转移"
echo " restore - 恢复原主节点"
;;
esac
}

# 执行主函数
main "$@"

5. 监控告警

5.1 副本集监控脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
#!/bin/bash
# mongodb_replica_monitor.sh - MongoDB副本集监控脚本
# @author 运维实战

# 监控配置
REPLICA_SET_NAME="rs0"
PRIMARY_NODE="192.168.1.10:27017"
SECONDARY_NODES=("192.168.1.11:27017" "192.168.1.12:27017")
MONGODB_USER="admin"
MONGODB_PASSWORD="admin123"
MONITOR_INTERVAL=30
LOG_FILE="/var/log/mongodb-replica-monitor.log"

# 记录监控数据
log_monitor_data() {
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
local metric=$1
local value=$2
local node=$3

echo "[$timestamp] Node:$node Metric:$metric Value:$value" >> $LOG_FILE
}

# 监控副本集状态
monitor_replica_set_status() {
log "开始监控MongoDB副本集状态..."

while true; do
# 监控主节点
PRIMARY_STATUS=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().members.find(m => m.state == 1).stateStr;
")

log_monitor_data "primary_status" $PRIMARY_STATUS "primary"

# 监控从节点
for node in "${SECONDARY_NODES[@]}"; do
SECONDARY_STATUS=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().members.find(m => m.name == '$node').stateStr;
")

log_monitor_data "secondary_status" $SECONDARY_STATUS $node

# 获取同步延迟
SYNC_LAG=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().members.find(m => m.name == '$node').optimeDate;
")

log_monitor_data "sync_lag" $SYNC_LAG $node

# 获取复制延迟
REPL_LAG=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().members.find(m => m.name == '$node').replicationLag;
")

log_monitor_data "repl_lag" $REPL_LAG $node
done

# 监控副本集整体状态
REPLICA_SET_STATUS=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().ok;
")

log_monitor_data "replica_set_status" $REPLICA_SET_STATUS "replica_set"

# 监控连接数
CONNECTIONS=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
db.serverStatus().connections.current;
")

log_monitor_data "connections" $CONNECTIONS "primary"

# 监控操作数
OPERATIONS=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
db.serverStatus().opcounters.total;
")

log_monitor_data "operations" $OPERATIONS "primary"

sleep $MONITOR_INTERVAL
done
}

# 生成副本集报告
generate_replica_set_report() {
local report_file="/var/log/mongodb-replica-set-report-$(date +%Y%m%d).txt"

log "生成MongoDB副本集报告: $report_file"

cat > $report_file << EOF
MongoDB副本集监控报告
生成时间: $(date)
========================================

EOF

# 副本集状态
echo "副本集状态:" >> $report_file
echo "----------------------------------------" >> $report_file
mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --eval "rs.status()" >> $report_file
echo "" >> $report_file

# 副本集配置
echo "副本集配置:" >> $report_file
echo "----------------------------------------" >> $report_file
mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --eval "rs.conf()" >> $report_file
echo "" >> $report_file

# 服务器状态
echo "服务器状态:" >> $report_file
echo "----------------------------------------" >> $report_file
mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --eval "db.serverStatus()" >> $report_file
echo "" >> $report_file

# 副本集统计
echo "副本集统计:" >> $report_file
echo "----------------------------------------" >> $report_file

# 计算同步延迟
for node in "${SECONDARY_NODES[@]}"; do
SYNC_LAG=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().members.find(m => m.name == '$node').optimeDate;
")
echo "节点 $node 同步延迟: $SYNC_LAG" >> $report_file
done

# 连接统计
CONNECTIONS=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
db.serverStatus().connections.current;
")
echo "当前连接数: $CONNECTIONS" >> $report_file

# 操作统计
OPERATIONS=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
db.serverStatus().opcounters.total;
")
echo "总操作数: $OPERATIONS" >> $report_file

log "副本集报告生成完成: $report_file"
}

# 设置副本集告警
setup_replica_set_alerts() {
log "设置MongoDB副本集告警..."

# 创建告警脚本
cat > /opt/mongodb-replica-set-alert.sh << 'EOF'
#!/bin/bash
# MongoDB副本集告警脚本

REPLICA_SET_NAME="rs0"
PRIMARY_NODE="192.168.1.10:27017"
SECONDARY_NODES=("192.168.1.11:27017" "192.168.1.12:27017")
MONGODB_USER="admin"
MONGODB_PASSWORD="admin123"

check_replica_set_alerts() {
# 检查主节点状态
PRIMARY_STATUS=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().members.find(m => m.state == 1).stateStr;
")

if [ "$PRIMARY_STATUS" != "PRIMARY" ]; then
echo "告警: 主节点状态异常 ($PRIMARY_STATUS)"
fi

# 检查从节点状态
for node in "${SECONDARY_NODES[@]}"; do
SECONDARY_STATUS=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().members.find(m => m.name == '$node').stateStr;
")

if [ "$SECONDARY_STATUS" != "SECONDARY" ]; then
echo "告警: 从节点 $node 状态异常 ($SECONDARY_STATUS)"
fi

# 检查同步延迟
SYNC_LAG=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().members.find(m => m.name == '$node').optimeDate;
")

if [ "$SYNC_LAG" != "null" ] && [ "$SYNC_LAG" != "undefined" ]; then
echo "告警: 从节点 $node 同步延迟过高 ($SYNC_LAG)"
fi
done

# 检查副本集整体状态
REPLICA_SET_STATUS=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
rs.status().ok;
")

if [ "$REPLICA_SET_STATUS" != "1" ]; then
echo "告警: 副本集状态异常 ($REPLICA_SET_STATUS)"
fi

# 检查连接数
CONNECTIONS=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
db.serverStatus().connections.current;
")

if [ $CONNECTIONS -gt 1000 ]; then
echo "告警: 连接数过高 ($CONNECTIONS)"
fi

# 检查操作数
OPERATIONS=$(mongo -u $MONGODB_USER -p $MONGODB_PASSWORD --authenticationDatabase admin --quiet --eval "
db.serverStatus().opcounters.total;
")

if [ $OPERATIONS -gt 1000000 ]; then
echo "告警: 操作数过高 ($OPERATIONS)"
fi
}

# 每5分钟检查一次
while true; do
check_replica_set_alerts
sleep 300
done
EOF

chmod +x /opt/mongodb-replica-set-alert.sh

# 启动告警服务
nohup /opt/mongodb-replica-set-alert.sh > /var/log/mongodb-replica-set-alert.log 2>&1 &

log "副本集告警设置完成"
}

# 主函数
main() {
case $1 in
"monitor")
monitor_replica_set_status
;;
"report")
generate_replica_set_report
;;
"alerts")
setup_replica_set_alerts
;;
*)
echo "用法: $0 {monitor|report|alerts}"
echo " monitor - 开始副本集监控"
echo " report - 生成副本集报告"
echo " alerts - 设置副本集告警"
;;
esac
}

# 执行主函数
main "$@"

6. 总结

6.1 副本集最佳实践

  1. 副本集配置: 合理配置副本集参数,确保数据同步的可靠性
  2. 故障转移: 建立完善的故障转移机制,确保服务的高可用性
  3. 数据一致性: 定期检查副本集内数据的一致性
  4. 监控告警: 建立全面的监控体系,及时发现和处理问题
  5. 性能优化: 优化MongoDB配置,提高系统性能

6.2 关键指标监控

  • 副本集状态: 监控主从节点状态
  • 同步延迟: 监控数据同步延迟
  • 连接数: 监控客户端连接数
  • 操作数: 监控数据库操作数
  • 故障转移: 监控故障转移事件

6.3 运维工具推荐

  1. 监控工具: MongoDB Compass, MongoDB Atlas
  2. 告警工具: 自定义告警脚本
  3. 管理工具: mongo shell, MongoDB Ops Manager
  4. 备份工具: mongodump, mongorestore
  5. 诊断工具: mongostat, mongotop

通过本文的MongoDB副本集运维实战指南,您可以建立完善的MongoDB副本集运维体系,确保系统的高可用性和数据一致性。记住,副本集的运维需要持续关注主从同步状态和故障转移机制,确保在任何情况下都能提供稳定的服务。