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

发布时间:2021-11-14作者:laosun阅读(2062)

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

    目录目录如下图所示:

    Image

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

    启动

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

    # 目录结构依旧在/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

    启动方式如下所示:

    Image

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

    首先我们选择任意一台机器,我这里选择了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()

    执行方式如下图所示:

    Image

    我们可以看到三台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


6 +1

版权声明

 请文明留言

3 条评论