MongoDB 是一款面向文档的数据库管理系统,使用 C++ 语言编写,用以解决应用程序开发过程中遇到的新问题,是目前最为常用的 NoSQL 数据库。由于其优越的性能和分布式架构,受到广大网络应用程序开发人员的追捧。
MongoDB 本身具有自制集功能,用于提升系统的高可用性。本文将详细讲解如何在 RHEL/CentOS 7 配置 MongoDB复制集群,用以提供数据冗余和高可用性。
1] 什么是MongoDB Replica
MongoDB Replica(复制集)可以理解为在不同 MongoDB 服务器上的 mongod 进程通过某种机制维护相同的数据集合。复制集根据功能至少需要三个节点,用于扮演不同的角色,分别为 Primary(主用)、Secondary(辅助)以及Arbiter(仲裁)。
- 主用节点:它是复制集的主服务器,所有读写操作都将在其上执行。
- 辅助节点:辅助节点将与主用节点同步,维护本节点具有同主用节点相同数据集的副本。 一个复制集中可以有多个辅助节点。当主用节点不可用时,复制集将在可用的辅助节点中选举一个节点,被选举出的节点将成为此复制集的下一个主用节点并继续正常提供服务,从而为数据库客户端提供高数据可用性。
- 仲裁节点:仲裁节点本身并不保存数据副本,其最重要的功能是在主用节点选举时确定哪个辅助节点将成为主用节点。仲裁节点通常用于具有多个辅助节点的复制集中,有助于减少选举时间。
2] 系统环境
节点1:
- 操作系统:CentOS 7
- 数据库:MongoDB 4.2
- IP地址:172.16.200.1
- 主机名:mongodb-node1
节点2:
- 操作系统:CentOS 7
- 数据库:MongoDB 4.2
- IP地址:172.16.200.2
- 主机名:mongodb-node2
节点3:
- 操作系统:CentOS 7
- 数据库:MongoDB 4.2
- IP地址:172.16.200.3
- 主机名:mongodb-node3
以上三个 MongoDB 节点,会有一台作为复制集的主用节点,而其他两台则为辅助节点。
编辑”/etc/hosts”文件,将以下内容添加到三台节点服务器中:
172.16.200.1 mongodb-node1 172.16.200.2 mongodb-node2 172.16.200.3 mongodb-node3
3] 安装 MongoDB 数据库
MongoDB 数据库安装比较简单,CentOS 7 系统可以使用 YUM 方式安装,首先创建一个”/etc/yum.repos.d/mongodb-org-4.2.repo”文件,然后将如下内容添加到文件中,用于指定 MongoDB 的软件库:
[mongodb-org-4.2] name=MongoDB Repository baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/4.2/x86_64/ gpgcheck=1 enabled=1 gpgkey=https://www.mongodb.org/static/pgp/server-4.2.asc
之后,就可以使用如下命令安装 MongoDB 数据库服务器了:
# yum install -y mongodb-org
4] 配置复制集
MongoDB 服务器的默认配置文件为”/etc/mongod.conf”,需要修改配置文件中的如下内容来启用复制集:
- Log File Path(日志文件路径):/var/log/mongodb/mongod.log
- DB Path(数据文件路径):/var/lib/mongo
- Port(端口号):默认使用27017
- Bind IP(绑定IP):默认侦听 127.0.0.1,即侦听本机,需要修改为0.0.0.0,即侦听所有IP地址
- Replication(复制):取消注释它以启用复制功能
- replSetName(复制集名称):启用复制功能后,就需要设置复制集名称,这里设置为”mongodb-rs”
注意:复制集名称可以自行设定,但是要求复制集中所有节点的复制集名称保持一致。
图.1 修改 MongoDB 配置文件启用复制集
修改完成后,重新启动所有节点 MongoDB 使用修改的内容生效。
# systemctl restart mongod # systemctl status mongod
5] 初始化复制集
复制集配置完成后,需要使用 Mongo Shell 命令初始化复制集,将 MongoDB 主机节点信息添加至复制集。登录成功后,使用如下命令添加复制集节点:
rs.initiate( { ... _id : "mongodb-rs", ... members: [ ... { _id: 0, host: "mongodb-node1:27017"}, ... { _id: 1, host: "mongodb-node2:27017"}, ... { _id: 2, host: "mongodb-node3:27017"} ... ] ... })
不出意外的话,复制集就启动成功了,启动成功后,命令提示符前面会显示当前节点在复制集中担任的角色(Primary 或者 Secondary)。使用如下命令便可以查看复制集的状态信息:
mongodb-rs:PRIMARY> rs.status() { "set" : "mongodb-rs", "date" : ISODate("2019-10-16T01:06:58.814Z"), "myState" : 1, "term" : NumberLong(2), "syncingTo" : "", "syncSourceHost" : "", "syncSourceId" : -1, "heartbeatIntervalMillis" : NumberLong(2000), "optimes" : { "lastCommittedOpTime" : { "ts" : Timestamp(1571188017, 1), "t" : NumberLong(2) }, "lastCommittedWallTime" : ISODate("2019-10-16T01:06:57.942Z"), "readConcernMajorityOpTime" : { "ts" : Timestamp(1571188017, 1), "t" : NumberLong(2) }, "readConcernMajorityWallTime" : ISODate("2019-10-16T01:06:57.942Z"), "appliedOpTime" : { "ts" : Timestamp(1571188017, 1), "t" : NumberLong(2) }, "durableOpTime" : { "ts" : Timestamp(1571188017, 1), "t" : NumberLong(2) }, "lastAppliedWallTime" : ISODate("2019-10-16T01:06:57.942Z"), "lastDurableWallTime" : ISODate("2019-10-16T01:06:57.942Z") }, "lastStableRecoveryTimestamp" : Timestamp(1571187977, 1), "lastStableCheckpointTimestamp" : Timestamp(1571187977, 1), "members" : [ { "_id" : 0, "name" : "mongodb-node1:27017", "ip" : "172.16.200.1", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 438, "optime" : { "ts" : Timestamp(1571188017, 1), "t" : NumberLong(2) }, "optimeDurable" : { "ts" : Timestamp(1571188017, 1), "t" : NumberLong(2) }, "optimeDate" : ISODate("2019-10-16T01:06:57Z"), "optimeDurableDate" : ISODate("2019-10-16T01:06:57Z"), "lastHeartbeat" : ISODate("2019-10-16T01:06:58.530Z"), "lastHeartbeatRecv" : ISODate("2019-10-16T01:06:58.509Z"), "pingMs" : NumberLong(0), "lastHeartbeatMessage" : "", "syncingTo" : "mongodb-node3:27017", "syncSourceHost" : "mongodb-node3:27017", "syncSourceId" : 3, "infoMessage" : "", "configVersion" : 1 }, { "_id" : 1, "name" : "mongodb-node2:27017", "ip" : "172.16.200.2", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 455, "optime" : { "ts" : Timestamp(1571188017, 1), "t" : NumberLong(2) }, "optimeDate" : ISODate("2019-10-16T01:06:57Z"), "syncingTo" : "", "syncSourceHost" : "", "syncSourceId" : -1, "infoMessage" : "", "electionTime" : Timestamp(1571187576, 1), "electionDate" : ISODate("2019-10-16T00:59:36Z"), "configVersion" : 1, "self" : true, "lastHeartbeatMessage" : "" }, { "_id" : 2, "name" : "mongodb-node3:27017", "ip" : "172.16.200.3", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 452, "optime" : { "ts" : Timestamp(1571188017, 1), "t" : NumberLong(2) }, "optimeDurable" : { "ts" : Timestamp(1571188017, 1), "t" : NumberLong(2) }, "optimeDate" : ISODate("2019-10-16T01:06:57Z"), "optimeDurableDate" : ISODate("2019-10-16T01:06:57Z"), "lastHeartbeat" : ISODate("2019-10-16T01:06:58.519Z"), "lastHeartbeatRecv" : ISODate("2019-10-16T01:06:57.049Z"), "pingMs" : NumberLong(0), "lastHeartbeatMessage" : "", "syncingTo" : "mongodb-node2:27017", "syncSourceHost" : "mongodb-node2:27017", "syncSourceId" : 1, "infoMessage" : "", "configVersion" : 1 } ], "ok" : 1, "$clusterTime" : { "clusterTime" : Timestamp(1571188017, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } }, "operationTime" : Timestamp(1571188017, 1) }
从命令的输出结果可以看出,当前复制集的主用节点为”_id: 1″的节点,也就是 node2。
6] 测试复制集高可用
如果因为某种原因,主用节点故障,则复制集会自动选举一台辅助节点作为主用节点。测试方便,停止 Node2 上的 MongoDB 服务,然后在其他点查看复制集状态。
mongodb-rs:SECONDARY> mongodb-rs:PRIMARY> mongodb-rs:PRIMARY> rs.status() { "set" : "mongodb-rs", "date" : ISODate("2019-10-16T01:22:11.914Z"), "myState" : 1, "term" : NumberLong(3), "syncingTo" : "", "syncSourceHost" : "", "syncSourceId" : -1, "heartbeatIntervalMillis" : NumberLong(2000), "optimes" : { "lastCommittedOpTime" : { "ts" : Timestamp(1571188927, 1), "t" : NumberLong(3) }, "lastCommittedWallTime" : ISODate("2019-10-16T01:22:07.626Z"), "readConcernMajorityOpTime" : { "ts" : Timestamp(1571188927, 1), "t" : NumberLong(3) }, "readConcernMajorityWallTime" : ISODate("2019-10-16T01:22:07.626Z"), "appliedOpTime" : { "ts" : Timestamp(1571188927, 1), "t" : NumberLong(3) }, "durableOpTime" : { "ts" : Timestamp(1571188927, 1), "t" : NumberLong(3) }, "lastAppliedWallTime" : ISODate("2019-10-16T01:22:07.626Z"), "lastDurableWallTime" : ISODate("2019-10-16T01:22:07.626Z") }, "lastStableRecoveryTimestamp" : Timestamp(1571188898, 1), "lastStableCheckpointTimestamp" : Timestamp(1571188898, 1), "members" : [ { "_id" : 0, "name" : "mongodb-node1:27017", "ip" : "172.16.200.1", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 1355, "optime" : { "ts" : Timestamp(1571188927, 1), "t" : NumberLong(3) }, "optimeDate" : ISODate("2019-10-16T01:22:07Z"), "syncingTo" : "", "syncSourceHost" : "", "syncSourceId" : -1, "infoMessage" : "", "electionTime" : Timestamp(1571188915, 1), "electionDate" : ISODate("2019-10-16T01:21:55Z"), "configVersion" : 1, "self" : true, "lastHeartbeatMessage" : "" }, { "_id" : 1, "name" : "mongodb-node2:27017", "ip" : "172.16.200.2", "health" : 0, "state" : 8, "stateStr" : "(not reachable/healthy)", "uptime" : 0, "optime" : { "ts" : Timestamp(0, 0), "t" : NumberLong(-1) }, "optimeDurable" : { "ts" : Timestamp(0, 0), "t" : NumberLong(-1) }, "optimeDate" : ISODate("1970-01-01T00:00:00Z"), "optimeDurableDate" : ISODate("1970-01-01T00:00:00Z"), "lastHeartbeat" : ISODate("2019-10-16T01:22:11.770Z"), "lastHeartbeatRecv" : ISODate("2019-10-16T01:21:54.904Z"), "pingMs" : NumberLong(0), "lastHeartbeatMessage" : "Error connecting to mongodb-node2:27017 (172.16.200.2:27017) :: caused by :: Co nnection refused", "syncingTo" : "", "syncSourceHost" : "", "syncSourceId" : -1, "infoMessage" : "", "configVersion" : -1 }, { "_id" : 2, "name" : "mongodb-node3:27017", "ip" : "172.16.200.3", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 1353, "optime" : { "ts" : Timestamp(1571188927, 1), "t" : NumberLong(3) }, "optimeDurable" : { "ts" : Timestamp(1571188927, 1), "t" : NumberLong(3) }, "optimeDate" : ISODate("2019-10-16T01:22:07Z"), "optimeDurableDate" : ISODate("2019-10-16T01:22:07Z"), "lastHeartbeat" : ISODate("2019-10-16T01:22:11.743Z"), "lastHeartbeatRecv" : ISODate("2019-10-16T01:22:10.419Z"), "pingMs" : NumberLong(0), "lastHeartbeatMessage" : "", "syncingTo" : "mongodb-node1:27017", "syncSourceHost" : "mongodb-node1:27017", "syncSourceId" : 0, "infoMessage" : "", "configVersion" : 1 } ], "ok" : 1, "$clusterTime" : { "clusterTime" : Timestamp(1571188927, 1), "signature" : { "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="), "keyId" : NumberLong(0) } }, "operationTime" : Timestamp(1571188927, 1) } mongodb-rs:PRIMARY>
在停止 Node2 服务的同时,复制集自动将 Node1 设置为主用,查看状态的话,可以看到node1状态为”PRIMARY”;Node2状态为”(not reachable/healthy)”;Node3状态为”SECONDARY”。复制集的工作如预想的一样正常。
至此,我们已经成功部署了一个高可用的 MongoDB 集群(MongoDB 复制集),该集群具有三个节点,一个是主用节点,其他是与主节点同步并维护相同数据集的辅助节点,从而为数据库基础架构提供可靠性 。