第一次使用 MongoDB + Docker - 从 Docker Compose 进行设置

8

我想尝试在GitHub上找到的一个项目, 所以我在MacOS上安装了MongoDB,现在我正在尝试通过目录中的docker compose文件正确设置它。 这是docker文件:

version: '3'
services:
# replica set 1
  mongors1n1:
    container_name: mongors1n1
    image: mongo
    command: mongod --shardsvr --replSet mongors1 --dbpath /data/db --port 27017
    ports:
      - 27017:27017
    expose:
      - "27017"
    volumes:
      - ~/mongo_cluster/data1:/data/db

  mongors1n2:
    container_name: mongors1n2
    image: mongo
    command: mongod --shardsvr --replSet mongors1 --dbpath /data/db --port 27017
    ports:
      - 27027:27017
    expose:
      - "27017"
    volumes:
      - ~/mongo_cluster/data2:/data/db

  mongors1n3:
    container_name: mongors1n3
    image: mongo
    command: mongod --shardsvr --replSet mongors1 --dbpath /data/db --port 27017
    ports:
      - 27037:27017
    expose:
      - "27017"

    volumes:
      - ~/mongo_cluster/data3:/data/db

# replica set 2
  mongors2n1:
    container_name: mongors2n1
    image: mongo
    command: mongod --shardsvr --replSet mongors2 --dbpath /data/db --port 27017
    ports:
      - 27047:27017
    expose:
      - "27017"
    volumes:
      - ~/mongo_cluster/data4:/data/db

  mongors2n2:
    container_name: mongors2n2
    image: mongo
    command: mongod --shardsvr --replSet mongors2 --dbpath /data/db --port 27017
    ports:
      - 27057:27017
    expose:
      - "27017"
    volumes:
      - ~/mongo_cluster/data5:/data/db

  mongors2n3:
    container_name: mongors2n3
    image: mongo
    command: mongod --shardsvr --replSet mongors2 --dbpath /data/db --port 27017
    ports:
      - 27067:27017
    expose:
      - "27017"

    volumes:
      - ~/mongo_cluster/data6:/data/db

  # mongo config server
  mongocfg1:
    container_name: mongocfg1
    image: mongo
    command: mongod --configsvr --replSet mongors1conf --dbpath /data/db --port 27017
    expose:
      - "27017"
    volumes:
      - ~/mongo_cluster/config1:/data/db

  mongocfg2:
    container_name: mongocfg2
    image: mongo
    command: mongod --configsvr --replSet mongors1conf --dbpath /data/db --port 27017
    expose:
      - "27017"
    volumes:
      - ~/mongo_cluster/config2:/data/db

  mongocfg3:
    container_name: mongocfg3
    image: mongo
    command: mongod --configsvr --replSet mongors1conf --dbpath /data/db --port 27017

    expose:
      - "27017"
    volumes:
      - ~/mongo_cluster/config3:/data/db

# mongos router
  mongos1:
    container_name: mongos1
    image: mongo
    depends_on:
      - mongocfg1
      - mongocfg2
    command: mongos --configdb mongors1conf/mongocfg1:27017,mongocfg2:27017,mongocfg3:27017 --port 27017
    ports:
      - 27019:27017
    expose:
      - "27017"

  mongos2:
    container_name: mongos2
    image: mongo
    depends_on:
      - mongocfg1
      - mongocfg2
    command: mongos --configdb mongors1conf/mongocfg1:27017,mongocfg2:27017,mongocfg3:27017 --port 27017
    ports:
      - 27020:27017
    expose:
      - "27017"


# TODO after running docker-compose
# conf = rs.config();
# conf.members[0].priority = 2;
# rs.reconfig(conf);

这是运行并创建分片等操作的脚本:
#!/bin/sh
docker-compose up
# configure our config servers replica set
docker exec -it mongocfg1 bash -c "echo 'rs.initiate({_id: \"mongors1conf\",configsvr: true, members: [{ _id : 0, host : \"mongocfg1\" },{ _id : 1, host : \"mongocfg2\" }, { _id : 2, host : \"mongocfg3\" }]})' | mongo"

# building replica shard
docker exec -it mongors1n1 bash -c "echo 'rs.initiate({_id : \"mongors1\", members: [{ _id : 0, host : \"mongors1n1\" },{ _id : 1, host : \"mongors1n2\" },{ _id : 2, host : \"mongors1n3\" }]})' | mongo"
docker exec -it mongors2n1 bash -c "echo 'rs.initiate({_id : \"mongors2\", members: [{ _id : 0, host : \"mongors2n1\" },{ _id : 1, host : \"mongors2n2\" },{ _id : 2, host : \"mongors2n3\" }]})' | mongo"


# we add shard to the routers
docker exec -it mongos1 bash -c "echo 'sh.addShard(\"mongors1/mongors1n1\")' | mongo "
docker exec -it mongos1 bash -c "echo 'sh.addShard(\"mongors2/mongors2n1\")' | mongo "

如果我试图直接运行脚本,会出现以下错误:

mongos1 | {"t":{"$date":"2021-07-25T09:03:56.101+00:00"},"s":"I", "c":"-", "id":4333222, "ctx":"ReplicaSetMonitor-TaskExecutor","msg":"RSM received error response","attr":{"host":"mongocfg3:27017","error":"HostUnreachable: Error connecting to mongocfg3:27017 (172.18.0.2:27017) :: caused by :: Connection refused","replicaSet":"mongors1conf","response":"{}"}}

mongos1 | {"t":{"$date":"2021-07-25T09:03:56.101+00:00"},"s":"I", "c":"NETWORK", "id":4712102, "ctx":"ReplicaSetMonitor-TaskExecutor","msg":"在副本集中主机失败", "attr":{"replicaSet":"mongors1conf","host":"mongocfg3:27017","error":{"code":6,"codeName":"HostUnreachable","errmsg":"连接到mongocfg3:27017 (172.18.0.2:27017)时出错:: caused by :: 连接被拒绝"}, "action":{"dropConnections":true,"requestImmediateCheck":false,"outcome":{"host":"mongocfg3:27017","success":false,"errorMessage":"HostUnreachable: 连接到mongocfg3:27017 (172.18.0.2:27017)时出错:: caused by :: 连接被拒绝"}}}}

还有其他的错误信息:

mongos1 | {"t":{"$date":"2021-07-25T09:05:39.743+00:00"},"s":"I", "c":"-", "id":4939300, "ctx":"monitoring-keys-for-HMAC","msg":"Failed to refresh key cache","attr":{"error":"FailedToSatisfyReadPreference: Could not find host matching read preference { mode: "nearest" } for set mongors1conf","nextWakeupMillis":1800}}

不应该由用户手动配置所有文件,而是应该由Docker自动完成。或者我需要手动创建一些内容,例如数据库等吗?

编辑:在运行脚本时出现的第一个错误在这里:log

1个回答

12

所以这里是一种帮助的尝试.. 大多数情况下,docker compose yaml文件非常接近,除了一些小的端口和绑定参数。期望初始化将是额外的命令。 例如:

  1. docker-compose up启动环境
  2. 运行一些脚本来初始化环境

...但这已经是原始帖子的一部分了。

所以这里是一个Docker Compose文件

docker-compose.yml

version: '3'
services:
 # mongo config server
  mongocfg1:
    container_name: mongocfg1
    hostname: mongocfg1
    image: mongo
    command: mongod --configsvr --replSet mongors1conf --dbpath /data/db --port 27019 --bind_ip_all
    volumes:
      - ~/mongo_cluster/config1:/data/db

  mongocfg2:
    container_name: mongocfg2
    hostname: mongocfg2
    image: mongo
    command: mongod --configsvr --replSet mongors1conf --dbpath /data/db --port 27019 --bind_ip_all
    volumes:
      - ~/mongo_cluster/config2:/data/db

  mongocfg3:
    container_name: mongocfg3
    hostname: mongocfg3
    image: mongo
    command: mongod --configsvr --replSet mongors1conf --dbpath /data/db --port 27019 --bind_ip_all
    volumes:
      - ~/mongo_cluster/config3:/data/db

# replica set 1
  mongors1n1:
    container_name: mongors1n1
    hostname: mongors1n1
    image: mongo
    command: mongod --shardsvr --replSet mongors1 --dbpath /data/db --port 27018 --bind_ip_all
    volumes:
      - ~/mongo_cluster/data1:/data/db

  mongors1n2:
    container_name: mongors1n2
    hostname: mongors1n2
    image: mongo
    command: mongod --shardsvr --replSet mongors1 --dbpath /data/db --port 27018 --bind_ip_all
    volumes:
      - ~/mongo_cluster/data2:/data/db

  mongors1n3:
    container_name: mongors1n3
    hostname: mongors1n3
    image: mongo
    command: mongod --shardsvr --replSet mongors1 --dbpath /data/db --port 27018 --bind_ip_all
    volumes:
      - ~/mongo_cluster/data3:/data/db

# replica set 2
  mongors2n1:
    container_name: mongors2n1
    hostname: mongors2n1
    image: mongo
    command: mongod --shardsvr --replSet mongors2 --dbpath /data/db --port 27018 --bind_ip_all
    volumes:
      - ~/mongo_cluster/data4:/data/db

  mongors2n2:
    container_name: mongors2n2
    hostname: mongors2n2
    image: mongo
    command: mongod --shardsvr --replSet mongors2 --dbpath /data/db --port 27018 --bind_ip_all
    volumes:
      - ~/mongo_cluster/data5:/data/db

  mongors2n3:
    container_name: mongors2n3
    hostname: mongors2n3
    image: mongo
    command: mongod --shardsvr --replSet mongors2 --dbpath /data/db --port 27018 --bind_ip_all
    volumes:
      - ~/mongo_cluster/data6:/data/db

# mongos router
  mongos1:
    container_name: mongos1
    hostname: mongos1
    image: mongo
    depends_on:
      - mongocfg1
      - mongocfg2
    command: mongos --configdb mongors1conf/mongocfg1:27019,mongocfg2:27019,mongocfg3:27019 --port 27017 --bind_ip_all
    ports:
      - 27017:27017

  mongos2:
    container_name: mongos2
    hostname: mongos2
    image: mongo
    depends_on:
      - mongocfg1
      - mongocfg2
    command: mongos --configdb mongors1conf/mongocfg1:27019,mongocfg2:27019,mongocfg3:27019 --port 27017 --bind_ip_all
    ports:
      - 27016:27017

...和一些脚本来完成初始化...

docker-compose up -d

等待几秒钟让其启动,然后发出指令...

# Init the replica sets (use the MONGOS host)
docker exec -it mongos1 bash -c "echo 'rs.initiate({_id: \"mongors1conf\",configsvr: true, members: [{ _id : 0, host : \"mongocfg1:27019\", priority: 2 },{ _id : 1, host : \"mongocfg2:27019\" }, { _id : 2, host : \"mongocfg3:27019\" }]})' | mongo --host mongocfg1:27019"
docker exec -it mongos1 bash -c "echo 'rs.initiate({_id : \"mongors1\", members: [{ _id : 0, host : \"mongors1n1:27018\", priority: 2 },{ _id : 1, host : \"mongors1n2:27018\" },{ _id : 2, host : \"mongors1n3:27018\" }]})' | mongo --host mongors1n1:27018"
docker exec -it mongos1 bash -c "echo 'rs.initiate({_id : \"mongors2\", members: [{ _id : 0, host : \"mongors2n1:27018\", priority: 2 },{ _id : 1, host : \"mongors2n2:27018\" },{ _id : 2, host : \"mongors2n3:27018\" }]})' | mongo --host mongors2n1:27018"

再次提醒,请等待10-15秒,以便系统适应最近的指令...
# ADD TWO SHARDS (mongors1, and mongors2)
docker exec -it mongos1 bash -c "echo 'sh.addShard(\"mongors1/mongors1n1:27018,mongors1n2:27018,mongors1n2:27018\")' | mongo"
docker exec -it mongos1 bash -c "echo 'sh.addShard(\"mongors2/mongors2n1:27018,mongors2n2:27018,mongors2n3:27018\")' | mongo"

现在,尝试从运行docker的主机连接到mongos(假设您在此主机上安装了mongo shell)。使用2个mongos主机作为种子列表。
mongo --host "localhost:27017,localhost:27016"

评论

注意在init()调用中如何将node0的优先级设置为2?

注意配置服务器都是端口27019 - 这遵循了MongoDB的建议。

注意分片服务器都是端口27018 - 再次遵循mongo建议。

mongos公开了2个端口,27017(MongoDB的默认端口)和27016(高可用性的第二个mongos)。

出于安全考虑,配置服务器和分片服务器不会公开它们各自的端口。应该使用mongos来访问数据。如果需要出于管理目的打开这些端口,只需将其添加到docker compose文件即可。

副本集之间的通信没有使用身份验证。这是一个安全隐患。需要决定哪种身份验证机制最适合您的场景 - 可以使用keyfile(只是一个在副本集成员之间相同的文本文件)或x509证书。如果选择x509,则需要在每个docker容器中包含CA.cert以供参考,以及与适当主机名对齐的每个服务器的单个证书。需要添加启动配置项以使用选择的任一身份验证方法的mongod进程。

未指定日志记录。将mongod和mongos的日志输出设置为默认位置/var/log/mongodb/mongod.log和/var/log/mongodb/mongos.log可能是有意义的。如果没有指定日志记录策略,我认为mongo会记录到标准输出,如果运行docker-compose up -d则会被抑制。

超级用户:系统上尚未创建任何用户。通常,在将副本集添加到分片集群之前,我喜欢为每个副本集添加一个具有根访问权限的超级用户帐户,以便在副本集级别进行管理更改时可以使用。通过docker-compose方法,您可以从mongos角度创建超级用户并执行大多数所需的分片集群操作,但仍然可以使用副本集用户。

操作系统可调参数 - Mongo 喜欢占用所有系统资源。在一个共享生态系统中,其中一个物理主机托管一堆Mongo进程时,你可能需要考虑指定wiredTiger缓存大小等参数。默认情况下,WiredTiger希望 (系统内存大小 - 1 GB) / 2。此外,您将受益于将ulimits设置为适当的值 - 即,每个用户64000个文件句柄是一个不错的开始 - mongo可能会使用大量的文件。此外,文件系统应该挂载在具有xfs的地方。此策略使用主机系统用户的主目录作为数据库数据目录。这里可以采用更周到的做法。

还有什么其他的吗?

我确定我遗漏了一些内容。如果您有任何问题,请留言,我会回复。

更新1

上述docker-compose.yml文件中一些主机缺少主机名属性,导致负载均衡器出现问题,因此我已编辑docker-compose.yml以在所有主机上包含主机名。

此外,addShard()方法只涉及副本集的一个主机。为了完整起见,我将其他主机添加到上述addShard()方法中。

按照以下步骤操作将创建一个全新的分片集群,但是尚未创建用户数据库,因此没有用户数据库进行分片。因此,让我们花一些时间添加一个数据库并将其分片,然后查看分片分布情况(也称为负载均衡器结果)。
我们必须通过mongos连接到数据库(如上所述)。本示例假定使用mongo shell。
mongo --host "localhost:27017,localhost:27016"

Mongo中的数据库可以通过多种方式创建。虽然没有明确的数据库创建命令,但有一个明确的创建集合命令(db.createCollection())。我们必须首先使用“use”命令设置数据库上下文...
use mydatabase
db.createCollection("mycollection")

...但是我们可以通过在不存在的集合上创建索引来创建数据库和集合,而不是使用这个命令。(如果您已经创建了集合,不用担心,下一个命令仍然会成功)。

use mydatabase
db.mycollection.createIndex({lastName: 1, creationDate: 1})

在这个例子中,我创建了一个由两个字段组成的复合索引...

  • lastName
  • creationDate

... 在一个尚不存在的集合上,在一个尚不存在的数据库上。 一旦我发出这个命令,数据库和集合都将被创建。 此外,我现在拥有了分片密钥的基础 - 分片分布将基于此密钥。 这个分片密钥将基于这个新索引具有这两个字段。

对数据库进行分片

假设我已经发出了createIndex命令,我现在可以在数据库上启用分片,并发出shardCollection命令...

sh.enableSharding("mydatabase")
sh.shardCollection("mydatabase.mycollection", { "lastName": 1, "creationDate": 1})

注意到命令'shardCollection()'是如何引用我们早先创建的索引字段的吗?假设分片已成功应用,我们现在可以通过发出sh.status()命令来查看数据的分布。
sh.status()

输出示例:(新集合,尚无数据,因此没有真正的数据分布 - 需要插入超过64MB的数据,以便有多个块进行分发)

mongos> sh.status()
--- Sharding Status --- 
  sharding version: {
    "_id" : 1,
    "minCompatibleVersion" : 5,
    "currentVersion" : 6,
    "clusterId" : ObjectId("6101c030a98b2cc106034695")
  }
  shards:
        {  "_id" : "mongors1",  "host" : "mongors1/mongors1n1:27018,mongors1n2:27018,mongors1n3:27018",  "state" : 1,  "topologyTime" : Timestamp(1627504744, 1) }
        {  "_id" : "mongors2",  "host" : "mongors2/mongors2n1:27018,mongors2n2:27018,mongors2n3:27018",  "state" : 1,  "topologyTime" : Timestamp(1627504753, 1) }
  active mongoses:
        "5.0.1" : 2
  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 }
        {  "_id" : "mydatabase",  "primary" : "mongors2",  "partitioned" : true,  "version" : {  "uuid" : UUID("bc890722-00c6-4cbe-a3e1-eab9692faf93"),  "timestamp" : Timestamp(1627504768, 2),  "lastMod" : 1 } }
                mydatabase.mycollection
                        shard key: { "lastName" : 1, "creationDate" : 1 }
                        unique: false
                        balancing: true
                        chunks:
                                mongors2    1
                        { "lastName" : { "$minKey" : 1 }, "creationDate" : { "$minKey" : 1 } } -->> { "lastName" : { "$maxKey" : 1 }, "creationDate" : { "$maxKey" : 1 } } on : mongors2 Timestamp(1, 0) 

插入一些数据

为了测试分片,我们可以添加一些测试数据。同样,我们希望按照 lastName 和 creationDate 进行分布。

在 mongoshell 中,我们可以运行 JavaScript。下面是一个脚本,它将创建测试记录,以便数据将被拆分和平衡。这将创建 500,000 条假记录。我们需要超过 64MB 的数据才能创建另一个块来平衡。500,000 条记录将创建大约 5 个块。这需要几分钟才能运行和完成。

use mydatabase

function randomInteger(min, max) {
    return Math.floor(Math.random() * (max - min) + min);
} 

function randomAlphaNumeric(length) {
  var result = [];
  var characters = 'abcdef0123456789';
  var charactersLength = characters.length;

  for ( var i = 0; i < length; i++ ) {
    result.push(characters.charAt(Math.floor(Math.random() * charactersLength)));
  }

  return result.join('');
}

function generateDocument() {
  return {
    lastName: randomAlphaNumeric(8),
    creationDate: new Date(),
    stringFixedLength: randomAlphaNumeric(8),
    stringVariableLength: randomAlphaNumeric(randomInteger(5, 50)),
    integer1: NumberInt(randomInteger(0, 2000000)),
    long1: NumberLong(randomInteger(0, 100000000)),
    date1: new Date(),
    guid1: new UUID()
  };
}

for (var j = 0; j < 500; j++) {
  var batch=[];

  for (var i = 0; i < 1000; i++) {
    batch.push(
      {insertOne: {
          document: generateDocument() 
        } 
      }
    );
  }
  
  db.mycollection.bulkWrite(batch, {ordered: false});
}

请稍等几分钟并在mongoshell中进行回顾,如果现在查看分片状态,我们应该看到chunks分布在两个分片上...

sh.status()

...我们应该看到类似的东西...

mongos> sh.status()
--- Sharding Status --- 
  sharding version: {
    "_id" : 1,
    "minCompatibleVersion" : 5,
    "currentVersion" : 6,
    "clusterId" : ObjectId("6101c030a98b2cc106034695")
  }
  shards:
        {  "_id" : "mongors1",  "host" : "mongors1/mongors1n1:27018,mongors1n2:27018,mongors1n3:27018",  "state" : 1,  "topologyTime" : Timestamp(1627504744, 1) }
        {  "_id" : "mongors2",  "host" : "mongors2/mongors2n1:27018,mongors2n2:27018,mongors2n3:27018",  "state" : 1,  "topologyTime" : Timestamp(1627504753, 1) }
  active mongoses:
        "5.0.1" : 2
  autosplit:
        Currently enabled: yes
  balancer:
        Currently enabled: yes
        Currently running: yes
        Collections with active migrations: 
                config.system.sessions started at Wed Jul 28 2021 20:44:25 GMT+0000 (UTC)
        Failed balancer rounds in last 5 attempts: 0
        Migration results for the last 24 hours: 
                60 : Success
  databases:
        {  "_id" : "config",  "primary" : "config",  "partitioned" : true }
                config.system.sessions
                        shard key: { "_id" : 1 }
                        unique: false
                        balancing: true
                        chunks:
                                mongors1    965
                                mongors2    59
                        too many chunks to print, use verbose if you want to force print
        {  "_id" : "mydatabase",  "primary" : "mongors2",  "partitioned" : true,  "version" : {  "uuid" : UUID("bc890722-00c6-4cbe-a3e1-eab9692faf93"),  "timestamp" : Timestamp(1627504768, 2),  "lastMod" : 1 } }
                mydatabase.mycollection
                        shard key: { "lastName" : 1, "creationDate" : 1 }
                        unique: false
                        balancing: true
                        chunks:
                                mongors1    2
                                mongors2    3
                        { "lastName" : { "$minKey" : 1 }, "creationDate" : { "$minKey" : 1 } } -->> {
                            "lastName" : "00001276",
                            "creationDate" : ISODate("2021-07-28T20:42:00.867Z")
                        } on : mongors1 Timestamp(2, 0) 
                        {
                            "lastName" : "00001276",
                            "creationDate" : ISODate("2021-07-28T20:42:00.867Z")
                        } -->> {
                            "lastName" : "623292c2",
                            "creationDate" : ISODate("2021-07-28T20:42:01.046Z")
                        } on : mongors1 Timestamp(3, 0) 
                        {
                            "lastName" : "623292c2",
                            "creationDate" : ISODate("2021-07-28T20:42:01.046Z")
                        } -->> {
                            "lastName" : "c3f2a99a",
                            "creationDate" : ISODate("2021-07-28T20:42:06.474Z")
                        } on : mongors2 Timestamp(3, 1) 
                        {
                            "lastName" : "c3f2a99a",
                            "creationDate" : ISODate("2021-07-28T20:42:06.474Z")
                        } -->> {
                            "lastName" : "ed75c36c",
                            "creationDate" : ISODate("2021-07-28T20:42:03.984Z")
                        } on : mongors2 Timestamp(1, 6) 
                        {
                            "lastName" : "ed75c36c",
                            "creationDate" : ISODate("2021-07-28T20:42:03.984Z")
                        } -->> { "lastName" : { "$maxKey" : 1 }, "creationDate" : { "$maxKey" : 1 } } on : mongors2 Timestamp(2, 1) 

在这里,我们可以看到平衡活动的证据。请参见mongors1和mongors2标签下的“块”。当它平衡我们的测试集合时,它也会预分裂并平衡另一个用于会话数据的集合。我相信这是一次性的系统自动化。
希望这些细节能够帮助您。如果您有其他问题,请告诉我。

1
非常感谢,它完美地运行了!只有一个问题:如何知道分片是否平衡?我可以通过命令行调用“mongo”来进行外部操作吗? - Fabio
@Fabio - 请查看上面帖子中的更新。 - barrypicker
我正在尝试在我的数据库集合上启用共享。我成功创建了索引,但是当我尝试启用共享时,出现了这个错误:https://imgur.com/a/Jt6cSvg。从我所理解的来看,问题可能与IP有关,你认为呢? - Fabio
1
在试图清除并重新启动我笔记本电脑上的目录(~/mongo_cluster)时,我发现了一个问题。如果要重新开始,则必须清除此目录;否则,生成的集群配置可能会受到污染,并可能让您感到非常困惑。请注意,此目录位于 Docker 主机上,而不是 Docker 容器中。 - barrypicker
@barrypicker,感谢您提供这个非常棒的答案。当我尝试运行它时,它显示“bash: mongo: command not found”。您有什么想法吗? - Saurabh Agrawal
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接