mongodb: docker-compose一主两从一仲裁副本集模式
一主两从一仲裁副本集模式是比较完善的数据库优化方案,和crontab定时shell脚本热备数据就更切合
为什么要仲裁节点:假如主从投票对决有可能公平撕逼,永远无法落实到底是谁,因此加入对主从加入仲裁节点,最简单的是主从仲裁各一个节点
一 基本部署
docker-compose.yaml 文件==》执行docker-compose up
version: '2' services: master: image: mongo:3.4 container_name: master volumes: - /Users/Alex/Desktop/docker_volumes/master:/data/db - /Users/Alex/Desktop/docker_volumes/master/mongod.log:/var/log/mongodb/mongod.log command: /bin/sh -c 'mongod --dbpath /data/db --replSet annosys' restart: always slave01: image: mongo:3.4 container_name: slave01 volumes: - /Users/Alex/Desktop/docker_volumes/slave01:/data/db - /Users/Alex/Desktop/docker_volumes/master/mongod.log:/var/log/mongodb/mongod.log command: /bin/sh -c 'mongod --dbpath /data/db --replSet annosys' restart: always slave02: image: mongo:3.4 container_name: slave02 volumes: - /Users/Alex/Desktop/docker_volumes/slave02:/data/db - /Users/Alex/Desktop/docker_volumes/master/mongod.log:/var/log/mongodb/mongod.log command: /bin/sh -c 'mongod --dbpath /data/db --replSet annosys' restart: always myarbiter: image: mongo:3.4 container_name: myarbiter command: mongod --dbpath /data/db --replSet annosys --smallfiles
(1)进入master节点,根据副本集名称初始化关联所有主从以及仲裁节点
docker-compose exec master mongo
初始化各个节点:
use admin
config={ _id:"annosys", members:[
{_id:0,host:'master:27017',priority:5},
{_id:1,host:'slave01:27017',priority:3},
{_id:2,host:'slave02:27017',priority:2},
{_id:3,host:'myarbiter:27017',arbiterOnly:true}]
}
rs.initiate(config)
查看配置与副本级状态:
rs.conf()
rs.status()
annosys:PRIMARY> rs.conf() { "_id" : "annosys", "version" : 1, "protocolVersion" : NumberLong(1), "members" : [ { "_id" : 0, "host" : "master:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 5, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 1, "host" : "slave01:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 3, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 2, "host" : "slave02:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 2, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 3, "host" : "myarbiter:27017", "arbiterOnly" : true, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 } ], "settings" : { "chainingAllowed" : true, "heartbeatIntervalMillis" : 2000, "heartbeatTimeoutSecs" : 10, "electionTimeoutMillis" : 10000, "catchUpTimeoutMillis" : 60000, "getLastErrorModes" : { }, "getLastErrorDefaults" : { "w" : 1, "wtimeout" : 0 }, "replicaSetId" : ObjectId("5c3eed9f4f69bfb3feba5b5c") } } annosys:PRIMARY> rs.status() { "set" : "annosys", "date" : ISODate("2019-01-16T08:43:10.784Z"), "myState" : 1, "term" : NumberLong(1), "syncingTo" : "", "syncSourceHost" : "", "syncSourceId" : -1, "heartbeatIntervalMillis" : NumberLong(2000), "optimes" : { "lastCommittedOpTime" : { "ts" : Timestamp(1547628187, 1), "t" : NumberLong(1) }, "appliedOpTime" : { "ts" : Timestamp(1547628187, 1), "t" : NumberLong(1) }, "durableOpTime" : { "ts" : Timestamp(1547628187, 1), "t" : NumberLong(1) } }, "members" : [ { "_id" : 0, "name" : "master:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 397, "optime" : { "ts" : Timestamp(1547628187, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2019-01-16T08:43:07Z"), "syncingTo" : "", "syncSourceHost" : "", "syncSourceId" : -1, "infoMessage" : "", "electionTime" : Timestamp(1547627945, 1), "electionDate" : ISODate("2019-01-16T08:39:05Z"), "configVersion" : 1, "self" : true, "lastHeartbeatMessage" : "" }, { "_id" : 1, "name" : "slave01:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 255, "optime" : { "ts" : Timestamp(1547628187, 1), "t" : NumberLong(1) }, "optimeDurable" : { "ts" : Timestamp(1547628187, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2019-01-16T08:43:07Z"), "optimeDurableDate" : ISODate("2019-01-16T08:43:07Z"), "lastHeartbeat" : ISODate("2019-01-16T08:43:10.044Z"), "lastHeartbeatRecv" : ISODate("2019-01-16T08:43:10.533Z"), "pingMs" : NumberLong(0), "lastHeartbeatMessage" : "", "syncingTo" : "master:27017", "syncSourceHost" : "master:27017", "syncSourceId" : 0, "infoMessage" : "", "configVersion" : 1 }, { "_id" : 2, "name" : "slave02:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 255, "optime" : { "ts" : Timestamp(1547628187, 1), "t" : NumberLong(1) }, "optimeDurable" : { "ts" : Timestamp(1547628187, 1), "t" : NumberLong(1) }, "optimeDate" : ISODate("2019-01-16T08:43:07Z"), "optimeDurableDate" : ISODate("2019-01-16T08:43:07Z"), "lastHeartbeat" : ISODate("2019-01-16T08:43:10.044Z"), "lastHeartbeatRecv" : ISODate("2019-01-16T08:43:10.520Z"), "pingMs" : NumberLong(0), "lastHeartbeatMessage" : "", "syncingTo" : "master:27017", "syncSourceHost" : "master:27017", "syncSourceId" : 0, "infoMessage" : "", "configVersion" : 1 }, { "_id" : 3, "name" : "myarbiter:27017", "health" : 1, "state" : 7, "stateStr" : "ARBITER", "uptime" : 255, "lastHeartbeat" : ISODate("2019-01-16T08:43:10.044Z"), "lastHeartbeatRecv" : ISODate("2019-01-16T08:43:06.195Z"), "pingMs" : NumberLong(0), "lastHeartbeatMessage" : "", "syncingTo" : "", "syncSourceHost" : "", "syncSourceId" : -1, "infoMessage" : "", "configVersion" : 1 } ], "ok" : 1 }
(2)进入slave01,slave02节点,根据master节点查看数据是否同步
插入信息到主节点:
docker-compose exec master mongo
use test
db.test.insert({msg: 'this is from primary', ts: new Date()})
在副本集中检测信息是否同步:
docker-compose exec slave01 mongo
db.test.find()
出现Error: error: {
"ok" : 0,
"errmsg" : "not master and slaveOk=false",
"code" : 13435,
"codeName" : "NotMasterNoSlaveOk"
}
执行:rs.slaveOk()
use test
db.test.find()
docker-compose exec slave02 mongo
db.test.find()
出现Error: error: {
"ok" : 0,
"errmsg" : "not master and slaveOk=false",
"code" : 13435,
"codeName" : "NotMasterNoSlaveOk"
}
执行rs.slaveOk()
use test
db.test.find()
如果发现数据同步,证明设置正常
二 故障测试模拟
停止主节点模拟故障:
docker-compose stop master
分别查看其它节点的信息:
docker-compose exec slave01 mongo
docker-compose exec slave02 mongo
直接出现annosys:PRIMARY>显得从节点变成主节点了,实际进入插入一条数据
db.test.insert({msg: 'this is from primary change xxx', ts: new Date()})
如果上面的slave01变成了primary,那可以测试slave02的数据是否同步,步骤如上
rs.slaveOk()
db.test.find()
恢复启动master:docker-compose start master,会重新变成primary。
但是其实使用查看主节点的最简单的做法:rs.status()
三 实际使用副本集数据库的用法
整体步骤:master读写,副本可读
(1)docker-compose exec master mongo
初始化各个节点:
use admin
config={ _id:"annosys", members:[
{_id:0,host:'master:27017',priority:5},
{_id:1,host:'slave01:27017',priority:3},
{_id:2,host:'slave02:27017',priority:2},
{_id:3,host:'myarbiter:27017',arbiterOnly:true}]
}
rs.initiate(config)
查看配置与副本级状态:
rs.conf()
rs.status()
(2)docker-compose exec slave01 mongo
rs.slaveOk()
(2)docker-compose exec slave02 mongo
rs.slaveOk()
暴露主节点master的端口直接连接:
version: '2' services: master: image: mongo:3.4 container_name: master ports: - 28017:27017 volumes: - /Users/Alex/Desktop/docker_volumes/master:/data/db - /Users/Alex/Desktop/docker_volumes/master/mongod.log:/var/log/mongodb/mongod.log command: /bin/sh -c 'mongod --dbpath /data/db --replSet annosys' restart: always slave01: image: mongo:3.4 container_name: slave01 volumes: - /Users/Alex/Desktop/docker_volumes/slave01:/data/db - /Users/Alex/Desktop/docker_volumes/master/mongod.log:/var/log/mongodb/mongod.log command: /bin/sh -c 'mongod --dbpath /data/db --replSet annosys' restart: always slave02: image: mongo:3.4 container_name: slave02 volumes: - /Users/Alex/Desktop/docker_volumes/slave02:/data/db - /Users/Alex/Desktop/docker_volumes/master/mongod.log:/var/log/mongodb/mongod.log command: /bin/sh -c 'mongod --dbpath /data/db --replSet annosys' restart: always myarbiter: image: mongo:3.4 container_name: myarbiter command: mongod --dbpath /data/db --replSet annosys --smallfiles
比如flask:
app.config["MONGO_URI"] = "mongodb://localhost:28017/testshop?replicaSet=annosys"
app.config['MONGODB_SETTINGS'] = {
'host': 'localhost',
'port': 28017,
'db': 'testshop',
'connect': False
}
四 附录备份mongodb-shell脚本
1 #!/bin/sh 2 3 /usr/bin/mongo --version > /data/www/backup/mongo_version.txt 4 DUMP=/usr/bin/mongodump #mongodump path 5 OUT_DIR=/data/www/backup/temp #temp backup file 6 TAR_DIR=/data/www/backup/final #backup file 7 8 DATE=`date +%Y_%m_%d_%H_%M_%S` 9 TAR_BAK="mongodb_backup_$DATE.tar.gz" 10 11 DB_HOST=localhost:27017 12 DB_USER= 13 DB_PASS= 14 DAYS=15 15 16 cd $OUT_DIR 17 rm -rf $OUT_DIR/* 18 mkdir -p $OUT_DIR/$DATE 19 20 21 echo "backup start" >> /data/www/backup/bp.log 22 # $DUMP -h $DB_HOST --authenticationDatabase "admin" -o $OUT_DIR/$DATE 23 $DUMP -h $DB_HOST -o $OUT_DIR/$DATE 24 echo "ing...." >> /data/www/backup/bp.log 25 tar -zcvf $TAR_DIR/$TAR_BAK $OUT_DIR/$DATE 26 find $TAR_DIR/ -mtime +$DAYS -delete 27 echo "backup end" >> /data/www/backup/bp.log 28 29 cd /data/www/backup 30 cmd=`date +%Y-%m-%d:%H:%M:%S` 31 cmd01=${cmd} 32 echo "$cmd01" >> /data/www/backup/bp.log 33 34 exit 35 36 # 挂载docker-compose:/data/www/backup: /data/www/backup 37 # sudo docker exec -it 0be535e8f2ba /bin/bash 38 # crontab -e 每隔4小时的第一分钟开始备份一次 39 # 1 */4 * * * /data/www/backup/monbp.sh >> /data/www/backup/bp.log 2>&1
使用主从或者副本集模式就可以:
$DUMP -h $DB_HOST --authenticationDatabase "admin" -o $OUT_DIR/$DATE --oplog