MongoDB搭建复制集集群(Docker版)

复制集注意事项

关于复制集:

  • 复制集为 MongoDB 提供了数据可靠性,当某个节点挂掉,可以重新选举出主节点;
  • 复制集为 MongoDB 提供了数据安全性,当节点宕机后,备份数据保证数据不丢失;
  • 复制集为 MOngoDB 提供了高性能,可通过配置主从读写分离提高服务性能;

关于硬件:

  • 因为正常的复制集节点都有可能成为主节点,它们的地位是一样的,因此硬件配置上必须一致;
  • 为了保证节点不会同时宕机,各节点使用的硬件必须具有独立性;
  • 此处用的 Docker 在同一个虚拟机上模拟三个节点;

关于软件:

  • 复制集各节点软件版本必须一致,以避免出现不可预知的问题;
  • 增加节点不会增加系统写性能;
  • 此处用的 MongoDB 6.0.5 版本;

环境准备

  • Docker 安装 MongoDB 并配置好环境变量;
  • 确保有 10GB 以上的硬盘空间;
  • 默认已安装 Docker,没安装的话请参考我的 Docker 教程;
  • 宿主机器为 CentOS7;

一主两从架构图:
image.png

安装步骤

(1)准备配置文件
复制集的每个 mongod 进程应该位于不同的服务器。我们现在在一台机器上运行 3 个进程,因此要为它们各自配置:

  • 不同的端口(28017/28018/28019)
  • 不同的数据目录
mkdir -p /data/db{1,2,3}

image.png

  • 不同日志文件路径(例如:/data/db1/logs)
mkdir -p /data/db{1,2,3}/logs

创建配置文件/data/db1/mongod.conf,内容如下:

systemLog:
  destination: file
  path: /data/logs/mongod.log # log path
  logAppend: true
storage:   
  dbPath: /data/db # data directory      
net:
  bindIp: 0.0.0.0
  port: 27017 # port
replication:
  replSetName: rs0 # 复制集名称
# processManagement: # 设置了该项会导致docker exec -it mongodb1 bash 进入容器后马上自动退出
#   fork: true

将该配置文件复制都 db2、db3 对应的目录下,注意其中配置的是容器中的地址,而不是宿主机对应的地址,且必须是 yaml 格式。
image.png

(2)启动 docker
启动第 1 个 mongod:

docker run --name mongodb1 -d --restart=always -v /data/db1:/data/db -v /data/db1/logs:/data/logs -v /data/db1/mongod.conf:/data/conf/mongod.conf -p 28017:27017 mongo:6.0.5 --config /data/conf/mongod.conf

–name:指定容器名称
-d:后台运行
–restart:设置容器的重启策略,always 表示重启宿主机自动运行 docker
-v:将宿主机文件挂载到容器中,注意格式为:“宿主机路径 : 容器路径”
-p:绑定宿主机和容器的端口,注意格式为:“宿主机端口 : 容器端口”
mongo:6.0.5:指定镜像名和版本号
–config:指定启动的配置文件,容器中的路径

启动第 2 个 mongod:

docker run --name mongodb2 -d --restart=always -v /data/db2:/data/db -v /data/db2/logs:/data/logs -v /data/db2/mongod.conf:/data/conf/mongod.conf -p 28018:27017 mongo:6.0.5 --config /data/conf/mongod.conf

启动第 3 个 mongod:

docker run --name mongodb3 -d --restart=always -v /data/db3:/data/db -v /data/db3/logs:/data/logs -v /data/db3/mongod.conf:/data/conf/mongod.conf -p 28019:27017 mongo:6.0.5 --config /data/conf/mongod.conf

查看 docker 进程状态及网络端口:
image.png
注意 docker 启动,宿主机的防火墙 firewalld 必须开启,否则会报如下错误:
image.png
通过客户端工具访问 mongodb:
image.png
进入 docker 容器操作 mongodb:

# 宿主机进入容器
docker exec -it mongodb1 bash
# 进入容器mongodb的命令行模式
mongosh
# 查看mongodb数据库
show dbs

image.png
至此,三个 mongod 节点安装完成。

配置复制集

复制集通过 mongosh 的 rs.initiate() 进行初始化,初始化后各个成员间开始发送心跳消息,并发起 Priamry 选举操作,获得大多数成员投票支持的节点,会成为 Primary,其余节点成为 Secondary。

进入 mongdb1 节点后执行 mongosh 命令后:

# mongosh --port 28017 
# 初始化复制集
> rs.initiate({
    _id: "rs0",
    members: [{
        _id: 0,
        host: "192.168.10.101:28017"
    },{
        _id: 1,
        host: "192.168.10.101:28018"
    },{
        _id: 2,
        host: "192.168.10.101:28019"
    }]
})

提示{ok:1}表示初始化成功:
image.png

验证主从节点读写操作:

  • MongoDB 主节点进行写入
# mongosh --port 28017
rs0 [direct: primary] test> db.user.insertMany([{name:"firechou"},{name:"monkey"}])

image.png

  • 切换到从节点写入,抛出异常 MongoBulkWriteError: not primary
# mongosh --port 28018  
rs0 [direct: secondary] test> db.user.insertMany([{name:"firechou"},{name:"monkey"}])

image.png

  • MongoDB 从节点进行读
# mongosh --port 28018
# 指定从节点可读
rs0:SECONDARY> rs.secondaryOk() # 需要执行该命令,否则会报错
rs0:SECONDARY> db.user.find()

image.png
或者执行如下命令开启从节点读:

db.getMongo().setReadPref("secondary")

image.png

  • 模拟重新选举 Master

停掉 mongodb1 服务:
image.png
查看 mongodb2 服务已被选举成了 Primary 节点:
image.png
启动 mongodb1 后再次执行写入操作,提示非主节点不能写入:
image.png

复制集状态查询

  • 查看复制集整体状态:
rs.status()

可查看各成员当前状态,包括是否健康,是否在全量同步,心跳信息,增量同步信息,选举信息,上一次的心跳时间等。
image.png
说明:

members:数组,一列体现了所有复制集成员的状态,主要如下:
health:成员是否健康,通过心跳进行检测。
state/stateStr:成员的状态,PRIMARY 表示主节点,而 SECONDARY 则表示备节点,如果节点出现故障,则可能出现一些其他的状态,例如 RECOVERY。
uptime:成员的启动时间。
optime/optimeDate:成员同步最后一条 oplog 的时间。
optimeDurable/optimeDurableDate:成员同步最后一条 oplog 持久化的时间。
pingMs:成员与当前节点的 ping 时延。
syncingTo:成员的同步来源。

  • 查看当前节点角色:
db.isMaster()

除了当前节点角色信息,是一个更精简化的信息,也返回整个复制集的成员列表,真正的 Primary 是谁,协议相关的配置信息等,Driver 在首次连接复制集时会发送该命令。

Mongos 复制集常用命令

**命令 ** 描述
rs.add() 为复制集新增节点
rs.addArb() 为复制集新增一个 arbiter
rs.conf() 返回复制集配置信息
rs.freeze() 防止当前节点在一段时间内选举成为主节点
rs.help() 返回 replica set 的命令帮助
rs.initiate() 初始化一个新的复制集
rs.printReplicationInfo() 以主节点的视角返回复制的状态报告
rs.printSecondaryReplicationInfo() 以从节点的视角返回复制状态报告
rs.reconfig() 通过重新应用复制集配置来为复制集更新配置
rs.remove() 从复制集中移除一个节点
rs.secondaryOk() 为当前的连接设置从节点可读
db.getMongo().setReadPref(“secondary”) 为当前的连接设置从节点可读,推荐使用
rs.status() 返回复制集状态信息
rs.stepDown() 让当前的 primary 变为从节点并触发
electionrs.syncFrom() 设置复制集节点从哪个节点处同步数据,将会覆盖默认选取逻辑