1. 定义
一般只要生产环境就需要考虑冗余设计,保证在某一台服务器由于某种原因宕机后服务还可以正常运行。
mongo副本集是一组服务器,其中有一个主服务器(primary),用于处理客户端请求;还有多个备份服务器(secondary),用于保存主服务器的数据副本。如果主服务器崩溃了,备份服务器就会自动将其中一个成员升级为新的主服务器。
副本集在选主的过程中有一个很重要的概念叫“大多数(majority)”,选择主节点需要由大多数决定,主节点只有在得到大多数支持时才能继续作为主节点,写操作被复制到大多数成员时,这个写操作就是安全的。这里的大多数被定义为“副本中一半以上成员”。这些副本集中的成员挂了或者不可用,并不会影响“大多数”,因为“大多数”是基于副本集的配置来计算的,如下表:
那怎样才能算大多数
1 | 1 |
2 | 2 |
3 | 2 |
4 | 3 |
5 | 3 |
6 | 4 |
7 | 4 |
副本集中通过设置priority的值来决定优先权的大小,这个值的范围是0--100,值越大,成为主的优先权越高。
目前读写分离的几种模式:
primary | 主节点,默认模式,读操作只在主节点,如果主节点不可用,报错或者抛出异常。 |
primaryPreferred | 首选主节点,大多情况下读操作在主节点,如果主节点不可用,如故障转移,读操作在从节点 |
secondary | 从节点,读操作只在从节点, 如果从节点不可用,报错或者抛出异常。 |
secondaryPreferred | 首选从节点,大多情况下读操作在从节点,特殊情况(如单主节点架构)读操作在主节点。 |
nearest | 最邻近节点,读操作在最邻近的成员,可能是主节点或者从节点 |
2. 安装
有了以上概念,就开始来部署副本集,我们设计3个mongo节点,分别如下:
名称 | 数据存储位置 | 优先级 | docker映射端口 | mongo启动端口 |
mongo1 | /data/mongo/data1 | 1 | 27117 | 27017 |
mongo2 | /data/mongo/data2 | 0.5 | 27217 | 27017 |
mongo3 | /data/mongo/data3 | 0.8 | 27317 | 27017 |
那么开始启动mongo容器:
docker run --name mongo1 -v /data/mongo/data1:/data/db -d -p 27117:27017 mongo:3.4.2 --replSet ms
docker run --name mongo2 -v /data/mongo/data2:/data/db -d -p 27217:27017 mongo:3.4.2 --replSet ms
docker run --name mongo3 -v /data/mongo/data3:/data/db -d -p 27317:27017 mongo:3.4.2 --replSet ms
3个均启动完成,随便进入到一个容器里,如mongo1,并连接到数据库
[root@vhost18 mongo]# docker exec -it mongo1 bash
root@a04f48f03f1e:/#
root@a04f48f03f1e:/# mongo
MongoDB shell version v3.4.2
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.4.2
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
http://docs.mongodb.org/Questions? Try the support group
http://groups.google.com/group/mongodb-user
Server has startup warnings:
2018-12-24T09:06:15.245+0000 I CONTROL [initandlisten]
2018-12-24T09:06:15.246+0000 I CONTROL [initandlisten] ** WARNING: Access control is not enabled for the database.
2018-12-24T09:06:15.246+0000 I CONTROL [initandlisten] ** Read and write access to data and configuration is unrestricted.
2018-12-24T09:06:15.246+0000 I CONTROL [initandlisten]
2018-12-24T09:06:15.246+0000 I CONTROL [initandlisten]
2018-12-24T09:06:15.246+0000 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.
2018-12-24T09:06:15.246+0000 I CONTROL [initandlisten] ** We suggest setting it to 'never'
2018-12-24T09:06:15.246+0000 I CONTROL [initandlisten]
2018-12-24T09:06:15.246+0000 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.
2018-12-24T09:06:15.246+0000 I CONTROL [initandlisten] ** We suggest setting it to 'never'
2018-12-24T09:06:15.246+0000 I CONTROL [initandlisten]
>
传入配置并初始化:
config={_id:"ms",members:[{_id:0,host:"ip:27117", priority:1},{_id:1,host:"ip:27217", priority:0.5},{_id:2,host:"ip:27317", priority:0.8}]}
rs.initiate(config);
执行成功后是这样的:
节点在经过短暂的同步后,登陆mongo的控制台会显示成ms:PRIMARY,备份节点会显示成ms:SECONDARY,此时副本集就安装好了。
3. 验证
进入primary服务器,随便创建一个测试数据库和测试表数据:
ms:PRIMARY> use test
switched to db test
ms:PRIMARY> db.testCollection.insert({"key":"value"})
WriteResult({ "nInserted" : 1 })
ms:PRIMARY> db.testCollection.find()
{ "_id" : ObjectId("5c20a3d7c5d174307dc3d516"), "key" : "value" }
然后进入secondary服务器查一下数据,会出现如下结果:
ms:SECONDARY> use test
switched to db test
ms:SECONDARY> db.testCollection.find()
Error: error: {
"ok" : 0,
"errmsg" : "not master and slaveOk=false",
"code" : 13435,
"codeName" : "NotMasterNoSlaveOk"
}
这是因为secondary服务器在写多读少的应用使用Replica Sets来实现读写分离。通过在连接时指定或者在主库指定slaveOk,由secondary来分担读的压力,primary只承担写操作
此时为了检测设置为slaveOk,并重新查询一下,会出现如下结果:
ms:SECONDARY> rs.slaveOk()
ms:SECONDARY> db.testCollection.find()
{ "_id" : ObjectId("5c20a3d7c5d174307dc3d516"), "key" : "value" }
可以检测到副本集生效了。
这时候主动把primary宕机或者删除,就可以看到主节点转移到mongo3了,为什么呢,因为我们配置的时候mongo3的priority比mongo2高。
4. 开启密码验证
副本集的验证与mongo单机开启验证不同:
mongo单机版本只需要在admin库中的system.user表里加好用户后就默认开启了验证模式。
mongo副本集在admin库中的system.user表里加好用户后,需要显示开启验证模式(启动参数添加:--auth),那么副本集之间的互相反问就需要用到keyFile了。
keyFile几个注意点:
- 权限不能太大,不然会报“permissions on xxx are too open”
- 权限不能太小,不然会报“permission denied”
因此我们在创建keyFile的时候首要先把权限赋值好:
openssl rand -base64 741 > mongodb.key
chmod 600 mongodb.key
#docker启动需要把赋为999
chown 999 mongodb.key
添加用户,需要到主节点上进行添加
docker exec -it mongo1 bash
mongo
use admin
db.createUser({user: "admin",pwd: "password",roles: [ { role: "root", db: "admin" } ]})
添加好用户后重启副本集,启动带上验证和keyFile
docker rm -f mongo1 mongo2 mongo3
docker run --name mongo1 -v /data/mongo/data1:/data/db -v /data/mongo/config/mongodb.key:/mongodb.key -d -p 27117:27017 mongo:3.4.2 --replSet ms --keyFile=mongodb.key --auth
docker run --name mongo2 -v /data/mongo/data2:/data/db -v /data/mongo/config/mongodb.key:/mongodb.key -d -p 27217:27017 mongo:3.4.2 --replSet ms --keyFile=mongodb.key --auth
docker run --name mongo3 -v /data/mongo/data3:/data/db -v /data/mongo/config/mongodb.key:/mongodb.key -d -p 27317:27017 mongo:3.4.2 --replSet ms --keyFile=mongodb.key --auth
重启完成后进行验证,与上一步的验证一致。
这样带有权限验证的mongo副本集就创建完毕了。