- 什么是副本集
那什么是副本集呢?打魔兽世界总说打副本,其实这两个概念差不多一个意思。游戏里的副本是指玩家集中在高峰时间去一个场景打怪,会出现玩家暴多怪物少的情况,游戏开发商为了保证玩家的体验度,就为每一批玩家单独开放一个同样的空间同样的数量的怪物,这一个复制的场景就是一个副本,不管有多少个玩家各自在各自的副本里玩不会互相影响。
mongoDB的副本也是这个,主从模式其实就是一个单副本的应用,没有很好的扩展性和容错性。而副本集具有多个副本保证了容错性,就算一个副本挂掉了还有很多副本存在,并且解决了上面第一个问题“主节点挂掉了,整个集群内会自动切换”。难怪mongoDB官方推荐使用这种模式。我们来看看mongoDB副本集的架构图:
由图可以看到客户端连接到整个副本集,不关心具体哪一台机器是否挂掉。主服务器负责整个副本集的读写,副本集定期同步数据备份,一但主节点挂掉,副本节点就会选举一个新的主服务器,这一切对于应用服务器不需要关心。我们看一下主服务器挂掉后的架构:
副本集中的副本节点在主节点挂掉后通过心跳机制检测到后,就会在集群内发起主节点的选举机制,自动选举一位新的主服务器。
官方推荐的副本集机器数量为至少3个,那我们也按照这个数量配置测试。
规划部署
因设备有限,暂时在一台服务器上,配置3个MongoDB;在/home/mongo目录下的mongo1,mongo2,mongo3,端口分别为:28017、28018、28019
主节点:192.168.0.197:28017
从节点:192.168.0.197:28018
从节点:192.168.0.197:28019
- 启动配置
先配置mongo1
存放整个mongodb文件
mkdir /home/mongo/mongo1
存放mongodb数据文件
mkdir /home/mongo/mongo1/data
进入mongo1的bin目录
cd /home/mongo/mongo1/bin
在/home/mongo/mongo1/bin目录下编写配置文件mongod1.conf
以下为具体内容:
#数据库文件路径
dbpath=/home/mongo/mongo1/data/
#日志文件
logpath=/home/mongo/mongo1/log/mongod.log
#副本集配置变量
replSet=repset
#修改数据目录存储模式,每个数据库的文件存储在DBPATH指定目录的不同的文件夹中
directoryperdb=true
#定义端口
port=28017
#后台启动
fork=true
#以追加的模式写入日志
logappend=true
在/home/mongo/mongo1/bin目录下编写启动脚本startmongod.sh
./mongod --config mongod1.conf
mongo2,mongo3的配置方法一样,但是注意目录和端口的区别
分别启动3个数据库服务器:mongo1,mongo2,mongo3
./startmongod.sh
- 副本集
登录任意一台mongo
./mongo --port 28017 #默认的是27017,这里需要指定
使用admin数据库
>use admin
- 配置副本集
>config = { _id:"repset", members:[
... {_id:0,host:"192.168.0.197:28017"},
... {_id:1,host:"192.168.0.197:28018"},
... {_id:2,host:"192.168.0.197:28019"}]
... }
输出
{
"_id" : "repset",
"members" : [
{
"_id" : 0,
"host" : "192.168.0.197:28017"
},
{
"_id" : 1,
"host" : "192.168.0.197:28018"
},
{
"_id" : 2,
"host" : "192.168.0.197:28019"
}
]
}
- 初始化副本集
>rs.initiate(config)
输出成功
{
"info" : "Config now saved locally. Should come online in about a minute.",
"ok" : 1
}
成功之后,当前节点会出现repset:Primary>的标示
注意,如果提示失败,有可能是数据库目录/home/mongo/mongo1/data在启动之前存在多余的文件或者空白文件夹,关闭所有MongoDB,删掉data里面的东西,重新启动
如果要导入数据库文件,在初始化副本集之前,就必须以导入
比如,在data下新建edu目录,然后再edu目录下放置 edu.ns edu.0 edu.1 等数据库文件。前面配置文件mongod1.conf 里面也配置了directoryperdb=true,它的作用就是修改数据目录存储模式,每个数据库的文件存储在DBPATH指定目录的不同的文件夹中
查看集群节点的状态
repset:Primary> rs.status()
输出
{
"set" : "repset",
"date" : ISODate("2016-06-15T07:13:43Z"),
"myState" : 2,
"syncingTo" : "127.0.0.1:28017",
"members" : [
{
"_id" : 0,
"name" : "127.0.0.1:28017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 545,
"optime" : Timestamp(1465974632, 1),
"optimeDate" : ISODate("2016-06-15T07:10:32Z"),
"lastHeartbeat" : ISODate("2016-06-15T07:13:42Z"),
"lastHeartbeatRecv" : ISODate("2016-06-15T07:13:41Z"),
"pingMs" : 0,
"electionTime" : Timestamp(1465974285, 1),
"electionDate" : ISODate("2016-06-15T07:04:45Z")
},
{
"_id" : 1,
"name" : "127.0.0.1:28018",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 623,
"optime" : Timestamp(1465974632, 1),
"optimeDate" : ISODate("2016-06-15T07:10:32Z"),
"self" : true
},
{
"_id" : 2,
"name" : "127.0.0.1:28019",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 545,
"optime" : Timestamp(1465974632, 1),
"optimeDate" : ISODate("2016-06-15T07:10:32Z"),
"lastHeartbeat" : ISODate("2016-06-15T07:13:42Z"),
"lastHeartbeatRecv" : ISODate("2016-06-15T07:13:42Z"),
"pingMs" : 0,
"syncingTo" : "127.0.0.1:28017"
}
],
"ok" : 1
}
到此,副本集已经搭建成功
- 副本集数据复制功能
登录到在主节点mongo1,并新建test数据库
repset:Primary> use test
插入数据库
repset:Primary> db.testdb.insert({"test1":"testval1"})
在副本节点mongo2、mongo3上连接到mongodb查看数据是否复制过来。
使用test 数据库。
repset:SECONDARY> use test
repset:SECONDARY> show tables;
输出
Sun Dec 29 21:50:48.590 error: { "$err" : "not master and slaveOk=false", "code" : 13435 } at src/mongo/shell/query.js:128
mongodb默认是从主节点读写数据的,副本节点上不允许读,需要设置副本节点可以读。
repset:SECONDARY> db.getMongo().setSlaveOk()
可以看到数据已经复制到了副本集。
repset:SECONDARY> db.testdb.find()
输出
{ "_id" : ObjectId("52c028460c7505626a93944f"), "test1" : "testval1" }
- 副本集故障转移功能
先停掉主节点mongo1,可通过kill -2 进程数 的方法关闭MongoDB
在从节点下查看整个集群的状态
repset:SECONDARY> rs.status()
输出
{
"set" : "repset",
"date" : ISODate("2013-12-29T14:28:35Z"),
"myState" : 2,
"syncingTo" : "192.168.0.197:28018",
"members" : [
{
"_id" : 0,
"name" : "192.168.0.197:28017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 9072,
"optime" : Timestamp(1388324934, 1),
"optimeDate" : ISODate("2013-12-29T13:48:54Z"),
"self" : true
},
{
"_id" : 1,
"name" : "192.168.0.197:28018",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 7329,
"optime" : Timestamp(1388324934, 1),
"optimeDate" : ISODate("2013-12-29T13:48:54Z"),
"lastHeartbeat" : ISODate("2013-12-29T14:28:34Z"),
"lastHeartbeatRecv" : ISODate("2013-12-29T14:28:34Z"),
"pingMs" : 1,
"syncingTo" : "192.168.1.138:27017"
},
{
"_id" : 2,
"name" : "192.168.0.197:28019",
"health" : 0,
"state" : 8,
"stateStr" : "(not reachable/healthy)",
"uptime" : 0,
"optime" : Timestamp(1388324934, 1),
"optimeDate" : ISODate("2013-12-29T13:48:54Z"),
"lastHeartbeat" : ISODate("2013-12-29T14:28:35Z"),
"lastHeartbeatRecv" : ISODate("2013-12-29T14:28:23Z"),
"pingMs" : 0,
"syncingTo" : "192.168.0.197:28018"
}
],
"ok" : 1
}
mongo2的标示也会变成repset:Primary> ,即mongo2变成了主节点,故障转移功能实现
做完以上测试,就可以开始做权限和认证
- 权限
需要配置两种用户,一个是数据库用户管理员,一个是数据库管理
进入主节点
repset:Primary> use admin
配置数据库用户管理员,用来管理所有数据库的用户
repset:Primary> db.createUser(
{
user: "admin",
pwd: "password",
roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
}
)
此权限可进行的操作(暂时只知道以下的)
查看admin库的用户
repset:Primary> show users
查看整个MongoDB全部的用户
repset:Primary> db.system.users.find()
添加admin库的可读写用户
repset:Primary> db.createUser(
{
user: "test",
pwd: "123456",
roles:
[
{ role: "readWrite", db: "admin" },
]
})
该用户只能对该用户新增的集合和数据进行读写,对admin库的system.indexes、system.users、system.version 等集合不具有读写权限
添加edu库的可读写用户
repset:Primary> use edu
repset:Primary> db.createUser(
{
user: "edu",
pwd: "password",
roles:
[
{ role: "readWrite", db: "edu" },
]
}
)
注意,MongoDB的用户是基于数据库的,要使用该用户的权限时,必须先进去到该用户权限所属的数据库。比如,你要用edu这个用户去查询edu数据库的东西,必须要先登录到edu库
repset:Primary> use edu
repset:Primary> db.auth("username","password")
查看当前位于哪个库
repset:Primary> db
配置完之后,配置信息将同步到其他节点
- 认证
配置完用户验证之后,停掉副本集,先用openssl生成密码文件
openssl rand -base64 90 > /home/mongo/mongo1/bin/keyfile
然后将keyfile文件复制到 mongo2和mongo3的bin目录下
注意,需要给keyfile文件赋予600的权限,文件才能被调用
chmod 600 /home/mongo/mongo1/bin/keyfile
然后再MongoDB启动脚本startmongod.sh中加入 keyfile
./mongod --config mongod1.conf --keyFile keyfile
或者在mongod1.conf配置文件中加入keyfile
#数据库文件路径
dbpath=/home/mongo/mongo1/data/
#日志文件
logpath=/home/mongo/mongo1/log/mongod.log
#副本集配置变量
replSet=repset
#修改数据目录存储模式,每个数据库的文件存储在DBPATH指定目录的不同的文件夹中
directoryperdb=true
#定义端口
port=28017
#后台启动
fork=true
#以追加的模式写入日志
logappend=true
#认证文件
keyFile=/home/mongo/mongo1/bin/keyfile
mongo2、mongo3也需要做以上的操作,要保持一致
逐一启动副本,只有经过密码文件认证的节点才能加入;数据库操作也有密码认证,大大提高了安全性