Home MongoDB集群日记
Post
Cancel

MongoDB集群日记

MongoDB集群的几种方式

1. 副本集(replica set)

副本集集群中,有三种角色:

  • primary 主节点
  • secondary(slave) 从节点
  • arbiter 仲裁节点(可选)

在此集群模式下,数据在primary主节点与secondary从节点之间同步,arbiter节点不做数据保存,只做选举投票。 一个副本集中,支持一个primary节点,多个secondary节点,多个arbiter节点。 与主从模式不同的是,副本集模式下的primary节点是动态的。当主节点不可达时,副本集会自动触发选举,推选出新的primary节点。 一般情况下,一个副本集中只有primary节点允许读写,从节点既不可写也不可读。在调用rs.secondaryOk()db.getMongo().setSecondaryOk()之后,允许从secondary从节点读取数据。

每次通过shell连接从节点时,都需要先设置rs.secondaryOk()。否则读取数据会报错。

我们可以通过URL: mongo://[username:password@]ip1:port1,ip2:port2,ip3:port3/?replicaSet={replica set name}连接到一个副本集。

2. 切片(sharding)

切片集群方式是由多个切片(副本集)+ 多个mongos + configServer副本集组成的。 mongos是入口,configServer中配置了数据的路由规则,数据进来之后,被分成多个片,存入不同的切片中。

切片模式还未实践,后续在补充。

一步步搭建副本集

我是通过docker-compose的方式在一台机器上起了两个副本节点一个仲裁节点。

1. 创建配置文件

提前创建一些配置文件以及数据库数据备份等,具体的文件目录如下:

  • data/db. 数据库数据挂载到本地,防止数据丢失。
  • log
    • mongod.log. 日志挂载到本地。
  • config
    • mongod.conf. mongoDB的配置文件,在容器起来时,指定conf文件。
    • key_file.txt. 节点之间通信必须要求安全校验,keyFile就是支持的其中一种。

mongod.conf的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
systemLog:
   #MongoDB发送所有日志输出的目标指定为文件 
   destination: file
   #诊断日志记录信息的日志文件的路径 
   path: "/var/log/mongod.log"
   #当mongos或mongod实例重新启动时,新条目附加到末尾。
   logAppend: true
storage:
   journal:
      #启用持久性日志以确保数据文件保持有效和可恢复。 
      enabled: true
net:
   bindIpAll: true 
   port: 15001
replication:
   replSetName: rs1
security:
  keyFile: "/etc/config/key_file.txt"

容器起来加载文件时,可能会报错。它对挂载的文件权限、属性有些特殊的限制:

  • mongod.log的文件属性。需求chown 999 mongod.log
  • key_file.txt的文件属性。需求chown 999 key_file.txt
  • key_file.txt的读取权限。要求chmod 600 key_file.txt

    mongod默认是不开启权限的。但是,在副本集模式下,开启了keyFile后,默认是开启权限认证的。

2. 创建docker-compose.yml

接着编写docker-compose.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
version: "3"
services:
        mongo1:
                image: mongo:4.4.6
                container_name: mongo_replica_1
                environment:
                        - MONGO_INITDB_ROOT_USERNAME=root
                        - MONGO_INITDB_ROOT_PASSWORD=123456
                restart: always
                networks:
                        - mongo_db
                volumes:
                        - "./mongo/replica1/config:/etc/config"
                        - "./mongo/replica1/data/db:/data/db"
                        - "./mongo/replica1/log:/var/log"
                        - "./mongo/replica1/mongorc.js:/etc/mongorc.js"
                ports:
                        - "15001:15001"
                command: --config /etc/config/mongod.conf
        mongo2:
                image: mongo:4.4.6
                container_name: mongo_replica_2
                environment:
                        - MONGO_INITDB_ROOT_USERNAME=root
                        - MONGO_INITDB_ROOT_PASSWORD=123456
                restart: always
                networks:
                        - mongo_db
                volumes:
                        - "./mongo/replica2/config:/etc/config"
                        - "./mongo/replica2/data/db:/data/db"
                        - "./mongo/replica2/log:/var/log"
                ports:
                        - "15002:15002"
                command: --config /etc/config/mongod.conf
        mongo3:
                image: mongo:4.4.6
                container_name: mongo_replica_3
                environment:
                        - MONGO_INITDB_ROOT_USERNAME=root
                        - MONGO_INITDB_ROOT_PASSWORD=123456
                restart: always
                networks:
                        - mongo_db
                volumes:
                        - "./mongo/replica3/config:/etc/config"
                        - "./mongo/replica3/data/db:/data/db"
                        - "./mongo/replica3/log:/var/log"
                ports:
                        - "15003:15003"
                command: --config /etc/config/mongod.conf

networks:
        mongo_db:

将第一步中建的文件,挂载到容器中去。同时,指定mongo启动时的config文件。

但是,我们在启动时,会发现报错了,提示add user failed啥的。其实就是说,我们通过环境变量创建的root角色添加失败了。暂不清楚这是不是bug,通过docker-compose起来时,如果配置了副本集,会导致预设的角色创建失败。 因此,目前而言,正确的步骤是:

  • 将mongod.conf中的replication那一段注释掉
  • 启动docker-compose up -d
  • 等待容器起来后,用root登进去,确定root账号创建成功
  • 停掉docker-compose down
  • 在将replication那一段打开
  • 重新启动docker-compose up -d
  • 进入其中一个容器,使用mongo shell命令登入:
    1
    2
    3
    4
    
      mongo --port 15001 -u root -p 123456
      rs.initiate() //初始化副本集
      rs.add("mongo2:15002") //向副本集中添加节点
      rs.addArb("mongo3:15003") //向副本集中添加仲裁节点
    
  • 通过rs.status()可以查看当前的副本集状态,通过rs.conf()查看副本集配置。 需要注意的是,注意rs.conf()中自己绑定的host,如果host是容器id的话,需要手动修改成容器名或者其他不变的域名、ip等。shell进入mongodb后,具体的命令是:
    1
    2
    3
    
      cc=rs.conf()
      cc.members[0].host="mongo1:15001"
      rs.reconfig(cc, {force: true})
    
  • 通过rs.stepDown(millseconds)中断心跳,来模拟主节点断开心跳。此时会触发选举,当前的主节点会自动从primary节点切换到secondary节点。
  • 进入secondary节点。默认是不能读取数据的,通过rs.secondaryOK()后,可以从secondary节点读取节点。

如果我们将节点分散到不同的机器上部署的话,会遇到一个问题。因为容器化的关系,在rs.conf()中自己的host不能设置成宿主ip,也不能设置成127.0.0.1这样的,它会同步该信息给到其他的节点。我们要选择一个容器本身能识别、其他节点也能识别的host。有一种方法:host设置成service name, 这样容器自身能识别。然后再宿主机的/etc/hosts中增加宿主ip与service name的路由对应,这样,其他的节点也能识别了。

mongo-express

mongo-express是mongo的web管理页面。我们也通过docker-compose来启动它。 express.yml内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
services:
        mongo-express:
                image: mongo-express:latest
                container_name: mongo_exp
                restart: always
                environment:
                        - ME_CONFIG_MONGODB_URL=mongodb://root:123456@mongo1:15001,mongo2:15002,mongo3:15003/?replicaSet=rs1
                        - ME_CONFIG_MONGODB_ADMINUSERNAME=root
                        - ME_CONFIG_MONGODB_ADMINPASSWORD=123456
                        - ME_CONFIG_BASICAUTH_USERNAME=root
                        - ME_CONFIG_BASICAUTH_PASSWORD=123456
                ports:
                        - "18081:8081"
                networks:
                        - test_mongo_db

networks:
        test_mongo_db:
                external: true

如果将每个节点分别部署到不同的机器上,host就不能取service_name,可以设置docker compose network_mode: “host”来使用宿主机ip.记得打开防火墙端口。

This post is licensed under CC BY 4.0 by the author.