MongoDB-集群搭建

发布于 2021-07-27  10 次阅读


一、环境

mongodb 3.4.24

二、安装

添加运行mongodb的用户mongo,避免直接使用root带来安全隐患

groupadd -g 1608 mongo
useradd -u 1608 -g mongo mongo

下载源码包

wget http://downloads.mongodb.org/linux/mongodb-linux-x86_64-rhel70-3.4.24.tgz

解压源码包

tar -xf mongodb-linux-x86_64-rhel70-3.4.24.tgz -C /usr/local/

准备mongodb 配置文件

mkdir /data/mongodb/{data,logs}  -p
mkdir /usr/local/mongodb/conf

添加到环境里面

vim /etc/profile
export MONGODB_HOME=/usr/local/mongodb
export PATH=$MONGODB_HOME/bin:$PATH

三、集群搭建

此次搭建为 mongo * 1 、3 shard * 3 、1 config * 3

目录及配置使用

configsvr的三个节点的数据目录和日志目录

/home/mongodb/config1/data
/home/mongodb/config1/log
/home/mongodb/config2/data
/home/mongodb/config2/log
/home/mongodb/config3/data
/home/mongodb/config3/log

shardsvr1的三个节点的数据目录和日志目录

/home/mongodb/shard11/data
/home/mongodb/shard11/log
/home/mongodb/shard12/data
/home/mongodb/shard12/log
/home/mongodb/shard13/data
/home/mongodb/shard13/log

shardsvr2的三个节点的数据目录和日志目录

/home/mongodb/shard21/data
/home/mongodb/shard21/log
/home/mongodb/shard22/data
/home/mongodb/shard22/log
/home/mongodb/shard23/data
/home/mongodb/shard23/log

shardsvr3的三个节点的数据目录和日志目录

/home/mongodb/shard31/data
/home/mongodb/shard31/log
/home/mongodb/shard32/data
/home/mongodb/shard32/log
/home/mongodb/shard33/data
/home/mongodb/shard33/log

mongos节点的日志目录

/home/mongodb/mongos/log

搭建configsvr副本集

1.准备单节点配置文件和数据目录,以单节点方式启动进程。

net:
  bindIp: 0.0.0.0
  port: 21001
  unixDomainSocket: {enabled: false}
processManagement: {fork: true, pidFilePath: /home/mongodb/config1/configsvr.pid}
storage:
  dbPath: /home/mongodb/config1/data/
  directoryPerDB: true
  engine: wiredTiger
  wiredTiger:
    collectionConfig: {blockCompressor: snappy}
    engineConfig: {directoryForIndexes: true, journalCompressor: snappy}
    indexConfig: {prefixCompression: true}
systemLog: {destination: file, logAppend: true, logRotate: reopen, path: /home/mongodb/config1/log/configsvr.log}

启动进程
mongod -f config1.conf

  1. 连接单节点,执行配置命令。
执行如下命令,修改副本集配置信息。
var cf=db.getSiblingDB('local').system.replset.findOne();
cf['members'][0]['host']='127.0.0.1:21001';
cf['members'][1]['host']='127.0.0.1:21002';
cf['members'][2]['host']='127.0.0.1:21003';
cf['members'][0]['hidden']=false;
cf['members'][1]['hidden']=false;
cf['members'][2]['hidden']=false;
cf['members'][0]['priority']=1;
cf['members'][1]['priority']=1;
cf['members'][2]['priority']=1;
db.getSiblingDB('local').system.replset.remove({});
db.getSiblingDB('local').system.replset.insert(cf)

执行如下命令,清理内置账号。(新搭建的无需此操作)
db.getSiblingDB('admin').dropAllUsers();
db.getSiblingDB('admin').dropAllRoles();
执行如下命令,更新mongos和shard信息。
db.getSiblingDB('config').mongos.remove({});

先查询config.shards表中的多个shard的_id信息,用于下面语句的_id的查询条件。逐个更新每条记录。
db.getSiblingDB('config').shards.update({'_id' : 'shard_1'},{$set: {'host': 'shard_1/127.0.0.1:27001,127.0.0.1:27002,127.0.0.1:27003'}})
db.getSiblingDB('config').shards.update({'_id' : 'shard_2'},{$set: {'host': 'shard_2/127.0.0.1:27004,127.0.0.1:27005,127.0.0.1:27006'}})
db.getSiblingDB('config').shards.update({'_id' : 'shard_3'},{$set: {'host': 'shard_3/127.0.0.1:27007,127.0.0.1:27008,127.0.0.1:27009'}})
db.getSiblingDB('config').mongos.find({});
db.getSiblingDB('config').shards.find({});

执行如下命令,关闭单节点进程
db.getSiblingDB('admin').shutdownServer();

3.搭建configsvr副本集
准备副本集配置文件和目录,将configsvr1节点的dbPath文件拷贝到其他两个节点目录下。

cp -aR /home/mongodb/config1/data /home/mongodb/config2/data
cp -aR /home/mongodb/config1/data /home/mongodb/config3/data

修改configsvr-1节点配置文件,增加副本集配置属性

net:
  bindIp: 0.0.0.0
  port: 21001
  unixDomainSocket: {enabled: false}
processManagement: {fork: true, pidFilePath: /home/mongodb/config1/configsvr.pid}
replication: {replSetName: config}
sharding: {archiveMovedChunks: false, clusterRole: configsvr}
storage:
  dbPath: /home/mongodb/config1/data/
  directoryPerDB: true
  engine: wiredTiger
  wiredTiger:
    collectionConfig: {blockCompressor: snappy}
    engineConfig: {directoryForIndexes: true, journalCompressor: snappy}
    indexConfig: {prefixCompression: true}
systemLog: {destination: file, logAppend: true, logRotate: reopen, path: /home/mongodb/config1/log/configsvr.log}

启动进程
mongod -f config1.conf

修改configsvr-2节点配置文件,增加副本集配置属性
复制config1配置文件,修改端口信息、数据和日志文件的路径
启动进程(configsvr3类似)
mongod -f config2.conf
mongod -f config3.conf

等待选主成功。
./mongo --host 127.0.0.1 --port 21001
执行命令rs.status(),查看是否已存在主节点Primary。

shardsvr副本集搭建(此处演示shardsvr1 shardsvr2,shardsvr3与shardsvr1搭建类似)

  1. 准备单节点配置文件和目录,以单节点方式启动进程。
net:
  bindIp: 0.0.0.0
  port: 27001
  unixDomainSocket: {enabled: false}
processManagement: {fork: true, pidFilePath: /home/mongodb/shard11/log/shard11.pid}
storage:
  dbPath: /home/mongodb/shard11/data/
  directoryPerDB: true
  engine: wiredTiger
  wiredTiger:
    collectionConfig: {blockCompressor: snappy}
    engineConfig: {directoryForIndexes: true, journalCompressor: snappy}
    indexConfig: {prefixCompression: true}
systemLog: {destination: file, logAppend: true, logRotate: reopen, path: /home/mongodb/shard11/log/shard11.log}

mongod -f shard11.conf

  1. 连接单节点,修改副本集配置信息
连接命令 /mongo --host 127.0.0.1 --port 27001
执行如下命令,修改副本集配置信息。
var cf=db.getSiblingDB('local').system.replset.findOne();
cf['members'][0]['host']='127.0.0.1:27001';
cf['members'][1]['host']='127.0.0.1:27002';
cf['members'][2]['host']='127.0.0.1:27003';
cf['members'][0]['hidden']=false;
cf['members'][1]['hidden']=false;
cf['members'][2]['hidden']=false;
cf['members'][0]['priority']=1;
cf['members'][1]['priority']=1;
cf['members'][2]['priority']=1;
db.getSiblingDB('local').system.replset.remove({});
db.getSiblingDB('local').system.replset.insert(cf)

db.getSiblingDB('admin').dropAllUsers();
db.getSiblingDB('admin').dropAllRoles();

var vs = db.getSiblingDB('admin').system.version.find();
while (vs.hasNext()) {
var curr = vs.next();
if (curr.hasOwnProperty('configsvrConnectionString')) {
db.getSiblingDB('admin').system.version.update({'_id' : curr._id}, {$set: {'configsvrConnectionString': 'config/127.0.0.1:21001,127.0.0.1:21002,127.0.0.1:21003'}});
}
}
执行如下命令,关闭单节点进程。
db.getSiblingDB('admin').shutdownServer();
  1. 搭建shardsvr1副本集。
    准备副本集配置文件和目录,将shardsvr1节点的dbPath文件拷贝到其他两个节点目录下。
cp -aR /home/mongodb/shard11/data /home/mongodb/shard12/data
cp -aR /home/mongodb/shard11/data /home/mongodb/shard13/data

修改shardsvr1-1节点配置文件,增加副本集配置属性。

net:
  bindIp: 0.0.0.0
  port: 27001
  unixDomainSocket: {enabled: false}
processManagement: {fork: true, pidFilePath: /home/mongodb/shard11/log/shard11.pid}
replication: {replSetName: shard_1}
sharding: {archiveMovedChunks: false, clusterRole: shardsvr}
storage:
  dbPath: /home/mongodb/shard11/data/
  directoryPerDB: true
  engine: wiredTiger
  wiredTiger:
    collectionConfig: {blockCompressor: snappy}
    engineConfig: {directoryForIndexes: true, journalCompressor: snappy}
    indexConfig: {prefixCompression: true}
systemLog: {destination: file, logAppend: true, logRotate: reopen, path: /home/mongodb/shard11/log/shard11.log}

启动进程
mongod -f shard11.conf
复制shardsvr1-1节点配置文件为shard12.conf、shard13.conf,修改shardsvr1-2、shardsvr1-3节点文件
修改端口,数据和日志的存储路径
启动进程
mongod -f shard12.conf
mongod -f shard13.conf

等待选主成功。
./mongo --host 127.0.0.1 --port 27001
执行命令rs.status(),查看是否已存在主节点Primary。

shardsvr2、shardsvr3 步骤省略,参考shardsvr1搭建步骤

mongos 节点搭建

准备mongos节点的配置文件和目录。
/home/mongodb/mongos/log
配置文件

net:
  bindIp: 0.0.0.0
  port: 20000
  unixDomainSocket: {enabled: false}
processManagement: {fork: true, pidFilePath: /home/mongodb/mongos/log/mongos.pid}
sharding: {configDB: 'config/127.0.0.1:21001,127.0.0.1:21002,127.0.0.1:21003'}
systemLog: {destination: file, logAppend: true, logRotate: reopen, path: /home/mongodb/mongos/log/mongos.log}

启动mongo节点
mongos -f mongos.conf

mongodb shard节点和config节点的数据恢复

  1. 将实例的备份文件下载到服务器上,例:configsvr的数据一个,三个分片的数据
  2. mongodb服务全部停止,config1上面的原本数据删除,将备份文件拷贝进去,测试启动config1是否成功
  3. 将config1 拷贝到副本集里面,启动副本集,正常启动后,依照上述方法将分片数据替换后启动

mongo安全

对于搭建好的mongodb副本集加分片集群,为了安全,启动安全认证,使用账号密码登陆

默认的mongodb是不设置认证的。只要ip和端口正确就能连接,这样是不安全的。mongodb官网上也说,为了能保障mongodb的安全可以做以下几个步骤:

使用新的端口,默认的27017端口如果一旦知道了ip就能连接上,不太安全
设置mongodb的网络环境,最好将mongodb部署到公司服务器内网,这样外网是访问不到的。公司内部访问使用vpn等
开启安全认证。认证要同时设置服务器之间的内部认证方式,同时要设置客户端连接到集群的账号密码认证方式

环境以上述环境为例:
对副本集执行访问控制需要配置两个方面:

副本集和共享集群的各个节点成员之间使用内部身份验证,可以使用密钥文件或x.509证书。密钥文件比较简单,本文介绍的也是使用密钥文件,官方推荐如果是测试环境可以使用密钥文件,但是正是环境,官方推荐x.509证书。原理就是,集群中每一个实例彼此连接的时候都检验彼此使用的证书的内容是否相同。只有证书相同的实例彼此才可以访问
使用客户端连接到mongodb集群时,开启访问授权。对于集群外部的访问。如通过可视化客户端,或者通过代码连接的时候,需要开启授权。

下面开始说明:

1、生成密钥文件

1.1 在keyfile身份验证中,副本集中的每个mongod实例都使用keyfile的内容作为共享密码,只有具有正确密钥文件的mongod或者mongos实例可以连接到副本集。密钥文件的内容必须在6到1024个字符之间,并且在unix/linux系统中文件所有者必须有对文件至少有读的权限。

1.2可以用任何方式生成密钥文件例如:

第一条命令是生成密钥文件,第二条命令是使用chmod更改文件权限,为文件所有者提供读权限
openssl rand -base64 756 > /usr/local/mongodb/conf/KeyFile.file
chmod 400 /usr/local/mongodb/conf/KeyFile.file

2、将密钥复制到集群中的每台机器的指定位置

2.1一定要保证密钥文件一致。文件位置随便。但是为了方便查找,建议每台机器都放到一个固定的位置

3、预先创建好一个管理员账号和密码然后将集群中的所有mongod和mongos全部关闭

账号可以在集群认开启认证以后添加。但是那时候添加比较谨慎。只能添加一次,如果忘记了就无法再连接到集群。建议在没开启集群认证的时候先添加好管理员用户名和密码然后再开启认证再重启

连接任意一台机器的mongos

mongo --port 20000

添加用户

use admin;
db.createUser(
    {
        user:"root",
        pwd:"cFCD37datarrubb99211ca",
        roles:[{role:"root",db:"admin"}]
    }
)

然后依次连接到每一台机器上执行。

killall mongod
killall mongos

然后删除每个mongod实例存储数据存储路径下面的mongod.lock(如果后面启动不报错可以不处理);也可以连接到每台的端口
执行命令db.getSiblingDB('admin').shutdownServer();

3.1可以发现。集群多少有的节点都关闭了。没开启认证的集群如果开启认证需要集群宕机几分钟。当然也有热启动的方式,官方文档中有介绍
说明:可以先开启认证重启后再添加用户。但是只能在admin库添加一次,所以如果忘记了,或者权限分配不恰当就无法再更改,所以建议先添加用户再开启认证重启,并且集群不建议在每个单节点添加用户,并且建议单节点关闭初始添加账号的权限,详情见enableLocalhostAuthBypass)

4、使用访问控制强制重新启动复制集的每个成员

4.1 依次在每台机器上的mongod(注意是所有的mongod不是mongos)的配置文件中加入下面一段配置。
如config server,shard1,shard2,shard3都加入下面的配置文件

security:
  keyFile: /usr/local/mongodb/conf/KeyFile.file
  authorization: enabled

4.2 依次在每台机器上的mongos配置文件中加入下面一段配置。

security:
  keyFile: /usr/local/mongodb/conf/KeyFile.file

解释:
mongos比mongod少了authorization:enabled的配置。原因是,副本集加分片的安全认证需要配置两方面的,副本集各个节点之间使用内部身份验证,用于内部各个mongo实例的通信,只有相同keyfile才能相互访问。所以都要开启keyFile: /usr/local/mongodb/conf/KeyFile.file
然而对于所有的mongod,才是真正的保存数据的分片。mongos只做路由,不保存数据。所以所有的mongod开启访问数据的授权authorization:enabled。这样用户只有账号密码正确才能访问到数据

重启每个mongo示例

5、连接mongodb集群

如果用mongo sell脚本连接

mongo --port 20000
use admin
db.auth("your account","your password")

如果返回1表示连接成功,然后你就可以访问自己的数据库