四时宝库

程序员的知识宝库

docker-compose搭建MongoDB副本集分片 多机版

想从网络上查阅一些docker-compose搭建mongo分片的教程不少,可是发现完整的太少了,多机版的更是少之又少,所以出一片文章供大家参考。

MongoDB副本集和分片 各个服务

mongos层:请求的入口,是router的角色,相当于监听,负责将请求分发到对应的存储数据的shard上,多副本冗余

config server层:记录了mongos中使用到的元数据,自动向mongos同步最新的集群配置,多副本冗余

shard主节点层:将数据分片,数据库拆分,并将其分散在不同的机器上,原理是将整个数据集合切块块分散到各个shard中,每个shard只负责总数据的一部分,通过一个均衡器来对各个shard均衡,多副本冗余

shard副本层:是shard的备份,多副本冗余

shard仲裁层:用于仲裁,不存储数据,使用最小的资源,需要基数个仲裁角色,且不能放在同一设备上

开始准备

为了更清晰方便的操作,这里提前准备好三台机器

192.168.0.121 虚拟机、192.168.0.122 虚拟机、192.168.0.123 虚拟机

三台机器的操作目录均为:/usr/local/docker/mongosvr 下

在三台机器上分别创建目录文件夹

mkdir data mongos shard1 shard2 shard3

生成keyFile

  • MongoDB使用keyfile认证,副本集中的每个mongod实例使用keyfile内容作为认证其他成员的共享密码。mongod实例只有拥有正确的keyfile才可以加入副本集。
  • keyFile的内容必须是6到1024个字符的长度,且副本集所有成员的keyFile内容必须相同。
  • 有一点要注意是的:在UNIX系统中,keyFile必须没有组权限或完全权限(也就是权限要设置成X00的形式)。Windows系统中,keyFile权限没有被检查。
  • 可以使用任意方法生成keyFile。例如,如下操作使用openssl生成复杂的随机的1024个字符串。然后使用chmod修改文件权限,只给文件拥有者提供读权限。
# 400权限是要保证安全性,否则mongod启动会报错
# 存放目录随意,只要对应yaml文件中的地址即可。
openssl rand -base64 756 > key.file
chmod 400 key.file

编写docker-compose.yaml文件,如下

version: '3.9'
networks: 
  mongo-network:
    external: false
services:
  # 配置服务器configsvr
  rs_config_server:
    image: mongo:latest
    networks:
      - mongo-network
    container_name: rs_config_server
    restart: always
    ports: 
     - 27019:27019
    command: --configsvr --replSet "rs_config_server" --bind_ip_all
    volumes:
      - ${PWD}/data/db:/data/db
      - ${PWD}/data/configdb:/data/configdb
      - ${PWD}/key.file:/data/mongodb/key.file
      
  # shard分片1
  rs_shard_server_01:
    image: mongo:latest
    networks:
      - mongo-network
    container_name: rs_shard_server_01
    restart: always
    ports: 
     - 27118:27018
    command: --shardsvr --replSet "rs_shard_server_01" --bind_ip_all
    volumes:
      - ${PWD}/shard1/db:/data/db
      - ${PWD}/shard1/configdb:/data/configdb
      - ${PWD}/key.file:/data/mongodb/key.file

  # shard分片2
  rs_shard_server_02:
    image: mongo:latest
    networks:
      - mongo-network
    container_name: rs_shard_server_02
    restart: always
    ports:
     - 27218:27018
    command: --shardsvr --replSet "rs_shard_server_02" --bind_ip_all
    volumes:
      - ${PWD}/shard2/db:/data/db
      - ${PWD}/shard2/configdb:/data/configdb
      - ${PWD}/key.file:/data/mongodb/key.file

  # shard分片3
  rs_shard_server_03:
    image: mongo:latest
    networks:
      - mongo-network
    container_name: rs_shard_server_03
    restart: always
    ports:
     - 27318:27018
    command: --shardsvr --replSet "rs_shard_server_03" --bind_ip_all
    volumes:
      - ${PWD}/shard3/db:/data/db
      - ${PWD}/shard3/configdb:/data/configdb
      - ${PWD}/key.file:/data/mongodb/key.file

  # 配置路由 mongos
  rs_mongos_server:
    image: mongo:latest
    networks:
      - mongo-network
    container_name: rs_mongos_server
    restart: always
    ports:
      - 27017:27017
    # 这里的 rs_config_server 是 配置的 --replSet "rs_config_server"
    # 将三台机器的configsvr都写在这个位置
    command: mongos --configdb rs-config-server/192.168.0.121:27019,192.168.0.122:27019,192.168.0.123:27019 --bind_ip_all
    volumes:
      - ${PWD}/mongos/db:/data/db
      - ${PWD}/mongos/configdb:/data/configdb
      - ${PWD}/key.file:/data/mongodb/key.file

目录目录如下图所示:

以上操作在三台机器上均操作一遍,保持目录结构一模一样,无需改动任何地方。

启动

三台机器均需要启动,启动方式如下:

# 目录结构依旧在/usr/local/docker/mongosvr目录下
cd /usr/local/docker/mongosvr

# 启动指令  docker-compose -f docker-compose.yaml up -d  (这里yaml文件名定义成了docker-compose.yaml,所以启动时无需指定-f)
docker-compose up -d 

# 停止指令  docker-compose -f docker-compose.yaml  (这里yaml文件名定义成了docker-compose.yaml,所以启动时无需指定-f)
# docker-compose down

启动方式如下所示:

都启动成功后,我们现在来进行初始化配置,进行副本集、分片、路由的关联和初始化。

首先我们选择任意一台机器,我这里选择了192.168.0.121第一台机器进行操作(只在一台机器操作,不可三台机器都操作

初始化配置服务器

# 进入rs_config_server容器
docker exec -it rs_config_server bash

# 登录mongo,这里的端口号不用解释了吧,docker-compose.yaml文件和使用docker ps 都能查看到
mongo --host 192.168.0.121 --port 27019

# 执行初始化语句,配置 mongo config
rs.initiate({
    _id: "rs_config_server",
    configsvr: true,
    members: [
      { _id : 0, host : "192.168.0.121:27019" },
      { _id : 1, host : "192.168.0.122:27019" },
      { _id : 2, host : "192.168.0.123:27019" },
    ]
});

# 设置完成后,可以使用以下指令查看状态
rs.status()

执行方式如下图所示:

我们可以看到三台config已经出现了变化。

现在开始关联分片 或者 设置仲裁节点

以下标红的地方注意,别写错。执行 rs.initiate 后,可以观察一会,让系统自动选出primary节点。

初始化分片1

# 进入 rs_shard_server_01 容器
docker exec -it rs_shard_server_01 bash

# 登录mongo
mongo --host 192.168.0.121 --port 27118

# 执行初始化语句,配置 mongo 分片1
# 也是可以设置裁决节点的,只需要在任意一个节点后增加arbiterOnly:true即可。 
# 比如:{ _id : 2, host : "192.168.0.123:27118", arbiterOnly:true }
rs.initiate({
    _id: "rs_shard_server_01",
    members: [
      { _id : 0, host : "192.168.0.121:27118" },
      { _id : 1, host : "192.168.0.122:27118" },
      { _id : 2, host : "192.168.0.123:27118" },
    ]
});

# 分片设置完成后,可以使用以下指令查看状态
rs.status()

初始化分片2

# 进入 rs_shard_server_01 容器
docker exec -it rs_shard_server_02 bash

# 登录mongo
mongo --host 192.168.0.121 --port 27218

# 执行初始化语句,配置 mongo 分片2
# 也是可以设置裁决节点的,只需要在任意一个节点后增加arbiterOnly:true即可。 
# 比如:{ _id : 2, host : "192.168.0.123:27218", arbiterOnly:true }
rs.initiate({
    _id: "rs_shard_server_02",
    members: [
      { _id : 0, host : "192.168.0.121:27218" },
      { _id : 1, host : "192.168.0.122:27218" },
      { _id : 2, host : "192.168.0.123:27218" },
    ]
});

# 分片设置完成后,可以使用以下指令查看状态
rs.status()

初始化分片3

# 进入 rs_shard_server_01 容器
docker exec -it rs_shard_server_03 bash

# 登录mongo
mongo --host 192.168.0.121 --port 27318

# 执行初始化语句,配置 mongo 分片3
# 也是可以设置裁决节点的,只需要在任意一个节点后增加arbiterOnly:true即可。 
# 比如:{ _id : 2, host : "192.168.0.123:27318", arbiterOnly:true }
# 指定设置主副节点,使用priority,设置优先级,值大的优先设置主节点
# 比如:{ _id : 0, host : "192.168.0.121:27318", priority:5 },
rs.initiate({
    _id: "rs_shard_server_03",
    members: [
      { _id : 0, host : "192.168.0.121:27318" },
      { _id : 1, host : "192.168.0.122:27318" },
      { _id : 2, host : "192.168.0.123:27318" },
    ]
});

# 分片设置完成后,可以使用以下指令查看状态
rs.status()

我们使用 rs.status()来查看一下 rs_shard_server_01 的状态吧,简单看一下。

{
  "set" : "rs_shard_server_01",
  "date" : ISODate("2021-11-03T05:20:57.821Z"),
  "myState" : 1,
  "term" : NumberLong(1),
  "syncSourceHost" : "",
  "syncSourceId" : -1,
  "heartbeatIntervalMillis" : NumberLong(2000),
  "majorityVoteCount" : 2,
  "writeMajorityCount" : 2,
  "votingMembersCount" : 3,
  "writableVotingMembersCount" : 3,
  "optimes" : {
    "lastCommittedOpTime" : {
      "ts" : Timestamp(1635916848, 1),
      "t" : NumberLong(1)
    },
    "lastCommittedWallTime" : ISODate("2021-11-03T05:20:48.424Z"),
    "readConcernMajorityOpTime" : {
      "ts" : Timestamp(1635916848, 1),
      "t" : NumberLong(1)
    },
    "appliedOpTime" : {
      "ts" : Timestamp(1635916848, 1),
      "t" : NumberLong(1)
    },
    "durableOpTime" : {
      "ts" : Timestamp(1635916848, 1),
      "t" : NumberLong(1)
    },
    "lastAppliedWallTime" : ISODate("2021-11-03T05:20:48.424Z"),
    "lastDurableWallTime" : ISODate("2021-11-03T05:20:48.424Z")
  },
  "lastStableRecoveryTimestamp" : Timestamp(1635916798, 1),
  "electionCandidateMetrics" : {
    "lastElectionReason" : "electionTimeout",
    "lastElectionDate" : ISODate("2021-11-03T05:12:58.325Z"),
    "electionTerm" : NumberLong(1),
    "lastCommittedOpTimeAtElection" : {
      "ts" : Timestamp(0, 0),
      "t" : NumberLong(-1)
    },
    "lastSeenOpTimeAtElection" : {
      "ts" : Timestamp(1635916368, 1),
      "t" : NumberLong(-1)
    },
    "numVotesNeeded" : 2,
    "priorityAtElection" : 1,
    "electionTimeoutMillis" : NumberLong(10000),
    "numCatchUpOps" : NumberLong(0),
    "newTermStartDate" : ISODate("2021-11-03T05:12:58.338Z"),
    "wMajorityWriteAvailabilityDate" : ISODate("2021-11-03T05:12:59.766Z")
  },
  "members" : [
    {
      "_id" : 0,
      "name" : "192.168.0.121:27118",
      "health" : 1,
      "state" : 1,
      "stateStr" : "PRIMARY",
      "uptime" : 1151,
      "optime" : {
        "ts" : Timestamp(1635916848, 1),
        "t" : NumberLong(1)
      },
      "optimeDate" : ISODate("2021-11-03T05:20:48Z"),
      "syncSourceHost" : "",
      "syncSourceId" : -1,
      "infoMessage" : "",
      "electionTime" : Timestamp(1635916378, 1),
      "electionDate" : ISODate("2021-11-03T05:12:58Z"),
      "configVersion" : 1,
      "configTerm" : 1,
      "self" : true,
      "lastHeartbeatMessage" : ""
    },
    {
      "_id" : 1,
      "name" : "192.168.0.122:27118",
      "health" : 1,
      "state" : 2,
      "stateStr" : "SECONDARY",
      "uptime" : 489,
      "optime" : {
        "ts" : Timestamp(1635916848, 1),
        "t" : NumberLong(1)
      },
      "optimeDurable" : {
        "ts" : Timestamp(1635916848, 1),
        "t" : NumberLong(1)
      },
      "optimeDate" : ISODate("2021-11-03T05:20:48Z"),
      "optimeDurableDate" : ISODate("2021-11-03T05:20:48Z"),
      "lastHeartbeat" : ISODate("2021-11-03T05:20:56.963Z"),
      "lastHeartbeatRecv" : ISODate("2021-11-03T05:20:56.228Z"),
      "pingMs" : NumberLong(0),
      "lastHeartbeatMessage" : "",
      "syncSourceHost" : "192.168.0.121:27118",
      "syncSourceId" : 0,
      "infoMessage" : "",
      "configVersion" : 1,
      "configTerm" : 1
    },
    {
      "_id" : 2,
      "name" : "192.168.0.123:27118",
      "health" : 1,
      "state" : 2,
      "stateStr" : "SECONDARY",
      "uptime" : 489,
      "optime" : {
        "ts" : Timestamp(1635916848, 1),
        "t" : NumberLong(1)
      },
      "optimeDurable" : {
        "ts" : Timestamp(1635916848, 1),
        "t" : NumberLong(1)
      },
      "optimeDate" : ISODate("2021-11-03T05:20:48Z"),
      "optimeDurableDate" : ISODate("2021-11-03T05:20:48Z"),
      "lastHeartbeat" : ISODate("2021-11-03T05:20:56.963Z"),
      "lastHeartbeatRecv" : ISODate("2021-11-03T05:20:56.228Z"),
      "pingMs" : NumberLong(0),
      "lastHeartbeatMessage" : "",
      "syncSourceHost" : "192.168.0.121:27118",
      "syncSourceId" : 0,
      "infoMessage" : "",
      "configVersion" : 1,
      "configTerm" : 1
    }
  ],
  "ok" : 1,
  "$clusterTime" : {
    "clusterTime" : Timestamp(1635916848, 1),
    "signature" : {
      "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
      "keyId" : NumberLong(0)
    }
  },
  "operationTime" : Timestamp(1635916848, 1)
}
# 题外话
# 如果是SECONDARY节点,用slaveOk命令允许查询操作。
rs_shard_server_03:SECONDARY> rs.slaveOk()
rs_shard_server_03:SECONDARY> rs.status()

配置路由mongos

 # 进入 rs_mongos_server 容器
 docker exec -it rs_mongos_server bash
 # 登录mongo
 mongo --host 192.168.0.121 --port 27017
 
# 执行初始化语句,配置 mongo router 路由信息
sh.addShard("rs_shard_server_01/192.168.0.121:27118,192.168.0.122:27118,192.168.0.123:27118");
sh.addShard("rs_shard_server_02/192.168.0.121:27218,192.168.0.122:27218,192.168.0.123:27218");
sh.addShard("rs_shard_server_03/192.168.0.121:27318,192.168.0.122:27318,192.168.0.123:27318");

# 执行成功后会显示如下打印信息
mongos> sh.addShard("rs_shard_server_03/192.168.0.121:27318,192.168.0.122:27318,192.168.0.123:27318");
{
  "shardAdded" : "rs_shard_server_03",
  "ok" : 1,
  "$clusterTime" : {
    "clusterTime" : Timestamp(1635917549, 1),
    "signature" : {
      "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
      "keyId" : NumberLong(0)
    }
  },
  "operationTime" : Timestamp(1635917548, 3)
}
mongos>

# 路由设置完成后,可以使用以下指令查看状态
mongos> sh.status()

--- Sharding Status ---
  sharding version: {
    "_id" : 1,
    "minCompatibleVersion" : 5,
    "currentVersion" : 6,
    "clusterId" : ObjectId("61821955f12d632a3b31b26e")
  }
  shards:
        {  "_id" : "rs_shard_server_01",  "host" : "rs_shard_server_01/192.168.0.121:27118,192.168.0.122:27118,192.168.0.123:27118",  "state" : 1,  "topologyTime" : Timestamp(1635917525, 2) }
        {  "_id" : "rs_shard_server_02",  "host" : "rs_shard_server_02/192.168.0.121:27218,192.168.0.122:27218,192.168.0.123:27218",  "state" : 1,  "topologyTime" : Timestamp(1635917544, 1) }
        {  "_id" : "rs_shard_server_03",  "host" : "rs_shard_server_03/192.168.0.121:27318,192.168.0.122:27318,192.168.0.123:27318",  "state" : 1,  "topologyTime" : Timestamp(1635917548, 1) }
  active mongoses:
        "5.0.3" : 3
  autosplit:
        Currently enabled: yes
  balancer:
        Currently enabled: yes
        Currently running: no
        Failed balancer rounds in last 5 attempts: 0
        Migration results for the last 24 hours:
                No recent migrations
  databases:
        {  "_id" : "config",  "primary" : "config",  "partitioned" : true }

截止到目前为止,MongoDB的副本集和分片高可用已经配置完成。

测试使用

# 任意一节点操作
# 进入mongos 容器中
docker exec -it rs_mongos_server bash
# 连接mongos
mongo --host 192.168.0.121 --port 27017
use admin


# 使用testdb1库
# 循环插入数据到testdb1库的tab1集合中的键id中
# 该库对应的该集合对应的该键被设置成了分片
# 查看分片情况

#  testdb1开启分片功能
db.runCommand( { enablesharding  : "testdb1"});
db.runCommand( { shardcollection : "testdb1.tab1",key : {id: 1} } )

# 添加数据 
use testdb1;
for(var i=1;i<=20000;i++) db.tab1.save({id:i,"test1":"testval1"});

db.tab1.stats();
exit

分别在三个主机上操作配置库、插入测试数据、查看测试数据

验证了副本同步,最后的显示结果看到 “sharded” : true 表示分片也是成功的

开启登录认证

# 在任意mongos节点操作
# 进入mongos 容器中
docker exec -it rs_mongos_server bash
 
# 连接mongos
mongo --host 192.168.0.121 --port 27017
mongos> use admin
switched to db admin    
mongos> show tables


# 添加两个管理员账号,一个系统管理员:system 一个数据库管理员:administrator
# 先添加系统管理员账号,用来管理用户
db.createUser({user:"root",pwd:"root",roles:[{role:"root",db:"admin"}]})

# 添加数据库管理员,用来管理所有数据库
db.createUser({user:'administrator', pwd:'administrator', roles:[{role:"userAdminAnyDatabase",db:"admin"}]})
# 添加管理员用户认证,认证之后才能管理所有数据库   
db.auth('administrator','administrator')

# 退出,用刚才创建的账号进行登录(两个都测试一下)
mongo 192.168.0.121:27017 -u root -proot --authenticationDatabase admin
mongo 192.168.0.121:27017 -u administrator -padministrator  --authenticationDatabase admin



附带一些常用命令列表:

# 数据库 命令
db.serverStatus().connections; //连接数查看
show collections  //查看表信息
db.test_shard.find().count() //查看table1数据长度
db.test_shard.remove({}) //删除数据表
db.stats()   //查看所有的分片服务器状态
db.adminCommand( { listShards: 1 } ) //分片列表
db.test_shard.find({ age: 36 }).explain()   //精确查询
db.test_shard.find({ age: { $gte : 36 ,$lt : 73 } }).explain() //范围查询

# 分片 操作命令
sh.enableSharding('testdb')                //开启数据库testdb分片
sh.shardCollection('testdb.users',{uid:1})    //按testdb.users的uid字段分片
sh.shardCollection("testdb.test_shard",{"age": 1})     //按ranged分片
sh.shardCollection("testdb.test_shard2",{"age": "hashed"}) //按hash分片
sh.status()   //查看分片节点
sh.addShard() //向集群中添加一个 shard
sh.getBalancerState()   //查看平衡器
sh.disableBalancing()   //禁用平衡器
sh.enableBalancing()    //启用平衡器
db.runCommand( { removeShard: "mongodb0" } ) //删除分片mongodb0,迁移数据查看命令
db.runCommand( { movePrimary: "test", to: "mongodb1" })   //将数据库test未分片mongodb0的数据,迁移到mongodb1主分片。
db.adminCommand("flushRouterConfig") //处理分片后,刷新路由数据。

use config 
db.databases.find()  //查看所有数据库使用分片
db.settings.save({_id:"chunksize",value:1}) //将 chunk 的大小调整为 1MB
db.serverStatus().sharding


# 副本集 操作命令
rs.status()   //查看成员的运行状态等信息
rs.config()    //查看配置信息
rs.slaveOk()  //允许在SECONDARY节点上进行查询操作,默认从节点不具有查询功能
rs.isMaster()  //查询该节点是否是主节点
rs.add({})   //添加新的节点到该副本集中
rs.remove()   //从副本集中删除节点
rs.stepDown //降级节点
db.printSlaveReplicationInfo()  //查看同步情况
rs.addArb("172.20.0.16:27038") //添加仲裁节点
rs.add({_id : 2, host : "192.168.1.22:26003", arbiterOnly:true}) //添加仲裁节点

# 强制加入仲裁节点:
config=rs.conf()
config.members=[config.members[0],config.members[1],{_id:5,host:"127.0.0.1:27023",priority:5,arbiterOnly:"true"}]
rs.reconfig(config,{force:true})

# 强制主节点:
cfg = rs.conf()
cfg.members[0].priority = 0.5
cfg.members[1].priority = 0.5
cfg.members[2].priority = 1
rs.reconfig(cfg)

# 备份/恢复
mongodump -h 127.0.0.1:27017 -d test -o /data/backup/
mongorestore -h 127.0.0.1:27017 -d test --dir /data/db/test




发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接