世界上只有一种英雄主义,就是看清生活的真相之后依然热爱生活!

MongoDB 数据库的高可用集群架构(分片+副本集+安全认证)

2019-07-18
196次查阅
2019/7/19

相关概念

mongodb 集群一般是 [mongos 路由] + [shard 分片] + [replicateSet 副本集] + [config 配置服务] 组成。

[mongos 路由]

数据库集群请求的入口,所有的请求都通过 mongos 进行协调,不需要在应用程序添加一个路由选择器,mongos 自己就是一个请求分发中心,它负责把对应的数据请求请求转发到对应的shard服务器上。在生产环境通常有多 mongos 作为请求的入口,防止其中一个挂掉所有的 mongodb 请求都没有办法操作。

[shard 分片]

是指将数据库拆分,将其分散在不同的机器上的过程。将数据分散到不同的机器上,不需要功能强大的服务器就可以存储更多的数据和处理更大的负载。基本思想就是将集合切成小块,这些块分散到若干片里,每个片只负责总数据的一部分,最后通过一个均衡器来对各个分片进行均衡(数据迁移)。在生产环境中,所有的分片都应该是副本集。

[replicateSet 副本集]

其实就是 shard 的备份,防止 shard 挂掉之后数据丢失。复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性, 并可以保证数据的安全性。

[config 配置服务]

存储所有数据库元信息(路由、分片)的配置。mongos本身没有物理存储分片服务器和数据路由信息,只是缓存在内存里,配置服务器则实际存储这些数据。mongos第一次启动或者关掉重启就会从 config server 加载配置信息,以后如果配置服务器信息变化会通知到所有的 mongos 更新自己的状态,这样 mongos 就能继续准确路由。在生产环境通常有多个 config server 配置服务器,因为它存储了分片路由的元数据,防止数据丢失!

仲裁者(Arbiter)

是复制集中的一个 MongoDB 实例,它并不保存数据。仲裁节点使用最小的资源并且不要求硬件设备,不能将 Arbiter 部署在同一个数据集节点中,可以部署在其他应用服务器或者监视服务器中,也可部署在单独的虚拟机中。为了确保复制集中有奇数的投票成员(包括 primary),需要添加仲裁节点做为投票,否则 primary 宕机时不会自动切换。

概念总结

应用请求 mongos 来操作 mongodb 的增删改查,配置服务器存储数据库元信息,并且和 mongos 做同步,数据最终存入在 shard(分片)上,为了防止数据丢失同步在副本集中存储了一份,仲裁在数据存储到分片的时候决定存储到哪个节点。

实验环境规划和准备工作

服务器系统:CentOS Linux release 7.6.1810 (Core) 

服务器IP:

  • 192.168.0.3(node-1)
  • 192.168.0.4(node-2)
  • 192.168.0.4(node-3)

实验环境拓扑图(实验环境把配置服务器和路由服务器都放在一起了):

端口分配:

  • mongos:20000
  • config server:21000
  • shard-1(分片1):27001
  • shard-2(分片2):27002
  • shard-3(分片3):27003

关闭防火墙和selinux:

systemctl disable firewalld && systemctl stop firewalld

setenforce 0
sed -i 's/^SELINUX=.*$/SELINUX=disabled/g' /etc/selinux/config

从Centos7版本开始,MongoDB会建议关闭系统的THP特性,否则可能会导致性能下降。

echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag

并将其写入到 /etc/rc.d/rc.local 文件中。
chmod +x /etc/rc.d/rc.local

以上操作完毕后,重启服务器。

安装mongodb

1、下载地址

2、下载二进制包安装

wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-3.6.13.tgz
tar zxvf mongodb-linux-x86_64-3.6.13.tgz
mv mongodb-linux-x86_64-3.6.13 /usr/local/mongodb
ln -s /usr/local/mongodb/bin/* /usr/local/bin/

3、创建数据和日志目录

分别在每台机器创建以下目录,其中 mongos 目录不存储数据,只需创建日志目录。

mkdir -p /usr/local/mongodb/conf
mkdir -p /data/mongodb/{config-server,mongos,shard-{1,2,3}}/log
mkdir -p /data/mongodb/{config-server,shard-{1,2,3}}/data

4、创建管理用户

groupadd mongo
useradd -s /sbin/nologin -g mongo mongo
chown -R mongo:mongo /usr/local/mongodb
chown -R mongo:mongo /data/mongodb

配置服务器 config server(三节点操作一致)

1、配置服务器也需要创建副本集,否则无法搭建集群,创建配置文件。配置文件官方教程:https://docs.mongodb.com/v3.6/reference/configuration-options/

vim /usr/local/mongodb/conf/config.conf
systemLog:
  destination: file
  logAppend: true
  path: /data/mongodb/config-server/log/config-srv.log
 
storage:
  engine: wiredTiger
  dbPath: /data/mongodb/config-server/data
  journal:
    enabled: true

processManagement:
  fork: true
  pidFilePath: /data/mongodb/config-server/log/config-srv.pid

net:
  port: 21000
  bindIp: 0.0.0.0

# 安全认证(集群搭建好之后再添加认证配置,下面的也一样)
#security:
#  keyFile: /data/mongodb/MongoKeyFile
#  authorization: enabled

# 副本集名称
replication:
  replSetName: configs

# 声明以配置服务器启动实例
sharding:
  clusterRole: configsvr

2、创建 systemd unit 文件来管理 config server

vim /usr/lib/systemd/system/mongo-config.service
[Unit]
Description=MongoDB Config Server
After=network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
User=mongo
Group=mongo
ExecStart=/usr/local/mongodb/bin/mongod -f /usr/local/mongodb/conf/config.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/usr/local/mongodb/bin/mongod --shutdown -f /usr/local/mongodb/conf/config.conf
PrivateTmp=true

[Install]
WantedBy=multi-user.target

3、分别启动三台配置服务器

systemctl daemon-reload
systemctl start mongo-config
systemctl enable mongo-config
systemctl status mongo-config

注:三台配置服务器都配置好并启动后,再进行以下操作。

4、初始化副本集

登录任意一台服务器执行:

[root@node-1 ~]# mongo --port 21000

> use admin;

# 配置 config 变量
> config = {
    _id : "configs",
     members : [
         {_id : 0, host : "192.168.0.3:21000" },
         {_id : 1, host : "192.168.0.4:21000" },
         {_id : 2, host : "192.168.0.5:21000" }
     ]
};

# 初始化副本集:
> rs.initiate(config)

# 查看状态
> rs.status()

注:这里的_id : "configs"必须和配置文件里replSet = configs一致。

三台分片服务器搭建副本集

配置 shard-1(分片1)服务器

1、在每台服务器上创建 shard-1 配置文件:

vim /usr/local/mongodb/conf/shard-1.conf
systemLog:
  destination: file
  logAppend: true
  path: /data/mongodb/shard-1/log/shard-1.log
 
storage:
  engine: wiredTiger
  dbPath: /data/mongodb/shard-1/data
  journal:
    enabled: true

processManagement:
  fork: true
  pidFilePath: /data/mongodb/shard-1/log/shard-1.pid
 
net:
  port: 27001
  bindIp: 0.0.0.0

#security:
#  keyFile: /data/mongodb/MongoKeyFile
#  authorization: enabled

# 副本集名称
replication:
  replSetName: shard-1

# 声明以分片服务器启动实例
sharding:
  clusterRole: shardsvr

2、创建 systemd unit 文件来管理 shard-1:

vim /usr/lib/systemd/system/mongo-shard-1.service
[Unit]
Description=MongoDB Shard-1 Server
After= mongo-config.target network.target

[Service]
Type=forking
User=mongo
Group=mongo
ExecStart=/usr/local/mongodb/bin/mongod -f /usr/local/mongodb/conf/shard-1.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/usr/local/mongodb/bin/mongod --shutdown -f /usr/local/mongodb/conf/shard-1.conf
PrivateTmp=true

[Install]
WantedBy=multi-user.target

3、分别启动三台服务器的shard-1 server:

systemctl daemon-reload
systemctl start mongo-shard-1
systemctl enable mongo-shard-1

4、三台服务器都启动 shard-1 server 后,任选一台进行初始化副本集操作:

[root@node-2 ~]# mongo --port 27001

# 进入 admin 库
> use admin;

# 配置 shard-1 副本集的地址和端口
> config = {
    _id: "shard-1",
    members: [
        {_id: 0, host: "192.168.0.3:27001"},
        {_id: 1, host: "192.168.0.4:27001"},
        {_id: 2, host: "192.168.0.5:27001", arbiterOnly: true}
    ]
};

# 初始化副本集
rs.initiate(config);

# 查看状态
rs.status();

注:arbiterOnly: true标识为仲裁节点。

2、配置 shard-2(分片2)服务器

1、在每台服务器上创建 shard-2 配置文件:

vim /usr/local/mongodb/conf/shard-2.conf
systemLog:
  destination: file
  logAppend: true
  path: /data/mongodb/shard-2/log/shard-2.log
 
storage:
  dbPath: /data/mongodb/shard-2/data
  journal:
    enabled: true

processManagement:
  fork: true
  pidFilePath: /data/mongodb/shard-2/log/shard-2.pid
 
net:
  port: 27002
  bindIp: 0.0.0.0

#security:
#  keyFile: /data/mongodb/MongoKeyFile
#  authorization: enabled

# 副本集名称
replication:
  replSetName: shard-2

# 声明以分片服务器启动实例
sharding:
  clusterRole: shardsvr

2、创建 systemd unit 文件来管理 shard-2:

vim /usr/lib/systemd/system/mongo-shard-2.service
[Unit]
Description=MongoDB Shard-2 Server
After= mongo-config.target network.target

[Service]
Type=forking
User=mongo
Group=mongo
ExecStart=/usr/local/mongodb/bin/mongod -f /usr/local/mongodb/conf/shard-2.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/usr/local/mongodb/bin/mongod --shutdown -f /usr/local/mongodb/conf/shard-2.conf
PrivateTmp=true

[Install]
WantedBy=multi-user.target

3、分别启动三台服务器的 shard-2 server:

systemctl daemon-reload
systemctl start mongo-shard-2
systemctl enable mongo-shard-2

4、三台服务器都启动 shard-2 server 后,任选一台进行初始化副本集操作:

[root@node-2 ~]# mongo --port 27002

# 进入 admin 库
> use admin;

# 配置
> config = {
    _id: "shard-2",
    members: [
        {_id: 0, host: "192.168.0.3:27002", arbiterOnly: true},
        {_id: 1, host: "192.168.0.4:27002"},
        {_id: 2, host: "192.168.0.5:27002"}
    ]
};

# 初始化
rs.initiate(config);

# 查看状态
rs.status();

3、配置 shard-3(分片3)服务器

1、在每台服务器上创建 shard-3 配置文件:

vim /usr/local/mongodb/conf/shard-3.conf
systemLog:
  destination: file
  logAppend: true
  path: /data/mongodb/shard-3/log/shard-3.log
 
storage:
  dbPath: /data/mongodb/shard-3/data
  journal:
    enabled: true

processManagement:
  fork: true
  pidFilePath: /data/mongodb/shard-3/log/shard-3.pid
 
net:
  port: 27003
  bindIp: 0.0.0.0

#security:
#  keyFile: /data/mongodb/MongoKeyFile
#  authorization: enabled

# 副本集名称
replication:
  replSetName: shard-3

# 声明以分片服务器启动实例
sharding:
  clusterRole: shardsvr

2、创建 systemd unit 文件来管理 shard-3:

vim /usr/lib/systemd/system/mongo-shard-3.service
[Unit]
Description=MongoDB Shard-3 Server
After= mongo-config.target network.target

[Service]
Type=forking
User=mongo
Group=mongo
ExecStart=/usr/local/mongodb/bin/mongod -f /usr/local/mongodb/conf/shard-3.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/usr/local/mongodb/bin/mongod --shutdown -f /usr/local/mongodb/conf/shard-3.conf
PrivateTmp=true

[Install]
WantedBy=multi-user.target

分别启动三台服务器的shard-3 server:

systemctl daemon-reload
systemctl start mongo-shard-3
systemctl enable mongo-shard-3

4、三台服务器都启动 shard-3 server 后,任选一台进行初始化副本集操作:

[root@node-1 ~]# mongo --port 27003

> use admin;

> config = {
    _id: "shard-3",
    members: [
        {_id: 0, host: "192.168.0.3:27003"},
        {_id: 1, host: "192.168.0.4:27003", arbiterOnly: true},
        {_id: 2, host: "192.168.0.5:27003"}
    ]
};

rs.initiate(config);
rs.status();

配置 mongos 路由服务器

先要启动配置服务器和分片服务器,最后启动 mongos 路由服务器。

1、在每台机器上创建 mongos 配置文件:

vim /usr/local/mongodb/conf/mongos.conf
systemLog:
  destination: file
  logAppend: true
  path: /data/mongodb/mongos/log/mongos.log
  
processManagement:
  fork: true
  pidFilePath: /data/mongodb/mongos/log/mongos.pid
  
net:
  port: 20000
  bindIp: 0.0.0.0

#security:
#  keyFile: /data/mongodb/MongoKeyFile

# 监听配置服务器 IP:端口,只能有1个或者3个
# configs为配置服务器的副本集名字
sharding:
  configDB: configs/192.168.0.3:21000,192.168.0.4:21000,192.168.0.5:21000

2、创建 systemd unit 文件来管理 mongos:

vim /usr/lib/systemd/system/mongos.service
[Unit]
Description=MongoDB Router Service (mongos)
After=mongo-config.service

[Service]
Type=forking
User=mongo
Group=mongo
ExecStart=/usr/local/mongodb/bin/mongos --config /usr/local/mongodb/conf/mongos.conf
Restart=on-failure

[Install]
WantedBy=multi-user.target

3、分别在3台机器上启动 mongos 服务:

systemctl daemon-reload
systemctl start mongos
systemctl enable mongos

4、启动分片功能(登录任意一台服务器操作):

mongo --port 20000

# 串联路由服务器与分配副本集
use admin;
sh.addShard("shard-1/192.168.0.3:27001,192.168.0.4:27001,192.168.0.5:27001");
sh.addShard("shard-2/192.168.0.3:27002,192.168.0.4:27002,192.168.0.5:27002");
sh.addShard("shard-3/192.168.0.3:27003,192.168.0.4:27003,192.168.0.5:27003");
sh.status();

到此配置服务、路由服务、分片服务、副本集服务都已配置完毕,下面需要做插入数据,希望数据能够自动分片。连接在mongos上,准备让指定的数据库、指定的集合分片生效。

集群安全认证配置

1、生成密钥文件

生产环境必须启用权限认证,对副本集执行访问控制需要配置两个方面:

  1. 集群各个节点之间使用内部身份验证,可以使用密钥文件或x.509证书,这里使用的是密钥文件方式。
  2. 使用客户端、代码等外部访问连接到 mongodb 集群时,开启访问授权。

在 node-1 节点生成密钥文件,密钥文件的内容必须在 6 到 1024 个字符之间:

openssl rand -base64 512 > /data/mongodb/MongoKeyFile
chown -R mongo:mongo /data/mongodb/MongoKeyFile
chmod 400 /data/mongodb/MongoKeyFile

将密钥文件分发到各个服务器上并赋予权限:

scp /data/mongodb/MongoKeyFile root@node-2:/data/mongodb
scp /data/mongodb/MongoKeyFile root@node-3:/data/mongodb
chown -R mongo:mongo /usr/local/mongodb
chown -R mongo:mongo /data/mongodb

2、配置超级管理用户

连接任意一台机器的 mongos 操作:

mongo --port 20000

# 这里必须进入 admin 库操作
use admin;
db.createUser(
  {
    user: "admin",
    pwd: "pwd123",
    roles: [ "root" ]
  }
);

root角色为所有资源提供完全权限,相当于MySQL里的 root 账户。

3、启用认证

先关闭每台机器所有的mongodb服务:

systemctl stop mongos.service
systemctl stop mongo-config.service
systemctl stop mongo-shard-1.service
systemctl stop mongo-shard-2.service
systemctl stop mongo-shard-3.service

在每台机器上的mongod(注意是 mongod 不是 mongos)的配置文件,也就时编辑config server、shard-1、shard-2、和shard-3服务器的配置文件,加入下面一段配置:

security:
  keyFile: /data/mongodb/MongoKeyFile
  authorization: enabled

修改每台机器的 mongos 的配置文件如下:

security:
  keyFile: /data/mongodb/MongoKeyFile

mongos 比 mongod 少了authorization:enabled的配置,是因为 mongos 只是和集群内部实例进行通信认证,只有真正保存数据的 mongod 实例才需要添加此配置,用于外部客户端认证。

最后依次启动所有mongodb服务:

systemctl start mongo-config.service
systemctl start mongo-shard-1.service
systemctl start mongo-shard-2.service
systemctl start mongo-shard-3.service
systemctl start mongos.service

如果出现启动报错问题,尝试删除每台服务器的mongod.lock文件再试:

rm -fr /data/mongodb/config-server/data/mongod.lock
rm -fr /data/mongodb/shard-1/data/mongod.lock
rm -fr /data/mongodb/shard-2/data/mongod.lock
rm -fr /data/mongodb/shard-3/data/mongod.lock

测试

示例使用 testdb 数据库中的 table1 集合测试:

连接 mongos 服务端口:

mongo --port 20000

指定要分片的数据库:

mongos> sh.enableSharding("testdb");
{
        "ok" : 1,
        "operationTime" : Timestamp(1563267824, 10),
        "$clusterTime" : {
                "clusterTime" : Timestamp(1563267824, 10),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        }
}

指定数据库里要分片的集合和片键:

mongos> sh.shardCollection("testdb.table1",{id:1});
{
        "collectionsharded" : "testdb.table1",
        "collectionUUID" : UUID("aebe4e43-ec34-4018-a8c4-a4d43799181b"),
        "ok" : 1,
        "operationTime" : Timestamp(1563267863, 15),
        "$clusterTime" : {
                "clusterTime" : Timestamp(1563267863, 15),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        }
}

进入testdb库,插入数据:

use testdb
for(var i=1;i<=20000;i++) db.table1.save({id:i, testkey:"test_val_"+i})

查看数据:

db.getCollection("table1").find({});

查看分片的详细信息:

db.printShardingStatus() 或 sh.status()

评论

想说点什么?