MongoDB副本(一主一备+仲裁)环境部署记录

Posted 散尽浮华

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MongoDB副本(一主一备+仲裁)环境部署记录相关的知识,希望对你有一定的参考价值。

 

MongoDB复制集是一个带有故障转移的主从集群。是从现有的主从模式演变而来,增加了自动故障转移和节点成员自动恢复。MongoDB复制集模式中没有固定的主结点,在启动后,多个服务节点间将自动选举产生一个主结点。该主结点被称为primary,一个或多个从结点被称为secondaries。primary结点基本上就是master结点,不同之处在于primary结点在不同时间可能是不同的服务器。如果当前的主结点失效了,复制集中的其余结点将会试图选出一个新的主结点。

MongoDB复制集模式的好处:

  • 一切自动化。首先,复制集模式本身做了大量的管理工作,自动管理从节点,确保数据不会不一致。
  • 主节点挂掉后,会自动判断集群中的服务器并进行故障转移,推举新的主节点。
  • 一个复制集集群支持1-7台服务器,在一个复制集中各个服务器数据保持完全一致。

在一个MongoDB复制集集群中,各个服务器有以下几种状态:

  • Primary 主节点,一个复制集有且仅有一台服务器处于Primary状态,只有主节点才对外提供读写服务。如果主节点挂掉,复制集将投票选出一个备节点成为新的主节点。
  • Secondary 备用节点,复制集允许有多台Secondary,每个备用节点的数据与主节点的数据是完全同步的。Recovering 恢复中,当复制集中某台服务器挂掉或者掉线后数据无法同步,重新恢复服务后从其他成员复制数据,这时就处于恢复过程,数据同步后,该节点又回到备用状态。
  • Arbiter 仲裁节点,该类节点可以不用单独存在,如果配置为仲裁节点,就主要负责在复本集中监控其他节点状态,投票选出主节点。该节点将不会用于存放数据。如果没有仲裁节点,那么投票工作将由所有节点共同进行。
  • Down 无效节点,当服务器挂掉或掉线时就会处于该状态。复制集的从节点读请求,也是在各个Driver层设置slaveOk的值来实现的。

如上介绍所知,mongodb中的复制可以在多台服务器中同步数据。复制提供了冗余和增加了数据的高可用性,防止单个节点易丢失数据的可能性,也可以用来进行读写分离提高客户端操作性能。复制集中各节点的mongodb实例有相同的数据集副本。主节点可以接收客户端所有写操作记录到日志中,从库复制主库的操作日志记录应用到其数据库中。一个客户端只能有一个主节点,如果主节点不可用(10秒内无法连接),复制集中将选一个成员节点作为主节点。

MongoDB主备+仲裁的基本结构如下:

主节点(Primary)
在复制集中,主节点是唯一能够接收写请求的节点。MongoDB在主节点进行写操作,并将这些操作记录到主节点的oplog中。而从节点将会从oplog复制到其本机,并将这些操作应用到自己的数据集上。(复制集最多只能拥有一个主节点)
从节点(Secondaries)
从节点通过应用主节点传来的数据变动操作来保持其数据集与主节点一致。从节点也可以通过增加额外参数配置来对应特殊需求。例如,从节点可以是non-voting或是priority 0.
仲裁节点(ARBITER)
仲裁节点即投票节点,其本身并不包含数据集,且也无法晋升为主节点。但是,旦当前的主节点不可用时,投票节点就会参与到新的主节点选举的投票中。仲裁节点使用最小的资源并且不要求硬件设备。投票节点的存在使得复制集可以以偶数个节点存在,而无需为复制集再新增节点 不要将投票节点运行在复制集的主节点或从节点机器上。 投票节点与其他 复制集节点的交流仅有:选举过程中的投票,心跳检测和配置数据。这些交互都是不加密的。

心跳检测
复制集成员每两秒向复制集中其他成员进行心跳检测。如果某个节点在10秒内没有返回,那么它将被标记为不可用。

MongoDB副本集是有故障恢复功能的主从集群,由一个primary节点和一个或多个secondary节点组成:
节点同步过程: Primary节点写入数据,Secondary通过读取Primary的oplog得到复制信息,开始复制数据并且将复制信息写入到自己的oplog。如果某个操作失败,则备份节点停止从当前数据源复制数据。如果某个备份节点由于某些原因挂掉了,当重新启动后,就会自动从oplog的最后一个操作开始同步,同步完成后,将信息写入自己的oplog,由于复制操作是先复制数据,复制完成后再写入oplog,有可能相同的操作会同步两份,不过MongoDB在设计之初就考虑到这个问题,将oplog的同一个操作执行多次,与执行一次的效果是一样的。

通俗理解:当Primary节点完成数据操作后,Secondary会做出一系列的动作保证数据的同步:
- 检查自己local库的oplog.rs集合,找出最近的时间戳
- 检查Primary节点local库oplog.rs集合,找出大于此时间戳的记录。
- 将找到的记录插入到自己的oplog.rs集合中,并执行这些操作。

副本集的同步和主从同步一样,都是异步同步的过程,不同的是副本集有个自动故障转移的功能。其原理是:slave端从primary端获取日志,然后在自己身上完全顺序的执行日志所记录的各种操作(该日志是不记录查询操作的),这个日志就是local数据 库中的oplog.rs表,默认在64位机器上这个表是比较大的,占磁盘大小的5%,oplog.rs的大小可以在启动参数中设 定:–oplogSize 1000,单位是M。

注意:在副本集的环境中,要是所有的Secondary都宕机了,只剩下Primary。最后Primary会变成Secondary,不能提供服务。

下面简单介绍下MongoDB 副本集的部署过程:

1)服务器信息
sign-mongo01.wangshibo.cn  172.16.51.216   Primary
sign-mongo02.wangshibo.cn  172.16.51.217   Secondary
sign-mongo03.wangshibo.cn  172.16.51.218   Arbiter

三台服务器均设置好主机名,关闭iptables及selinux(略)

2)在3台服务器文件hosts文件中都添加以下3行:
[root@sign-mongo01 ~]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
172.16.51.216   sign-mongo01.wangshibo.cn
172.16.51.217   sign-mongo02.wangshibo.cn
172.16.51.218   sign-mongo03.wangshibo.cn 

3)安装部署mongodb(三台机器都安装)
下载地址:https://www.mongodb.org/dl/linux/x86_64-rhel62
[app@sign-mongo01 software]$ pwd
/data/software
[app@sign-mongo01 software]$ ls
mongodb-linux-x86_64-rhel62-v3.2-latest.tgz
[app@sign-mongo01 software]$ tar -zvxf mongodb-linux-x86_64-rhel62-v3.2-latest.tgz 
[app@sign-mongo01 software]$ mv mongodb-linux-x86_64-rhel62-3.2.17-34-g4c1bae566c /data/mongodb
[app@sign-mongo01 software]$ cd /data/mongodb/
[app@sign-mongo01 mongodb]$ mkdir data
[app@sign-mongo01 mongodb]$ mkdir log
[app@sign-mongo01 mongodb]$ vim mongodb.conf
pidfilepath=/data/mongodb/log/mongod.pid
logpath=/data/mongodb/log/mongod.log
dbpath=/data/mongodb
logappend=true
bind_ip=172.16.51.216
port=27017
fork=true
replSet=rs0

备节点的mongodb.conf配置文件分别为:
[app@sign-mongo02 mongodb]$ vim mongodb.conf
pidfilepath=/data/mongodb/log/mongod.pid
logpath=/data/mongodb/log/mongod.log
dbpath=/data/mongodb
logappend=true
bind_ip=172.16.51.217
port=27018
fork=true
replSet=rs0

其中:replSet=rs0  #表示复制集名称:rs0

启动主备服务器的mongodb
[app@sign-mongo01 ~]$ /data/mongodb/bin/mongod --config /data/mongodb/mongodb.conf
about to fork child process, waiting until server is ready for connections.
forked process: 7317
child process started successfully, parent exiting
[app@sign-mongo01 mongodb]$ ps -ef|grep mongodb
app       7317     1  0 21:38 ?        00:00:00 /data/mongodb/bin/mongod --config /data/mongodb/mongodb.conf
app       7342  7254  0 21:38 pts/1    00:00:00 grep mongodb
[app@sign-mongo01 mongodb]$ lsof -i:27017
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
mongod  7317  app    6u  IPv4  27011      0t0  TCP sign-mongo01.wangshibo.cn:27017 (LISTEN)

[app@sign-mongo02 mongodb]$ /data/mongodb/bin/mongod --config /data/mongodb/mongodb.conf
about to fork child process, waiting until server is ready for connections.
forked process: 9725
child process started successfully, parent exiting
[app@sign-mongo02 mongodb]$ lsof -i:27018
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
mongod  9725  app    6u  IPv4  27191      0t0  TCP sign-mongo02.wangshibo.cn:27018 (LISTEN)

设置mongodb的环变量
[root@sign-mongo01 src]# vim /etc/profile
......
export PATH=$PATH:/data/mongodb/bin
[root@sign-mongo01 src]# source /etc/profile

登录到mongodb中:
[app@sign-mongo01 ~]$ mongo 172.16.51.216:27017
MongoDB shell version: 3.2.17-34-g4c1bae566c
connecting to: 172.16.51.216:27017/test
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: 
2017-11-22T21:38:18.063+0800 I CONTROL  [initandlisten] 
2017-11-22T21:38:18.063+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is \'always\'.
2017-11-22T21:38:18.063+0800 I CONTROL  [initandlisten] **        We suggest setting it to \'never\'
2017-11-22T21:38:18.063+0800 I CONTROL  [initandlisten] 
2017-11-22T21:38:18.063+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is \'always\'.
2017-11-22T21:38:18.063+0800 I CONTROL  [initandlisten] **        We suggest setting it to \'never\'
2017-11-22T21:38:18.063+0800 I CONTROL  [initandlisten] 
> 

初始化复制集:(集合为:"rs0" ;第一个成员为:"sign-mongo01.wangshibo.cn:27017" 
> rs.initiate({_id: "rs0",members: [{ _id: 0 , host: "sign-mongo01.wangshibo.cn:27017" }]})
{ "ok" : 1 }
rs0:OTHER>                    //接着回车,显示这个节点为Primary主节点
rs0:PRIMARY>

接着添加另1个成员:
rs0:PRIMARY> rs.add("sign-mongo02.wangshibo.cn:27018")
{ "ok" : 1 }

查看成员信息 (或者使用 db.isMaster() )
rs0:PRIMARY> rs.status()
{
    "set" : "rs0",
    "date" : ISODate("2017-11-22T13:55:28.446Z"),
    "myState" : 1,
    "term" : NumberLong(1),
    "heartbeatIntervalMillis" : NumberLong(2000),
    "members" : [
        {
            "_id" : 0,
            "name" : "sign-mongo01.wangshibo.cn:27017",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 1031,
            "optime" : {
                "ts" : Timestamp(1511358895, 1),
                "t" : NumberLong(1)
            },
            "optimeDate" : ISODate("2017-11-22T13:54:55Z"),
            "infoMessage" : "could not find member to sync from",
            "electionTime" : Timestamp(1511358843, 2),
            "electionDate" : ISODate("2017-11-22T13:54:03Z"),
            "configVersion" : 2,
            "self" : true
        },
        {
            "_id" : 1,
            "name" : "sign-mongo02.wangshibo.cn:27018",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 32,
            "optime" : {
                "ts" : Timestamp(1511358895, 1),
                "t" : NumberLong(1)
            },
            "optimeDate" : ISODate("2017-11-22T13:54:55Z"),
            "lastHeartbeat" : ISODate("2017-11-22T13:55:27.684Z"),
            "lastHeartbeatRecv" : ISODate("2017-11-22T13:55:27.827Z"),
            "pingMs" : NumberLong(0),
            "configVersion" : 2
        }
    ],
    "ok" : 1
}


或者使用该方法查看,结果也是一样:
rs0:PRIMARY> use admin
switched to db admin
rs0:PRIMARY> db.runCommand( { replSetGetStatus : 1 } )  
{
    "set" : "rs0",
    "date" : ISODate("2017-11-23T01:12:20.701Z"),
    "myState" : 1,
    "term" : NumberLong(1),
    "heartbeatIntervalMillis" : NumberLong(2000),
    "members" : [
        {
            "_id" : 0,
            "name" : "sign-mongo01.wangshibo.cn:27017",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 41643,
            "optime" : {
                "ts" : Timestamp(1511358895, 1),
                "t" : NumberLong(1)
            },
            "optimeDate" : ISODate("2017-11-22T13:54:55Z"),
            "electionTime" : Timestamp(1511358843, 2),
            "electionDate" : ISODate("2017-11-22T13:54:03Z"),
            "configVersion" : 2,
            "self" : true
        },
        {
            "_id" : 1,
            "name" : "sign-mongo02.wangshibo.cn:27018",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 40645,
            "optime" : {
                "ts" : Timestamp(1511358895, 1),
                "t" : NumberLong(1)
            },
            "optimeDate" : ISODate("2017-11-22T13:54:55Z"),
            "lastHeartbeat" : ISODate("2017-11-23T01:12:19.418Z"),
            "lastHeartbeatRecv" : ISODate("2017-11-23T01:12:16.225Z"),
            "pingMs" : NumberLong(0),
            "configVersion" : 2
        }
    ],
    "ok" : 1
}
rs0:PRIMARY> 

详细说明如下:
"_id" :  #集群中节点编号  
"name" :  #成员服务器名称及端口  
"health" :  #表示成员中的健康状态(0:down;1:up)  
"state" :  #为0~10,表示成员的当前状态  
"stateStr" :  #描述该成员是主库(PRIMARY)还是备库(SECONDARY)  
"uptime" :  #该成员在线时间(秒)  
"optime" :  #成员最后一次应用日志(oplog)的信息  
"optimeDate" :  #成员最后一次应用日志(oplog)的时间  
"electionTime" :  #当前primary从操作日志中选举信息  
"electionDate" :  #当前primary被选定为primary的日期  
"configVersion" :  #mongodb版本  
"self" :  #为true 表示当前节点  

4)测试操作。在主库中,可以任意操作:
rs0:PRIMARY> show dbs
local  0.000GB
rs0:PRIMARY> use mydb      //切换到要创建的数据库
switched to db mydb
rs0:PRIMARY> show dbs      //use只是转到相关数据库,此时并没有做任何操作,所以并不会创建相应的数据库,只有当真正的操作了一次数据库就会自动创建。
local  0.000GB
rs0:PRIMARY> db.stats();
{
    "db" : "mydb",
    "collections" : 0,
    "objects" : 0,
    "avgObjSize" : 0,
    "dataSize" : 0,
    "storageSize" : 0,
    "numExtents" : 0,
    "indexes" : 0,
    "indexSize" : 0,
    "fileSize" : 0,
    "ok" : 1
}
rs0:PRIMARY> db.coll.insert({"id":1}) 
WriteResult({ "nInserted" : 1 })
rs0:PRIMARY> db.coll.find() 
{ "_id" : ObjectId("5a162222991b83743942d169"), "id" : 1 }
rs0:PRIMARY> db.coll.remove({"id":1}) 
WriteResult({ "nRemoved" : 1 })
rs0:PRIMARY> show dbs
local  0.000GB
mydb   0.000GB


现在到备库中
172.16.51.217 (sign-mongo02.wangshibo.cn )
查看分库数据库目录,发现多了数据库,数据库与主库(172.16.51.216)一致!是主库同步过来的。
[app@sign-mongo02 ~]$ mongo 172.16.51.217:27018
MongoDB shell version: 3.2.17-34-g4c1bae566c
connecting to: 172.16.51.217:27018/test
Server has startup warnings: 
2017-11-22T21:46:38.417+0800 I CONTROL  [initandlisten] 
2017-11-22T21:46:38.418+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is \'always\'.
2017-11-22T21:46:38.418+0800 I CONTROL  [initandlisten] **        We suggest setting it to \'never\'
2017-11-22T21:46:38.418+0800 I CONTROL  [initandlisten] 
2017-11-22T21:46:38.418+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is \'always\'.
2017-11-22T21:46:38.418+0800 I CONTROL  [initandlisten] **        We suggest setting it to \'never\'
2017-11-22T21:46:38.418+0800 I CONTROL  [initandlisten] 

在副本服务器中登录其本地数据库,发现可以连接,但是无法读写操作:
rs0:SECONDARY> show dbs
2017-11-23T09:25:35.961+0800 E QUERY    [thread1] Error: listDatabases failed:{ "ok" : 0, "errmsg" : "not master and slaveOk=false", "code" : 13435 } :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
Mongo.prototype.getDBs@src/mongo/shell/mongo.js:62:1
shellHelper.show@src/mongo/shell/utils.js:781:19
shellHelper@src/mongo/shell/utils.js:671:15
@(shellhelp2):1:1
rs0:SECONDARY>

从库开启读操作(此时可以测试主库插入,从库查看,同步正常):
rs0:SECONDARY> rs.slaveOk();  
rs0:SECONDARY> show dbs
local  0.000GB
mydb   0.000GB


5)现在模拟主库不可用,将主节点服务停止:
[app@sign-mongo01 mongodb]$ pkill -9 mongod
[app@sign-mongo01 mongodb]$ ps -ef|grep mongodb
app       9524  9398  0 09:32 pts/0    00:00:00 grep mongodb

到备节点172.16.51.217 中登录mongodb,查看复制集状态:
[app@sign-mongo02 ~]$ mongo 172.16.51.217:27018
MongoDB shell version: 3.2.17-34-g4c1bae566c
connecting to: 172.16.51.217:27018/test
Server has startup warnings: 
2017-11-22T21:46:38.417+0800 I CONTROL  [initandlisten] 
2017-11-22T21:46:38.418+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is \'always\'.
2017-11-22T21:46:38.418+0800 I CONTROL  [initandlisten] **        We suggest setting it to \'never\'
2017-11-22T21:46:38.418+0800 I CONTROL  [initandlisten] 
2017-11-22T21:46:38.418+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is \'always\'.
2017-11-22T21:46:38.418+0800 I CONTROL  [initandlisten] **        We suggest setting it to \'never\'
2017-11-22T21:46:38.418+0800 I CONTROL  [initandlisten] 
rs0:SECONDARY> rs.status() 
{
    "set" : "rs0",
    "date" : ISODate("2017-11-23T01:33:29.688Z"),
    "myState" : 2,
    "term" : NumberLong(1),
    "heartbeatIntervalMillis" : NumberLong(2000),
    "members" : [
        {
            "_id" : 0,
            "name" : "sign-mongo01.wangshibo.cn:27017",
            "health" : 0,
            "state" : 8,
            "stateStr" : "(not reachable/healthy)",
            "uptime" : 0,
            "optime" : {
                "ts" : Timestamp(0, 0),
                "t" : NumberLong(-1)
            },
            "optimeDate" : ISODate("1970-01-01T00:00:00Z"),
            "lastHeartbeat" : ISODate("2017-11-23T01:33:28.732Z"),
            "lastHeartbeatRecv" : ISODate("2017-11-23T01:32:28.099Z"),
            "pingMs" : NumberLong(0),
            "lastHeartbeatMessage" : "Connection refused",
            "configVersion" : -1
        },
        {
            "_id" : 1,
            "name" : "sign-mongo02.wangshibo.cn:27018",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 42412,
            "optime" : {
                "ts" : Timestamp(1511399985, 1),
                "t" : NumberLong(1)
            },
            "optimeDate" : ISODate("2017-11-23T01:19:45Z"),
            "infoMessage" : "could not find member to sync from",
            "configVersion" : 2,
            "self" : true
        }
    ],
    "ok" : 1
}

有上面可看出,主节点删除服务进程,primary并没有切换到备节点上:
再次启动主节点的mongodb服务,发现primary才自动切换回到主节点:
[app@sign-mongo01 mongodb]$ nohup /data/mongodb/bin/mongod --config /data/mongodb/mongodb.conf
nohup: ignoring input and appending output to `nohup.out\'
[app@sign-mongo01 mongodb]$ ps -ef|grep mongodb
app       9538     1  8 09:37 ?        00:00:00 /data/mongodb/bin/mongod --config /data/mongodb/mongodb.conf
app       9610  9398  0 09:37 pts/0    00:00:00 grep mongodb
[app@sign-mongo01 mongodb]$ mongo 172.16.51.216:27017
MongoDB shell version: 3.2.17-34-g4c1bae566c
connecting to: 172.16.51.216:27017/test
Server has startup warnings: 
2017-11-23T09:37:26.467+0800 I CONTROL  [initandlisten] 
2017-11-23T09:37:26.467+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is \'always\'.
2017-11-23T09:37:26.467+0800 I CONTROL  [initandlisten] **        We suggest setting it to \'never\'
2017-11-23T09:37:26.467+0800 I CONTROL  [initandlisten] 
2017-11-23T09:37:26.467+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is \'always\'.
2017-11-23T09:37:26.467+0800 I CONTROL  [initandlisten] **        We suggest setting it to \'never\'
2017-11-23T09:37:26.467+0800 I CONTROL  [initandlisten] 
rs0:PRIMARY> rs.status()
{
    "set" : "rs0",
    "date" : ISODate("2017-11-23T01:38:19.631Z"),
    "myState" : 1,
    "term" : NumberLong(2),
    "heartbeatIntervalMillis" : NumberLong(2000),
    "members" : [
        {
            "_id" : 0,
            "name" : "sign-mongo01.wangshibo.cn:27017",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 54,
            "optime" : {
                "ts" : Timestamp(1511401058, 2),
                "t" : NumberLong(2)
            },
            "optimeDate" : ISODate("2017-11-23T01:37:38Z"),
            "infoMessage" : "could not find member to sync from",
            "electionTime" : Timestamp(1511401058, 1),
            "electionDate" : ISODate("2017-11-23T01:37:38Z"),
            "configVersion" : 2,
            "self" : true
        },
        {
            "_id" : 1,
            "name" : "sign-mongo02.wangshibo.cn:27018",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 53,
            "optime" : {
                "ts" : Timestamp(1511401058, 2),
                "t" : NumberLong(2)
            },
            "optimeDate" : ISODate("2017-11-23T01:37:38Z"),
            "lastHeartbeat" : ISODate("2017-11-23T01:38:18.072Z"),
            "lastHeartbeatRecv" : ISODate("2017-11-23T01:38:18.596Z"),
            "pingMs" : NumberLong(0),
            "syncingTo" : "sign-mongo01.wangshibo.cn:27017",
            "configVersion" : 2
        }
    ],
    "ok" : 1
}

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
现在开始部署仲裁节点sign-mongo03.wangshibo.cn
[app@sign-mongo03 ~]$ mkdir /data/mongodb/arbiter
[app@sign-mongo03 ~]$ ll -d /data/mongodb/arbiter
drwxrwxr-x 2 app app 4096 Nov 23 09:39 /data/mongodb/arbiter

本案例是在普通用户app账号下部署的,权限都是app.app。
如果是在root账号下部署,那么需要将mongodb数据目录下的文件全部设置mongodb.mongodb权限

mongodb.conf配置:
[app@sign-mongo03 ~]$ vim /data/mongodb/mongodb.conf
pidfilepath=/data/mongodb/log/mongod.pid
logpath=/data/mongodb/log/mongod.log
dbpath=/data/mongodb/arbiter
logappend=false
bind_ip=172.16.51.218
port=27019
fork=true
replSet=rs0

接着重启服务:
[app@sign-mongo03 ~]$ /data/mongodb/bin/mongod --config /data/mongodb/mongodb.conf
about to fork child process, waiting until server is ready for connections.
forked process: 9217
child process started successfully, parent exiting
[app@sign-mongo03 ~]$ ps -ef|grep mongo
app       9217     1  1 09:46 ?        00:00:00 /data/mongodb/bin/mongod --config /data/mongodb/mongodb.conf
app       9242  9158  0 09:46 pts/0    00:00:00 grep mongo
[app@sign-mongo03 ~]$ lsof -i:27019
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
mongod  9217  app    6u  IPv4  37321      0t0  TCP sign-mongo03.wangshibo.cn:27019 (LISTEN)

然后在Paimary主节点sign-mongo01.wangshibo.cn的mongodb中添加仲裁节点并查看结果
[app@sign-mongo01 mongodb]$ mongo 172.16.51.216:27017
MongoDB shell version: 3.2.17-34-g4c1bae566c
connecting to: 172.16.51.216:27017/test
Server has startup warnings: 
2017-11-23T09:37:26.467+0800 I CONTROL  [initandlisten] 
2017-11-23T09:37:26.467+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is \'always\'.
2017-11-23T09:37:26.467+0800 I CONTROL  [initandlisten] **        We suggest setting it to \'never\'
2017-11-23T09:37:26.467+0800 I CONTROL  [initandlisten] 
2017-11-23T09:37:26.467+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is \'always\'.
2017-11-23T09:37:26.467+0800 I CONTROL  [initandlisten] **        We suggest setting it to \'never\'
2017-11-23T09:37:26.467+0800 I CONTROL  [initandlisten] 
rs0:PRIMARY> rs.addArb("sign-mongo03.wangshibo.cn:27019")
{ "ok" : 1 }
rs0:PRIMARY> db.isMaster() 
{
    "hosts" : [
        "sign-mongo01.wangshibo.cn:27017",
        "sign-mongo02.wangshibo.cn:27018"
    ],
    "arbiters" : [
        "sign-mongo03.wangshibo.cn:27019"
    ],
    "setName" : "rs0",
    "setVersion" : 4,
    "ismaster" : true,
    "secondary" : false,
    "primary" : "sign-mongo01.wangshibo.cn:27017",
    "me" : "sign-mongo01.wangshibo.cn:27017",
    "electionId" : ObjectId("7fffffff0000000000000003"),
    "maxBsonObjectSize" : 16777216,
    "maxMessageSizeBytes" : 48000000,
    "maxWriteBatchSize" : 1000,
    "localTime" : ISODate("2017-11-23T01:59:03.554Z"),
    "maxWireVersion" : 4,
    "minWireVersion" : 0,
    "ok" : 1
}
rs0:PRIMARY> rs.status()
{
    "set" : "rs0",
    "date" : ISODate("2017-11-23T02:00:26.312Z"),
    "myState" : 1,
    "term" : NumberLong(3),
    "heartbeatIntervalMillis" : NumberLong(2000),
    "members" : [
        {
            "_id" : 0,
            "name" : "sign-mongo01.wangshibo.cn:27017",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 368,
            "optime" : {
                "ts" : Timestamp(1511402420, 1),
                "t" : NumberLong(3)
            },
            "optimeDate" : ISODate("2017-11-23T02:00:20Z"),
            "electionTime" : Timestamp(1511402069, 1),
            "electionDate" : ISODate("2017-11-23T01:54:29Z"),
            "configVersion" : 5,
            "self" : true
        },
        {
            "_id" : 1,
            "name" : "sign-mongo02.wangshibo.cn:27018",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 367,
            "optime" : {
                "ts" : Timestamp(1511402420, 1),
                "t" : NumberLong(3)
            },
            "optimeDate" : ISODate("2017-11-23T02:00:20Z"),
            "lastHeartbeat" : ISODate("2017-11-23T02:00:26.306Z"),
            "lastHeartbeatRecv" : ISODate("2017-11-23T02:00:26.309Z"),
            "pingMs" : NumberLong(0),
            "syncingTo" : "sign-mongo01.wangshibo.cn:27017",
            "configVersion" : 5
        },
        {
            "_id" : 3,
            "name" : "sign-mongo03.wangshibo.cn:27019",
            "health" : 1,
            "state" : 7,
            "stateStr" : "ARBITER",
            "uptime" : 93,
            "lastHeartbeat" : ISODate("2017-11-23T02:00:26.306Z"),
            "lastHeartbeatRecv" : ISODate("2017-11-23T02:00:25.306Z"),
            "pingMs" : NumberLong(0),
            "configVersion" : 5
        }
    ],
    "ok" : 1
}


至此,添加完成!!
再次测试主备切换。关闭172.16.51.216(primary)中删除服务进程:
[app@sign-mongo01 mongodb]$ pkill -9 mongod
[app@sign-mongo01 mongodb]$ ps -ef|grep mongod
app       9664  9398  0 09:50 pts/0    00:00:00 grep mongod
[app@sign-mongo01 mongodb]$ lsof -i:27017
[app@sign-mongo01 mongodb]$ 

然后到172.16.51.217:27018(secondary)查看,发现primary已经切换为172.16.51.217
[app@sign-mongo02 ~]$ mongo 172.16.51.217:27018
MongoDB shell version: 3.2.17-34-g4c1bae566c
connecting to: 172.16.51.217:27018/test
Server has startup warnings: 
2017-11-22T21:46:38.417+0800 I CONTROL  [initandlisten] 
2017-11-22T21:46:38.418+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is \'always\'.
2017-11-22T21:46:38.418+0800 I CONTROL  [initandlisten] **        We suggest setting it to \'never\'
2017-11-22T21:46:38.418+0800 I CONTROL  [initandlisten] 
2017-11-22T21:46:38.418+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is \'always\'.
2017-11-22T21:46:38.418+0800 I CONTROL  [initandlisten] **        We suggest setting it to \'never\'
2017-11-22T21:46:38.418+0800 I CONTROL  [initandlisten] 
rs0:PRIMARY> rs.status()
{
    "set" : "rs0",
    "date" : ISODate("2017-11-23T02:01:17.762Z"),
    "myState" : 1,
    "term" : NumberLong(4),
    "heartbeatIntervalMillis" : NumberLong(2000),
    "members" : [
        {
            "_id" : 0,
            "name" : "sign-mongo01.wangshibo.cn:27017",
            "health" : 0,
            "state" : 8,
            "stateStr" : "(not reachable/healthy)",
            "uptime" : 0,
            "optime" : {
                "ts" : Timestamp(0, 0),
                "t" : NumberLong(-1)
            },
            "optimeDate" : ISODate("1970-01-01T00:00:00Z"),
            "lastHeartbeat" : ISODate("2017-11-23T02:01:17.316Z"),
            "lastHeartbeatRecv" : ISODate("2017-11-23T02:01:04.327Z"),
            "pingMs" : NumberLong(0),
            "lastHeartbeatMessage" : "Connection refused",
            "configVersion" : -1
        },
        {
            "_id" : 1,
            "name" : "sign-mongo02.wangshibo.cn:27018",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 44080,
            "optime" : {
                "ts" : Timestamp(1511402475, 2),
                "t" : NumberLong(4)
            },
            "optimeDate" : ISODate("2017-11-23T02:01:15Z"),
            "infoMessage" : "could not find member to sync from",
            "electionTime" : Timestamp(1511402475, 1),
            "electionDate" : ISODate("2017-11-23T02:01:15Z"),
            "configVersion" : 5,
            "self" : true
        },
        {
            "_id" : 3,
            "name" : "sign-mongo03.wangshibo.cn:27019",
            "health" : 1,
            "state" : 7,
            "stateStr" : "ARBITER",
            "uptime" : 145,
            "lastHeartbeat" : ISODate("2017-11-23T02:01:17.315Z"),
            "lastHeartbeatRecv" : ISODate("2017-11-23T02:01:15.316Z"),
            "pingMs" : NumberLong(0),
            "configVersion" : 5
        }
    ],
    "ok" : 1
}
rs0:PRIMARY> show dbs
local  0.000GB
mydb   0.000GB

有上面信息可知,添加仲裁节点后,primary能正常启动切换了!~

现在看看arbiter,连接到172.16.51.218:27019
[app@sign-mongo03 ~]$ mongo 172.16.51.218:27019
MongoDB shell version: 3.2.17-34-g4c1bae566c
connecting to: 172.16.51.218:27019/test
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: 
2017-11-23T09:58:34.978+0800 I CONTROL  [initandlisten] 
2017-11-23T09:58:34.978+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is \'always\'.
2017-11-23T09:58:34.978+0800 I CONTROL  [initandlisten] **        We suggest setting it to \'never\'
2017-11-23T09:58:34.978+0800 I CONTROL  [initandlisten] 
2017-11-23T09:58:34.978+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is \'always\'.
2017-11-23T09:58:34.978+0800 I CONTROL  [initandlisten] **        We suggest setting it to \'never\'
2017-11-23T09:58:34.978+0800 I CONTROL  [initandlisten] 
rs0:ARBITER> rs.slaveOk(); 
rs0:ARBITER> db.isMaster()
{
    "hosts" : [
        "sign-mongo01.wangshibo.cn:27017",
        "sign-mongo02.wangshibo.cn:27018"
    ],
    "arbiters" : [
        "sign-mongo03.wangshibo.cn:27019"
    ],
    "setName" : "rs0",
    "setVersion" : 5,
    "ismaster" : false,
    "secondary" : false,
    "primary" : "sign-mongo02.wangshibo.cn:27018",
    "arbiterOnly" : true,
    "me" : "sign-mongo03.wangshibo.cn:27019",
    "maxBsonObjectSize" : 16777216,
    "maxMessageSizeBytes" : 48000000,
    "maxWriteBatchSize" : 1000,
    "localTime" : ISODate("2017-11-23T02:03:33.874Z"),
    "maxWireVersion" : 4,
    "minWireVersion" : 0,
    "ok" : 1
}
rs0:ARBITER> show dbs
local  0.000GB

arbiter 最为仲裁者,没有数据副本存储在本地,能读取复制集的信息。
-------------------------------------------------------------------------------------------------
在primary主库上,创建locs数据库,该库的用户名为locsopr,密码为locsopr@123
[app@sign-mongo02 ~]$ mongo 172.16.51.217:27018
......
rs0:PRIMARY> use locs
switched to db locs
rs0:PRIMARY> db.createUser({user:"locsopr",pwd :"locsopr@123",roles:["readWrite"]})
Successfully added user: { "user" : "locsopr", "roles" : [ "readWrite" ] }
rs0:PRIMARY> show users;          //查看当前库下的用户名
{
    "_id" : "locs.locsopr",
    "user" : "locsopr",
    "db" : "locs",
    "roles" : [
        {
            "role" : "readWrite",
            "db" : "locs"
        }
    ]
}


使用上面的用户登录primary主库
[app@sign-mongo02 ~]$ mongo 172.16.51.217:27018/locs -ulocsopr -plocsopr@123
.........
rs0:PRIMARY> show users
{
    "_id" : "locs.locsopr",
    "user" : "locsopr",
    "db" : "locs",
    "roles" : [
        {
            "role" : "readWrite",
            "db" : "locs"
        }
    ]
}
rs0:PRIMARY> show dbs
admin  0.000GB
local  0.000GB
rs0:PRIMARY> 

再登录从库看下:
[app@sign-mongo01 ~]$ mongo 172.16.51.216:27017/locs -ulocsopr -plocsopr@123
.......
rs0:SECONDARY> rs.slaveOk();
rs0:SECONDARY> show users
{
    "_id" : "locs.locsopr",
    "user" : "locsopr",
    "db" : "locs",
    "roles" : [
        {
            "role" : "readWrite",
            "db" : "locs"
        }
    ]
}
rs0:SECONDARY> show dbs
admin  0.000GB
local  0.000GB

-------------------------------------------------------------------------------------------------------------------------------------------------------
上面介绍的是三台mongodb节点:一主一备一仲裁,这样,主节点挂了后,通过仲裁机制将primary自动切换到备机上!
如果上面的三台mongodb节点:一主两备,没有仲裁节点,那么主节点挂了后,primary会自动切换到其余两台备节点中的一台上!

一主两从(端口分别为27017、27018、27019)的mongodb配置和上面一主一从(端口分别为27017、27018)的配置一样。
多加的一个从节点,在主节点登陆mongodb,使用rs.add命令将这个成员加到集群中即可!

一主两从的模式,比如:
主节点sign-mongo01.wangshibo.cn的mongodb服务程序挂了后,另外两个从节点中的一个(比如sign-mongo02.wangshibo.cn)会自动变成primary主节点,
另一个节点sign-mongo03.wangshibo.cn则是新的主节点(sign-mongo02.wangshibo.cn)的从节点。

++++++++++++++++++++++++++++++如果想让切换回原来的主节点,做法如下+++++++++++++++++++++++++++++++
1)恢复原来的主节点sign-mongo01.wangshibo.cn的mongodb服务,使用命令rs.status() 确认数据集成员运行正常。
2)到次节点sign-mongo03.wangshibo.cn中登录mongodb,运行freeze使其120内不会变为主节点。  
> rs.freeze(120)  
3) 到新的主节点sign-mongo02.wangshibo.cn中强制切换主节点,stepDown将阻止长事务和写入操作  
> rs.stepDown(120) 
4)此时sign-mongo01.wangshibo.cn节点变成primary主节点。使用rs.status()命令可以查看到集群状态。

+++++++++++++++++++++++若要使某个节点永远不会变为主节点,设置优先级为0即可+++++++++++++++++++++++
登陆当前主节点的mongodb,执行下面操作:
rs0:PRIMARY> cfg = rs.conf()  
rs0:PRIMARY> cfg.members[0].priority = 0.5  
rs0:PRIMARY> cfg.members[1].priority = 0.5  
rs0:PRIMARY> cfg.members[2].priority = 0  
rs0:PRIMARY> rs.reconfig(cfg)  

说明:
其中成员编号0/1/2为 rs.status()中的 "_id"值  
members[2]表示sign-mongo03.wangshibo.cn,则它将永远不会变成主节点!因为优先级设置为0了!

++++++++++++++++++++++++++++++++移除一个复制成员(两种方法)++++++++++++++++++++++++++++++++++++
登陆当前主节点的mongodb,执行下面操作:
rs0:PRIMARY> rs.remove("sign-mongo03.wangshibo.cn:27019")  
rs0:PRIMARY> rs.conf()  

或者:  
rs0:PRIMARY> cfg = rs.conf()  
rs0:PRIMARY> cfg.members.splice(2,1)  
rs0:PRIMARY> rs.reconfig(cfg)  

移除后到移除的节点服务器(即sign-mongo03.wangshibo.cn),更改配置文件mongod.conf
#replSet=rs0       //将这一行注释
然后再重启mongodb服务,这就完成了移除(数据库文件仍保留在当前服务器)。

以上是关于MongoDB副本(一主一备+仲裁)环境部署记录的主要内容,如果未能解决你的问题,请参考以下文章

Linux 部署MySQL 一主一从一备

Mongodb 4.2版本副本集配置

mongodb 使用密钥文件身份验证部署副本集

MongoDB副本集(一主两从)读写分离故障转移功能环境部署记录

centos7 Mysql5.7.15版本 一主一备架构(生产环境带数据,不锁库)

Linux 搭建MongoDB复制集群