
使用docker-compose 搭建mongo多副本

MongoDB副本集(Replica Set)是有自动故障恢复功能的主从集群,有一个Primary节点和一个或者多个Secondary节点组成。副本集没有固定的主节点,当主节点发生故障时,整个集群会选举一个主节点 为系统提供服务以保证系统的高可用。
副本集拥有三种节点:主节点、从节点、仲裁节点
1.主节点 负责处理客户端请求,读、写数据。
2.从节点 从主节点中复制数据,并可以接收读请求。在主节点故障时可通过投票选举出新的主节点。
3.仲裁节点 不持有数据副本,只参与投票过程,决定哪个节点成为主节点。
本文使用mongo5.0.8作为样例
1.docker-compose.yaml
这里先直接给出单个mongo 的docker-compose.yaml
version: '3.0'
services:
mongo1:
hostname: mongo1
image: mongo:5.0.8
restart: unless-stopped
container_name: mongodb1
command: --replSet app --bind_ip_all --keyFile /data/mongodb/keyFile
environment:
TZ: 'Asia/Shanghai'
MONGO_INITDB_ROOT_USERNAME: 'admin'
MONGO_INITDB_ROOT_PASSWORD: 'password'
ports:
- 27020:27017
volumes:
- ./data1:/data/db
- ./keyFile:/data/mongodb/keyFile
networks:
- bridge_network
mongo2:
hostname: mongo2
image: mongo:5.0.8
restart: unless-stopped
container_name: mongodb2
command: --replSet app --bind_ip_all --keyFile /data/mongodb/keyFile
environment:
TZ: 'Asia/Shanghai'
MONGO_INITDB_ROOT_USERNAME: 'admin'
MONGO_INITDB_ROOT_PASSWORD: 'password'
ports:
- 27018:27017
volumes:
- ./data2:/data/db
- ./keyFile:/data/mongodb/keyFile
networks:
- bridge_network
mongo3:
hostname: mongo3
image: mongo:5.0.8
restart: unless-stopped
container_name: mongodb3
command: --replSet app --bind_ip_all --keyFile /data/mongodb/keyFile
environment:
TZ: 'Asia/Shanghai'
MONGO_INITDB_ROOT_USERNAME: 'admin'
MONGO_INITDB_ROOT_PASSWORD: 'password'
ports:
- 27019:27017
volumes:
- ./data3:/data/db
- ./keyFile:/data/mongodb/keyFile
networks:
- bridge_network
networks:
bridge_network:
driver: bridge
这里可以选择在一台机器上启动3个容器,也可以在不同的机器上启动。本例用上述文件分别在IP为10.0.1.11(主)、10.0.1.31(从)、10.0.1.32(从)三台机器上启动。
在10.0.1.11执行docker compose up -d mongo1
在10.0.1.31执行docker compose up -d mongo2
在10.0.1.32执行docker compose up -d mongo3
这里 docker compose up -d
后的 mongo1、mongo2、mongo3是指只启动yaml文件中的一个服务。如果将yaml文件拆分成不同的文件分别在对应的机器里启动就不需要在命令后面加服务名称了。
需要注意:
组成副本的每一台机器的keyFile文件必须一样!!!mongodb实例只有拥有正确的keyfile才可以加入副本集。keyFile文件的具体操作方法可以参考https://blog.csdn.net/qq_40315096/article/details/124814101
2.容器启动后
在容器启动后,我们使用docker exec 命令进入作为主节点的容器,进入mongo shell
mongo -u admin --authenticationDatabase admin
初始化mongo 副本集
rs.initiate({
_id: "app",
members: [
{ _id: 0, host: "10.0.1.11:27020" },
{ _id: 1, host: "10.0.1.31:27018" },
{ _id: 2, host: "10.0.1.32:27019" }
]
})
注意,当初我在初始化时,误以为可以直接使用rs.initiate()无参的初始化方法,然后再依次使用rs.add(‘ip:port’)将剩余的节点加入副本集。但是这是错误的,因为在初始化时,如果使用无参的初始化方法,mongo会直接获取宿主机(这时对于mongo来说宿主机是docker容器)的hostname+端口组成mongo的host并写入配置,但是由于mongo的宿主机其实是容器,所以会获取到容器的hostname以及mongo在容器里的端口。
显然这个当多个不同机器的mongo副本相互通信时,这个host是无效的。
所以在初始化时最好直接指定好具体的机器。
当初始化完成以后可以使用rs.status()
查看副本集状态
截取部分内容
"members" : [
{
"_id" : 0,
"name" : "10.0.1.11:27020",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 2299,
"optime" : {
"ts" : Timestamp(1717560117, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2024-06-05T04:01:57Z"),
"lastAppliedWallTime" : ISODate("2024-06-05T04:01:57.808Z"),
"lastDurableWallTime" : ISODate("2024-06-05T04:01:57.808Z"),
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "",
"electionTime" : Timestamp(1717559887, 1),
"electionDate" : ISODate("2024-06-05T03:58:07Z"),
"configVersion" : 1,
"configTerm" : 1,
"self" : true,
"lastHeartbeatMessage" : ""
},
{
"_id" : 1,
"name" : "10.0.1.31:27018",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 249,
"optime" : {
"ts" : Timestamp(1717560117, 1),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1717560117, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2024-06-05T04:01:57Z"),
"optimeDurableDate" : ISODate("2024-06-05T04:01:57Z"),
"lastAppliedWallTime" : ISODate("2024-06-05T04:01:57.808Z"),
"lastDurableWallTime" : ISODate("2024-06-05T04:01:57.808Z"),
"lastHeartbeat" : ISODate("2024-06-05T04:02:05.988Z"),
"lastHeartbeatRecv" : ISODate("2024-06-05T04:02:05.581Z"),
"pingMs" : NumberLong(1),
"lastHeartbeatMessage" : "",
"syncSourceHost" : "10.0.1.11:27020",
"syncSourceId" : 0,
"infoMessage" : "",
"configVersion" : 1,
"configTerm" : 1
},
{
"_id" : 2,
"name" : "10.0.1.32:27019",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 249,
"optime" : {
"ts" : Timestamp(1717560117, 1),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1717560117, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2024-06-05T04:01:57Z"),
"optimeDurableDate" : ISODate("2024-06-05T04:01:57Z"),
"lastAppliedWallTime" : ISODate("2024-06-05T04:01:57.808Z"),
"lastDurableWallTime" : ISODate("2024-06-05T04:01:57.808Z"),
"lastHeartbeat" : ISODate("2024-06-05T04:02:05.988Z"),
"lastHeartbeatRecv" : ISODate("2024-06-05T04:02:05.629Z"),
"pingMs" : NumberLong(1),
"lastHeartbeatMessage" : "",
"syncSourceHost" : "10.0.1.11:27020",
"syncSourceId" : 0,
"infoMessage" : "",
"configVersion" : 1,
"configTerm" : 1
}
],
这里可以看到执行初始化的机器为主节点,剩下的为从节点
关于stateStr字段的各个含义:
STARTUP:成员正在启动。
PRIMARY:成员是主节点。
SECONDARY:成员是次节点。
RECOVERING:成员正在恢复。
ARBITER:成员是仲裁节点。
DOWN:成员无法与大多数节点通信。
UNKNOWN:成员的状态未知。
ROLLBACK:成员正在进行回滚操作。
REMOVED:成员已经被副本集移除。
最后如果有后续需要加入的新节点可以使用 rs.add(‘ip:port’)加入。如果需要加入仲裁节点(不推荐)
可以使用rs.add(‘ip:port’,true)加入
关于节点的数量
mongodb官方说当节点超过2个时,推荐副本集成员为奇数个成员,而不使用仲裁节点。所以在实际运用中尽可能的保证奇数个节点而不使用仲裁节点。如果迫不得已正常主从节点总数为偶数,这时仲裁节点就可以加入副本集(仲裁节点不参与存储等业务,只参与投票,所以仲裁节点对于性能的要求很小,加一个仲裁节点的难度不会很大),这样拥有投票权的节点总数又为奇数了。
具体为什么需要奇数个节点,这需要投票选举的算法以及实际部署的情况来解释(脑裂问题)。




更多推荐
所有评论(0)