MongoDB Replica Sets的配置

MongoDB是一种很不错的NoSQL数据库,数据库存储基于BSON,一种JSON的变种。在使用的时候，有时候我们dump某个单个的collection/db作为备份的时候，往往会连累到整库都访问困难。集群有时候果断是个不错的选择。

MongoDB的存储是Replica,有三种角色如下:

一个cluster中只可以有一个primary,可以有多个secondary,当只有一个primary和一个secondary的时候，需要一个arbiter来帮忙投票哪个才是primary.

目前我们在使用的是tokumx,对外的访问和mongodb是一样的，但内部的配置略有区别。所以若各位使用的是官方mongodb,可能会发现略有不同。

当前，我们已经有一个单个的mongodb node,里面存了一堆数据,我的目标是将此单个node扩展为一个cluster.

首先，我们需要准备一个keyfile.生成方法可以参考这里. 简单说就是两行命令如下:

openssl rand -base64 741 > mongodb-keyfile
chmod 600 mongodb-keyfile

然后就得到了一个mongodb-keyfile. 启动的时候加上keyfile参数如下

/path/to/tokumx/bin/mongod -f xxx.cnf --keyFile /path/to/key/mongodb-keyfile

一般配置文件我都是写在cnf中的，唯独这个keyFile貌似很难找到，所以我直接写启动脚本里面了。

对于单个文件我们并不需要这个keyfile,但若要做cluster,这个似乎是最简单的。因为两个node之间的通讯和验证全靠这个了。

在cnf中记得加上

replSet = your_rs_name

本cluster中所有的replSet都要是这个名字,若对不上也是认证不了的。

然后简单配置下其他环境,primary node就可以启动了。无意中找到一个不错的sample(链接)，各位若使用tokumx可以考虑参考下.

启动的脚本和配置文件复制两份,路径稍稍修改下,改个端口启动.这样三个node就启动了.此时三者用的keyfile都是同一个文件。

mongodb Replica 的配置中,默认对机器的识别是hostname:port这么玩的.hostname的查看也很容易

uname -n

一般是写在/etc/hostname中的.内网环境下，你还可以自己写/etc/hosts,强行将名字解析到指定IP中.这样做的好处是之后都可以用一些特定的字符串指代某个机器。当然，非要用ip:port也是可以的。

然后我们就可以进入primary node中开始配置节点了

mongo 127.0.0.1:27017/admin -u your_username -p your_password

进入后，就可以看到提示自己已经在primary界面了。若不是,请到此处复习怎样创建user.顺便一提,tokumx1.5貌似是db.addUser()而不是createUser().

your_rs_name:PRIMARY> rs.initiate()

mongo3.0好像又有点区别, 应该先initiate,然后create user.

> rs.initiate()
xx:PRIMARY> db.createUser({"user":"your_username","pwd":"your_password","roles":["root","dbAdmin","dbOwner"]})

或者 toku 的是:

> rs.initiate()
xx:PRIMARY> db.addUser("your_username","your_password")

初始化结束后,可以查看config

your_rs_name:PRIMARY> rs.conf()
{
"_id" : "your_rs_name",
....
"members" : [
{
"_id":0
"host" : "your_hostname:your_first_port",
}
]
}

初始状态大致如上. 然后添加arbiter和secondary.

your_rs_name:PRIMARY> rs.addArb("your_arbiter_hostname:arbiter_port")
{ "ok" : 1 }
your_rs_name:PRIMARY> rs.add("your_secondary_hostname:secondary_port")
{ "ok" : 1 }

arbiter节点中并不存储数据，只有一个投票帮助决定primary,而secondary中有完整的备份。若前面验证都通过了的话，基本应该就加成功了。看log应该可以看到your_hostname:your_first_port正在向your_secondary_hostname:secondary_port同步数据的信息。

这个时候,你的your_hostname:your_first_port这个node和your_secondary_hostname:secondary_port这个node两者的priority是一样的，任何一个都可能是primary node，这可能并不是我们希望的。我们可能希望让某个node为primary node,另一个在第一个node挂了后担当起primary node的作用

your_rs_name:PRIMARY> cfg = rs.config()
{
"_id" : "your_rs_name",
"version" : 326971,
"protocolVersion" : 65,
"members" : [
{
"_id" : 0,
"host" : "your_hostname:your_first_port"
},
{
"_id" : 1,
"host" : "your_arbiter_hostname:arbiter_port",
"arbiterOnly" : true
},
{
"_id" : 2,
"host" : "your_secondary_hostname:secondary_port"
}
]
}

我们将当前的config赋值给cfg,然后在cfg上修改一下。

your_rs_name:PRIMARY> cfg.members[0].priority = 1
your_rs_name:PRIMARY> cfg.members[2].priority = 0.5
your_rs_name:PRIMARY> rs.reconfig(cfg, {force: true})

再看config

your_rs_name:PRIMARY> cfg = rs.config()
{
"_id" : "your_rs_name",
"version" : 326971,
"protocolVersion" : 65,
"members" : [
{
"_id" : 0,
"host" : "your_hostname:your_first_port",
"priority" : 1
},
{
"_id" : 1,
"host" : "your_arbiter_hostname:arbiter_port",
"arbiterOnly" : true
},
{
"_id" : 2,
"host" : "your_secondary_hostname:secondary_port",
"priority" : 0.5
}
]
}

当然，你还可以有一些更多的操作.比如添加hide=true可以让外部无法访问,priority=0的时候，这个node永远不可能会是primary.

若某个节点不要了,你可以用remove将其删去

your_rs_name:PRIMARY> rs.remove("your_secondary_hostname:secondary_port")

也可以直接修改config后 force reconfig.

最后，查看当前状态可以使用rs.isMaster()

References

Replication Introduction

Deploy a Replica Set for Testing and Development

Generate a Key File

Add an Arbiter to Replica Set

Reconfigure a Replica Set with Unavailable Members

Configure Replica Set Tag Sets