mongodb的多服务器配置,master-slave模式,不能自动实现故障转移和恢复。为了实现多服务器的高可用,我们可以使用mongodb的replica set。replica set类似于heartbeat的功能,实现高可用。

复制集是一个带有故障转移的主从集群。是从现有的主从模式演变而来,增加了自动故障转移和节点成员自动恢复。复制集模式中没有固定的主结点,在启动后,多个服务节点间将自动选举产生一个主结点。该主结点被称为primary,一个或多个从结点被称为secondaries。primary结点基本上就是master结点,不同之处在于primary结点在不同时间可能是不同的服务器。如果当前的主结点失效了,复制集中的其余结点将会试图选出一个新的主结点。复制集模式的好处是,一切自动化。首先,复制集模式本身做了大量的管理工作,自动管理从节点,确保数据不会不一致。其次,主节点挂掉后,会自动判断集群中的服务器并进行故障转移,推举新的主节点。 一个复制集集群支持1-7台服务器,在一个复制集中各个服务器数据保持完全一致。

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

Primary 主节点:一个复制集有且仅有一台服务器处于Primary状态,只有主节点才对外提供读写服务。如果主节点挂掉,复制集将会投票选出一个备用节点成为新的主节点。

Secondary 备用节点:复制集允许有多台Secondary,每个备用节点的数据与主节点的数据是完全同步的。

Recovering 恢复中:当复制集中某台服务器挂掉或者掉线后数据无法同步,重新恢复服务后从其他成员复制数据,这时就处于恢复过程,数据同步后,该节点又回到备用状态。

Arbiter 仲裁节点:该类节点可以不用单独存在,如果配置为仲裁节点,就主要负责在复本集中监控其他节点状态,投票选出主节点。该节点将不会用于存放数据。如果没有仲裁节点,那么投票工作将由所有节点共同进行。

Down 无效节点:当服务器挂掉或掉线时就会处于该状态。

复制集的从节点读请求,也是在各个Driver层设置slaveOk的值来实现的。

 spacer.gif

wKiom1j6FMzAgZLnAAK6yFlPNO4478.png-wh_50


MongoDB(M)表示主节点,Mongodb(S)表示备节点,Mongodb(A)表示仲裁节点。主备节点存储数据,仲裁节点不存储数据。客户端同时连接主节点与备节点,不连接仲裁节点。

默认设置下,主节点提供所有增删查改服务,备节点不提供任何服务。但是可以通过设置使备节点提供查询服务,这样就可以减少主节点的压力,当客户端进行数据查询时,请求自动转到备节点上。这个设置叫做Read Preference Modes,同时Java客户端提供了简单的配置方式,可以不必直接对数据库进行操作。

仲裁节点是一种特殊的节点,它本身并不存储数据,主要的作用是决定哪一个备节点在主节点挂掉之后提升为主节点,所以客户端不需要连接此节点。这里虽然只有一个备节点,但是仍然需要一个仲裁节点来提升备节点级别。我开始也不相信必须要有仲裁节点,但是自己也试过没仲裁节点的话,主节点挂了备节点还是备节点,所以咱们还是需要它的。


一、主机环境情况:

系统:centos6.5---mongdb3.4

mongodb master:192.168.0.248

mongodb slave : 192.168.0.8 192.168.0.9

mongodb arbiter : 192.168.0.247


二、mongodb的安装配置

添加yum源和安装mongodb

https://docs.mongodb.com/manual/tutorial/install-mongodb-on-red-hat/

 

[root@7cest ~]# cat /etc/yum.repos.d/MongoDB.repo

[mongodb-org-3.4]

name=MongoDB Repository

baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/3.4/x86_64/

gpgcheck=1

enabled=1

gpgkey=https://www.mongodb.org/static/pgp/server-3.4.asc

 

[mongodb-org-2.6]

name=MongoDB 2.6 Repository

baseurl=http://downloads-distro.mongodb.org/repo/redhat/os/x86_64/

gpgcheck=0

enabled=1

 

[root@7cest ~]# yum clean all

[root@7cest ~]# yum makecache

 

直接yum安装如下:(mongodb为2.x版本,mongodb-org为3.x版本)

[root@7cest ~]# yum install -y mongodb

[root@7cest ~]# yum install -y mongodb-org

三、配置mongodb

#/etc/ansible/hosts

[redis]

192.168.0.247 ansible_ssh_pass=centos

192.168.0.248 ansible_ssh_pass=centos

[mongo]

192.168.0.9 ansible_ssh_pass=centos

192.168.0.8 ansible_ssh_pass=centos

[M3:children]

redis

mongo

1、创建节点数据目录

建立data文件夹、logs文件夹、conf文件夹:

#ansible M3 -m shell -a 'mkdir -p /mongodb/data/{master,slaver,arbiter}'

#ansible M3 -m shell -a 'mkdir -p /mongodb/{logs,conf}'

创建相关的文件:

#ansible M3 -m shell -a 'touch /mongodb/data/{master.pid,slaver.pid,arbiter.pid}'

#ansible M3 -m shell -a 'touch /mongodb/logs/{master.log,slave.log,arbiter.log}'

2、准备相应节点的配置文件:

[root@202-test repSet-conf]# pwd

/bak/ansiblesoft/mongoDB/repSet-conf

[root@202-test repSet-conf]# ls

mongodb-arbiter.conf  mongodb-master.conf  mongodb-slaver8.conf  mongodb-slaver9.conf 

具体情况:

192.168.0.248主节点:mongodb-master.conf

# cat mongodb-master.conf

#mongodb-master.conf 

dbpath=/mongodb/data/master 

logpath=/mongodb/logs/master.log 

pidfilepath=/mongodb/data/master.pid 

directoryperdb=true 

logappend=true 

replSet=xcprs 

bind_ip=192.168.0.248

port=27017

oplogSize=1000

fork=true 

noprealloc=true

192.168.0.8和192.168.0.9从节点:

# cat mongodb-slaver8.conf

#mongodb-slaver8.conf 

dbpath=/mongodb/data/slaver 

logpath=/mongodb/logs/slaver.log 

pidfilepath=/mongodb/data/slaver.pid 

directoryperdb=true 

logappend=true 

replSet=xcprs 

bind_ip=192.168.0.8

port=27017

oplogSize=1000

fork=true 

noprealloc=true

smallfiles=true

# cat mongodb-slaver9.conf 

#mongodb-slaver9.conf  

dbpath=/mongodb/data/slaver  

logpath=/mongodb/logs/slaver.log  

pidfilepath=/mongodb/data/slaver.pid  

directoryperdb=true  

logappend=true  

replSet=xcprs  

bind_ip=192.168.0.9

port=27017

oplogSize=1000

fork=true  

noprealloc=true

smallfiles=true

192.168.0.247仲裁节点:

# cat mongodb-arbiter.conf

#mongodb-arbiter.conf 

dbpath=/mongodb/data/arbiter 

logpath=/mongodb/logs/arbiter.log 

pidfilepath=/mongodb/data/arbiter.pid 

directoryperdb=true 

logappend=true 

replSet=xcprs 

bind_ip=192.168.0.247

port=27017

oplogSize=1000

fork=true 

noprealloc=true

smallfiles=true

参数解释:

dbpath:数据存放目录

logpath:日志存放路径

pidfilepath:进程文件,方便停止mongodb

directoryperdb:为每一个数据库按照数据库名建立文件夹存放

logappend:以追加的方式记录日志

replSet:replica set的名字

bind_ip:mongodb所绑定的ip地址

port:mongodb进程所使用的端口号,默认为27017

oplogSize:mongodb操作日志文件的最大大小。单位为Mb,默认为硬盘剩余空间的5%

fork:以后台方式运行进程

noprealloc:不预先分配存储

copy文件到指定节点的目录:

# ansible M3 -m copy -a 'src=/bak/ansiblesoft/mongoDB/repSet-conf/mongodb-master.conf dest=/mongodb/conf/mongodb-master.conf backup=yes' --limit=192.168.0.248

# ansible M3 -m copy -a 'src=/bak/ansiblesoft/mongoDB/repSet-conf/mongodb-arbiter.conf dest=/mongodb/conf/mongodb-arbiter.conf backup=yes' --limit=192.168.0.247

# ansible M3 -m copy -a 'src=/bak/ansiblesoft/mongoDB/repSet-conf/mongodb-slaver8.conf dest=/mongodb/conf/mongodb-slaver.conf backup=yes' --limit=192.168.0.8

# ansible M3 -m copy -a 'src=/bak/ansiblesoft/mongoDB/repSet-conf/mongodb-slaver9.conf dest=/mongodb/conf/mongodb-slaver.conf backup=yes' --limit=192.168.0.9

四、启动服务

如果mongodb设置了开机自动启动,请取消开机自动启动

# ansible M3 -m shell -a 'chkconfig mongod off'

# ansible M3 -m shell -a 'chkconfig --list mongod'

根据配置每个节点的配置文件,启动相应的服务:

主节点:

# ansible M3 -m shell -a 'echo "/usr/bin/mongod -f /mongodb/conf/mongodb-master.conf" >>/etc/rc.local' --limit=192.168.0.248

从节点:

# ansible M3 -m shell -a 'echo "/usr/bin/mongod -f /mongodb/conf/mongodb-slaver.conf" >>/etc/rc.local' --limit=192.168.0.8

# ansible M3 -m shell -a 'echo "/usr/bin/mongod -f /mongodb/conf/mongodb-slaver.conf" >>/etc/rc.local' --limit=192.168.0.9

仲裁节点:

# ansible M3 -m shell -a 'echo "/usr/bin/mongod -f /mongodb/conf/mongodb-arbiter.conf" >>/etc/rc.local' --limit=192.168.0.247

重启测试并检测:

# ansible M3 -m shell -a "reboot"

# ansible M3 -m shell -a "lsof -i -P -n | grep mongod"

192.168.0.248 | SUCCESS | rc=0 >>

mongod    1089 root    7u  IPv4  10866      0t0  TCP 192.168.0.248:27017 (LISTEN)

mongod    1089 root   29u  IPv4  13873      0t0  TCP 192.168.0.248:27017->192.168.0.9:54846 (ESTABLISHED)

mongod    1089 root   30u  IPv4  11057      0t0  TCP 192.168.0.248:27017->192.168.0.247:35553 (ESTABLISHED)

mongod    1089 root   31u  IPv4  13350      0t0  TCP 192.168.0.248:27017->192.168.0.8:34084 (ESTABLISHED)

mongod    1089 root   32u  IPv4  13353      0t0  TCP 192.168.0.248:37493->192.168.0.8:27017 (ESTABLISHED)

mongod    1089 root   33u  IPv4  13875      0t0  TCP 192.168.0.248:60433->192.168.0.9:27017 (ESTABLISHED)

mongod    1089 root   34u  IPv4  13371      0t0  TCP 192.168.0.248:49951->192.168.0.247:27017 (ESTABLISHED)

mongod    1089 root   35u  IPv4  13390      0t0  TCP 192.168.0.248:27017->192.168.0.8:34095 (ESTABLISHED)

mongod    1089 root   37u  IPv4  13391      0t0  TCP 192.168.0.248:27017->192.168.0.8:34096 (ESTABLISHED)

如果重启没有生效请手动重启:

# /usr/bin/mongod -f /mongodb/conf/mongodb-slaver.conf

五、配置主、备、仲裁节点(Replica Set集群)

可以通过客户端连接mongodb,也可以直接在三个节点中选择一个连接mongodb


[root@redis248 conf]# mongo 192.168.0.248:27017

MongoDB shell version v3.4.0

connecting to: mongodb://192.168.0.248:27017

MongoDB server version: 3.4.0

Server has startup warnings:

2017-01-13T11:39:12.722+0800 I STORAGE  [initandlisten]

2017-01-13T11:39:13.510+0800 I CONTROL  [initandlisten]

2017-01-13T11:39:13.510+0800 I CONTROL  [initandlisten] ** WARNING: Access control is not enabled for the database.

2017-01-13T11:39:13.510+0800 I CONTROL  [initandlisten] **          Read and write access to data and configuration is unrestricted.

2017-01-13T11:39:13.510+0800 I CONTROL  [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended.

2017-01-13T11:39:13.510+0800 I CONTROL  [initandlisten]

> cfg={_id:"xcprs",members:[{_id:0,host:'192.168.0.248:27017',priority:3},{_id:1,host:'192.168.0.8:27017',priority:2},{_id:2,host:'192.168.0.9:27017',priority:1},{_id:3,host:'192.168.0.247:27017',arbiterOnly:true}]};

{

    "_id" : "xcprs",

    "members" : [

        {

            "_id" : 0,

            "host" : "192.168.0.248:27017",

            "priority" : 3

        },

        {

            "_id" : 1,

            "host" : "192.168.0.8:27017",

            "priority" : 2

        },

        {

            "_id" : 2,

            "host" : "192.168.0.9:27017",

            "priority" : 1

        },

        {

            "_id" : 3,

            "host" : "192.168.0.247:27017",

            "arbiterOnly" : true

        }

    ]

}

> rs.initiate(cfg)

{ "ok" : 1 }

cfg是可以任意的名字,当然最好不要是mongodb的关键字,conf,config都可以。最外层的_id表示replica set的名字,members里包含的是所有节点的地址以及优先级。优先级最高的即成为主节点,即这里的192.168.0.248:27017。特别注意的是,对于仲裁节点,需要有个特别的配置——arbiterOnly:true。这个千万不能少了,不然主备模式就不能生效。配置的生效时间根据不同的机器配置会有长有短,配置不错的话基本上十几秒内就能生效,有的配置需要一两分钟。如果生效了,执行rs.status()命令会看到如下信息:

xcprs:OTHER> rs.status();

{

    "set" : "xcprs",

    "date" : ISODate("2017-01-13T03:44:18.672Z"),

    "myState" : 1,

    "term" : NumberLong(1),

    "heartbeatIntervalMillis" : NumberLong(2000),

    "optimes" : {

        "lastCommittedOpTime" : {

            "ts" : Timestamp(1484279049, 1),

            "t" : NumberLong(1)

        },

        "appliedOpTime" : {

            "ts" : Timestamp(1484279049, 1),

            "t" : NumberLong(1)

        },

        "durableOpTime" : {

            "ts" : Timestamp(1484279049, 1),

            "t" : NumberLong(1)

        }

    },

    "members" : [

        {

            "_id" : 0,

            "name" : "192.168.0.248:27017",

            "health" : 1,

            "state" : 1,

            "stateStr" : "PRIMARY",

            "uptime" : 306,

            "optime" : {

                "ts" : Timestamp(1484279049, 1),

                "t" : NumberLong(1)

            },

            "optimeDate" : ISODate("2017-01-13T03:44:09Z"),

            "electionTime" : Timestamp(1484278888, 1),

            "electionDate" : ISODate("2017-01-13T03:41:28Z"),

            "configVersion" : 1,

            "self" : true

        },

        {

            "_id" : 1,

            "name" : "192.168.0.8:27017",

            "health" : 1,

            "state" : 2,

            "stateStr" : "SECONDARY",

            "uptime" : 180,

            "optime" : {

                "ts" : Timestamp(1484279049, 1),

                "t" : NumberLong(1)

            },

            "optimeDurable" : {

                "ts" : Timestamp(1484279049, 1),

                "t" : NumberLong(1)

            },

            "optimeDate" : ISODate("2017-01-13T03:44:09Z"),

            "optimeDurableDate" : ISODate("2017-01-13T03:44:09Z"),

            "lastHeartbeat" : ISODate("2017-01-13T03:44:17.086Z"),

            "lastHeartbeatRecv" : ISODate("2017-01-13T03:44:17.358Z"),

            "pingMs" : NumberLong(0),

            "syncingTo" : "192.168.0.9:27017",

            "configVersion" : 1

        },

        {

            "_id" : 2,

            "name" : "192.168.0.9:27017",

            "health" : 1,

            "state" : 2,

            "stateStr" : "SECONDARY",

            "uptime" : 180,

            "optime" : {

                "ts" : Timestamp(1484279049, 1),

                "t" : NumberLong(1)

            },

            "optimeDurable" : {

                "ts" : Timestamp(1484279049, 1),

                "t" : NumberLong(1)

            },

            "optimeDate" : ISODate("2017-01-13T03:44:09Z"),

            "optimeDurableDate" : ISODate("2017-01-13T03:44:09Z"),

            "lastHeartbeat" : ISODate("2017-01-13T03:44:17.086Z"),

            "lastHeartbeatRecv" : ISODate("2017-01-13T03:44:17.969Z"),

            "pingMs" : NumberLong(0),

            "syncingTo" : "192.168.0.248:27017",

            "configVersion" : 1

        },

        {

            "_id" : 3,

            "name" : "192.168.0.247:27017",

            "health" : 1,

            "state" : 7,

            "stateStr" : "ARBITER",

            "uptime" : 180,

            "lastHeartbeat" : ISODate("2017-01-13T03:44:17.086Z"),

            "lastHeartbeatRecv" : ISODate("2017-01-13T03:44:14.898Z"),

            "pingMs" : NumberLong(0),

            "configVersion" : 1

        }

    ],

    "ok" : 1

}

xcprs:PRIMARY>

如果配置正在生效,其中会包含如下信息:

"stateStr" : "RECOVERING"

同时可以查看对应节点的日志,发现正在等待别的节点生效或者正在分配数据文件。

六、测试

1、往主节点插入数据,能从备节点查到之前插入的数据

PRIMARY> use test; 

switched to db test 

PRIMARY> db.user.insert({name:'jack',age:80}); 

PRIMARY> db.user.find(); 

{ "_id" : ObjectId("55069502bcdd8c8031522ddb"), "name" : "jack", "age" : 80 }

2、停掉主节点,备节点能变成主节点提供服务

可以直接重启或端口网络即可:

此时,我们可以发现主节点是不可用的

3、恢复主节点,备节点也能恢复其备的角色,而不是继续充当主的角色

主节点192.168.0.248:27017又获取PRIMARY

注意:

当我们在 replica set 进行检索操作时,默认的路由会选择 master 机器,当我们需 要针对任意的从机进行查询的时候,我们需要开启 slaveOk 选项。当我们在没有 开启 salveOk 选项的时候,如果进行此操作会报如下错:

*** not master and slaveok=false 所以我们要进行如下操作:

rs.slaveOk(); // enable querying a secondary

db.user.find(...)