1 简介
官方3.4手册里明确说明,主从复制将被副本集全部替代,后续版本应该不在支持主从复制,因此建议不要主从复制了,直接用副本集。
IMPORTANT
Replica sets replace master-slave replication for most use cases. If possible, use replica sets rather than master-slave replication for all new production deployments. This documentation remains to support legacy deployments and for archival purposes only.
MongoDB的replica set架构是通过一个日志来存储写操作的,这个日志就叫做 oplog 。oplog.rs 是一个固定长度的 Capped Collection,它存在于local数据库中,用于记录replicaSets操作日志。在默认情况下,对于64位的MongoDB,oplog是比较大的,可以达到5%的磁盘空间,oplog的大小是可以通过mongod的
-oplogSize (单位:MB)来改变oplog的日志大小。
–smallfiles 声明使用更小的默认文件。
经测试,mongo3 shell无法连接到mongo4。
在docker中部署副本集需要考虑容器的ip问题,可以使用命令 “docker exec 6bc cat /etc/hosts” 得到容器IP,
再用 “rs.initiate({_id: “rs”, members: [{_id: 0, host: “x.x.x.x:27017″}] })” 初始化副本集。
如果使用的是 driver: overlay 网路,那么可以不去获取容器IP,直接用 “falmongo” 名字就好了。
2 Docker 镜像副本集
部署副本集
#1. 启动三个 mongo 服务,端口分别是 27011、27012、27013 docker run -it --rm -p 27011:27011 -v /home/mongo/rsdata27011:/data/db mongo:4.4 bash -c 'mongod --dbpath=/data/db --bind_ip_all --port 27011 --replSet rs0' docker run -it --rm -p 27012:27012 -v /home/mongo/rsdata27012:/data/db mongo:4.4 bash -c 'mongod --dbpath=/data/db --bind_ip_all --port 27012 --replSet rs0' docker run -it --rm -p 27013:27013 -v /home/mongo/rsdata27013:/data/db mongo:4.4 bash -c 'mongod --dbpath=/data/db --bind_ip_all --port 27013 --replSet rs0' #2. 用客户端连任意一台 mongo 服务,准备创建副本集 docker run -it --rm mongo:4.4 bash -c 'mongo --host 192.168.3.222 --port 27011' #3. 连上后,创建副本集 rs.initiate({_id: 'rs0', members: [{_id: 0, host: '192.168.3.222:27011'},{_id: 1, host: '192.168.3.222:27012'},{_id: 2, host: '192.168.3.222:27013'}] }) #4. 查看副本集状态,确认 master 主节点是哪一台(找 members 下的 stateStr 值,PRIMARY 的为主节点),我这里 27011 这台是主节点 rs.status() #5. 验证副本集 #(1)主节点 Primary 插入数据 docker run -it --rm mongo:4.4 bash -c 'mongo --host 192.168.3.222 --port 27011' db.test.insert({ a:1 }) #(2)从节点 Secondary 读取数据,默认是不允许读,因此会报错:not master and slaveOk=false docker run -it --rm mongo:4.4 bash -c 'mongo --host 192.168.3.222 --port 27012' db.test.find({}) #6. 关闭 27011 这台主节点,27012 上查看副本集状态,发现 27012 变成 Primary 了,并且可以查询数据 rs.status() db.test.find({}) #7. 再次恢复 27011 这台,发现变成 SECONDARY 了。
测试效果
三个mongo服务 kill掉 primary,选举产生了新primary,policy仍工作正常;再kill掉primary剩一台,此时无法选举产生新primary,policy 也不工作;快速重新启动两台kill掉的,policy恢复工作;policy mongo Timeout后再重启两台kill掉的,即使选举出primary policy也不工作了,需重启policy才恢复。
默认超时时间是30秒,可以改成10分钟,只需添加一个连接字符串选项 serverSelectionTimeoutMS=600000。
注意点
开启副本集后,若启动2台mongo进程都一直报 Heartbeat 错误,因为没有足够的裁判员来选举出 PRIMARY 节点。
启动第一台时一直报错:Heartbeat failed after max retries,客户端连上看到是 SECONDARY 节点。
启动第二台时一直报错:Heartbeat failed after max retries,客户端连上看到是 SECONDARY 节点。
启动第三台后前面两台都不报错了,客户端连上看到的也是 SECONDARY 节点,此时再去看第二台,发现已变成 PRIMARY 节点。
启用副本集认证
- 登陆主节点,创建账号
use admin db.dropUser('faasec') db.createUser({user: 'faa', pwd: 'admin.0512#', roles: [{role: 'clusterAdmin', db: 'admin'},{role: "userAdminAnyDatabase", db: "admin"},"readWriteAnyDatabase"]}) #有时候客户那要求用最小权限,如: # admin用于认证、local用于读取oplog,faapolicy是业务数据库 db.createUser({user: 'faa', pwd: 'admin.0512#', roles: [{role: 'readWrite', db: 'admin'},{role: 'readWrite', db: 'faapolicy'},{role: "readWrite", db: "local"}]})
2. 登陆主节点,查看创建的账号,即使主节点发生了IP漂移也没关系
use admin db.system.users.find()
3. 创建 keyfile
openssl rand -base64 666 > /home/mongo/keyfile
4. 使用 keyfile 启用副本集认证(请先关闭三个mongo服务)
docker run -it --rm -p 27011:27011 -v /home/mongo/rsdata27011:/data/db -v /home/mongo/keyfile:/home/mongo/keyfile mongo:4.4 bash -c 'chown 600 /home/mongo/keyfile && mongod --dbpath=/data/db --keyFile /home/mongo/keyfile --bind_ip_all --port 27011 --replSet rs0' docker run -it --rm -p 27012:27012 -v /home/mongo/rsdata27012:/data/db -v /home/mongo/keyfile:/home/mongo/keyfile mongo:4.4 bash -c 'chown 600 /home/mongo/keyfile && mongod --dbpath=/data/db --keyFile /home/mongo/keyfile --bind_ip_all --port 27012 --replSet rs0' docker run -it --rm -p 27013:27013 -v /home/mongo/rsdata27013:/data/db -v /home/mongo/keyfile:/home/mongo/keyfile mongo:4.4 bash -c 'chown 600 /home/mongo/keyfile && mongod --dbpath=/data/db --keyFile /home/mongo/keyfile --bind_ip_all --port 27013 --replSet rs0'
5. 登陆主节点,查询数据直接报错:command find requires authentication
docker run -it --rm mongo:4.4 bash -c 'mongo --host 192.168.3.222 --port 27011' use admin db.system.users.find()
6. 登陆主节点,验证后查询数据不报错了
use admin db.auth('faasec','admin.0512#') db.system.users.find()
写成脚本
mongo-entrypoint.sh:
#!/bin/bash _term() { # 测试请执行 docker stop <containerId>,会发送 SIGTERM 信号 echo "> mongod --shutdown --dbpath /data/db" mongod --shutdown --dbpath /data/db } trap _term HUP INT QUIT TERM set -e set -- $@ --dbpath /data/db --bind_ip 0.0.0.0 --port 27017 --replSet rs --oplogSize 128 echo ">>> starting mongo..." exec "$@" & child1=$! # 这里使用 wait 让 sleep 后台运行,以便在 sleep 时可以接收到 SIGTERM 信号。sleep 前台运行,调用 docker stop 是接收不到的。 # 运行速度慢的机器需要更久的 sleep sleep 30s & child2=$! wait $child2 echo ">>> set docker ip to replica set ..." #ipAddr=$(cat /etc/hosts | grep `hostname` | awk '{print $1}') #echo '>>> hostname:' $ipAddr echo ">>> initiate replica set..." mongo ${USR_LOCAL_BIN}/script.js echo ">>> initialization is finished." wait $child1
script.js:
rs.initiate({_id: "rs", members: [{_id: 0, host: "falmongo:27017"}] });
数据是如何复制的?
当一个修改操作,无论是插入、更新或删除,到达主节点时,它对数据的操作将被记录下来(经过一些必要的转换),这些记录称为 oplog。从节点通过在主节点上打开一个 tailable 游标不断获取新进入主节点的 oplog,并在自己的数据上回放,以此保持跟主节点的数据一致。无论 Primary、Secondary都是有 oplog 的。