本文最后更新于:2024年11月11日 上午

之前我们介绍过 MongoDB 分片GridFS 的工作原理,本文用 Docker 进行实战部署,并在 GridFS 上进行分片测试。

基础准备

实战目标

  1. Docker compose 配置化部署 MongoDB 服务
    • 配置 3 个 config 服务器
    • 配置 3 个 分片存储服务器
    • 配置 1 个 路由服务器
  2. 脚本启动、初始化
  3. 实现 GridFS 数据分片
  4. 添加 GridFS 数据
  5. 查看分片效果

环境准备

环境配置

  • 安装 Docker
  • 安装 mongodbsh
  • 配置 docker compose

创建 mongo 网络

检查名为mongo的网络是否已经存在

1
docker network ls

如果mongo网络不存在,需要手动创建:

1
docker network create mongo

在 docker compose 文件中添加网络配置

1
2
3
networks:
mongo:
external: true

重新启动服务

1
docker-compose -f fates-mongo-compose.yaml up -d

注意事项

  • 如果你正在使用多个Docker Compose文件或者与其他项目共享网络,确保所有项目都使用相同的网络名称。
  • 如果你之前已经运行了Docker Compose并且删除了网络,你可能需要先删除旧的容器和卷,然后重新创建网络。

检查时区文件

构建时间同步的多个 MongoDB 服务需要统一的时区信息,通过共用本机的 /etc/localtime文件实现。

检查该文件是否存在,如果不存在则需要手动创建符号链接,以 Shanghai 时区为例:

1
sudo ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

更新系统时钟(可选操作)

1
sudo hwclock --systohc

验证设置

验证时区设置是否正确:

1
timedatectl

创建文件夹

在根目录创建这样的文件夹结构:

1
2
3
4
5
6
7
/mongodata
├── config1
├── config2
├── config3
├── shard1
├── shard2
└── shard3

赋予其他用户读写权限

1
sudo chmod -R 757 /mongodata

docker-compose 配置

服务定义

services部分定义不同的MongoDB容器,包括分片(shard)、配置服务器(config)和路由进程(mongos)。

shard
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
services:
shard1:
image: mongodb/mongodb-community-server:7.0.12-ubuntu2204
container_name: mongo_shard1
# --shardsvr: 这个参数仅仅只是将默认的27017端口改为27018,如果指定--port参数,可用不需要这个参数
# --directoryperdb:每个数据库使用单独的文件夹
# --replSet 副本集
command: mongod --shardsvr --directoryperdb --replSet shard1
volumes:
- /etc/localtime:/etc/localtime
- /mongodata/shard1:/data/db
privileged: true
# mem_limit: 16000000000
networks:
- mongo
config
1
2
3
4
5
6
7
8
9
10
11
config1:
image: mongodb/mongodb-community-server:7.0.12-ubuntu2204
container_name: mongo_config1
# --configsvr: 这个参数仅仅是将默认端口由27017改为27019, 如果指定--port可不添加该参数
command: mongod --configsvr --directoryperdb --replSet fates-mongo-config
restart: always
volumes:
- /etc/localtime:/etc/localtime
- /mongodata/config1:/data/configdb
networks:
- mongo
mongos
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mongos:
image: mongodb/mongodb-community-server:7.0.12-ubuntu2204
container_name: mongo_mongos
command: mongos --configdb fates-mongo-config/config1:27019,config2:27019,config3:27019 --bind_ip 0.0.0.0 --port 27017
ports:
- 27017:27017
restart: always
volumes:
- /etc/localtime:/etc/localtime
depends_on:
- config1
- config2
- config3
networks:
- mongo

网络配置

1
2
3
networks:
mongo:
external: true

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
services:
shard1:
image: mongodb/mongodb-community-server:7.0.12-ubuntu2204
container_name: mongo_shard1
command: mongod --shardsvr --directoryperdb --replSet shard1
restart: always
volumes:
- /etc/localtime:/etc/localtime
- /mongodata/shard1:/data/db
privileged: true
networks:
- mongo

shard2:
image: mongodb/mongodb-community-server:7.0.12-ubuntu2204
container_name: mongo_shard2
command: mongod --shardsvr --directoryperdb --replSet shard2
restart: always
volumes:
- /etc/localtime:/etc/localtime
- /mongodata/shard2:/data/db
privileged: true
networks:
- mongo

shard3:
image: mongodb/mongodb-community-server:7.0.12-ubuntu2204
container_name: mongo_shard3
command: mongod --shardsvr --directoryperdb --replSet shard3
restart: always
volumes:
- /etc/localtime:/etc/localtime
- /mongodata/shard3:/data/db
privileged: true
networks:
- mongo

config1:
image: mongodb/mongodb-community-server:7.0.12-ubuntu2204
container_name: mongo_config1
command: mongod --configsvr --directoryperdb --replSet fates-mongo-config
restart: always
volumes:
- /etc/localtime:/etc/localtime
- /mongodata/config1:/data/configdb
networks:
- mongo

config2:
image: mongodb/mongodb-community-server:7.0.12-ubuntu2204
container_name: mongo_config2
restart: always
command: mongod --configsvr --directoryperdb --replSet fates-mongo-config
volumes:
- /etc/localtime:/etc/localtime
- /mongodata/config2:/data/configdb
networks:
- mongo

config3:
image: mongodb/mongodb-community-server:7.0.12-ubuntu2204
container_name: mongo_config3
restart: always
command: mongod --configsvr --directoryperdb --replSet fates-mongo-config
volumes:
- /etc/localtime:/etc/localtime
- /mongodata/config3:/data/configdb
networks:
- mongo

mongos:
image: mongodb/mongodb-community-server:7.0.12-ubuntu2204
container_name: mongo_mongos
restart: always
command: mongos --configdb fates-mongo-config/config1:27019,config2:27019,config3:27019 --bind_ip 0.0.0.0 --port 27017
ports:
- 27017:27017
volumes:
- /etc/localtime:/etc/localtime
depends_on:
- config1
- config2
- config3
networks:
- mongo
networks:
mongo:
external: true

构建容器

用脚本执行,文件 mongo_deploy.sh

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
55
56
57
58
#!/bin/bash

# 检查当前用户是否是root
if [ "$(id -u)" -ne 0 ]; then
echo "此脚本需要以root用户运行,请使用sudo运行此脚本。"
exit 1
fi

# 定义一个函数 create_dir_if_not_exists
create_dir_if_not_exists() {
local dir_path="$1"
# 检查目录是否存在
if [ ! -d "$dir_path" ]; then
# 如果不存在,则创建目录
mkdir -p "$dir_path"
echo "目录 '$dir_path' 已创建。"
else
# 如果存在,则打印消息并跳过创建
echo "目录 '$dir_path' 已存在,无需创建。"
fi
}

# 使用函数创建目录
create_dir_if_not_exists /mongodata/config1
create_dir_if_not_exists /mongodata/config2
create_dir_if_not_exists /mongodata/config3

create_dir_if_not_exists /mongodata/shard1
create_dir_if_not_exists /mongodata/shard2
create_dir_if_not_exists /mongodata/shard3

# 其他用户写权限
echo "修改文件夹权限。"
chmod -R 757 /mongodata


# 启动容器
docker-compose -f docker-compose.yml up -d

echo "睡眠30s,等待mongodb所有容器构建完成"
sleep 30s


docker-compose -f docker-compose.yml exec config1 bash -c "echo 'rs.initiate({_id: \"fates-mongo-config\",configsvr: true, members: [{ _id : 0, host : \"config1:27019\" },{ _id : 1, host : \"config2:27019\" }, { _id : 2, host : \"config3:27019\" }]})' | mongosh --port 27019"

docker-compose -f docker-compose.yml exec shard1 bash -c "echo 'rs.initiate({_id: \"shard1\",members: [{ _id : 0, host : \"shard1:27018\" }]})' | mongosh --port 27018"
docker-compose -f docker-compose.yml exec shard2 bash -c "echo 'rs.initiate({_id: \"shard2\",members: [{ _id : 0, host : \"shard2:27018\" }]})' | mongosh --port 27018"
docker-compose -f docker-compose.yml exec shard3 bash -c "echo 'rs.initiate({_id: \"shard3\",members: [{ _id : 0, host : \"shard3:27018\" }]})' | mongosh --port 27018"

sleep 5s

docker-compose -f docker-compose.yml exec mongos bash -c "echo 'sh.addShard(\"shard1/shard1:27018\")' | mongosh"
sleep 1s
docker-compose -f docker-compose.yml exec mongos bash -c "echo 'sh.addShard(\"shard2/shard2:27018\")' | mongosh"
sleep 1s
docker-compose -f docker-compose.yml exec mongos bash -c "echo 'sh.addShard(\"shard3/shard3:27018\")' | mongosh"

sleep 1s

执行脚本

1
2
chmod +x mongo_deploy.sh
sudo ./mongo_deploy.sh

GridFS 分片

vvd 数据库为例

mongosh 进入 mongos 容器的 mongodb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
> mongosh
Current Mongosh Log ID: 67177c2e4b6a404e86838725
Connecting to: mongodb://127.0.0.1:27017/?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+2.2.15
Using MongoDB: 7.0.12
Using Mongosh: 2.2.15
mongosh 2.3.2 is available for download: https://www.mongodb.com/try/download/shell

For mongosh info see: https://docs.mongodb.com/mongodb-shell/

------
The server generated these startup warnings when booting
2024-10-22T18:13:59.284+08:00: Access control is not enabled for the database. Read and write access to data and configuration is unrestricted
------

[direct: mongos] test> use vvd
switched to db vvd
[direct: mongos] vvd>

启用分片,让当前库支持分片

1
sh.enableSharding("vvd")

fs.chunks 集合分片

可选创建索引 db.fs.chunks.createIndex( { files_id : 1 , n : 1 } )

1
sh.shardCollection('vvd.fs.chunks',{files_id:'hashed', n:1})

添加 GridFS 数据

需要进入到 mongos 里,我是这么干的:

1
docker exec -it mongo_mongos bash

之后为了找一个测试文件,我进到 /var/log中,把 faillog 文件当作测试文件

1
mongofiles -d myDatabase -c myGridFSBucket put /path/to/your/file.txt

这里-d指定数据库,-c指定GridFS存储桶(如果未指定,默认为fs)。

1
2
cd /var/log
mongofiles -d vvd put faillog

多执行几遍,权当测试文件

查看分片状态

回到刚刚的 mongosh,这时在 vvd 数据库中查看 collections 可以看到 fs.chunksfs.files集合,表明 GridFS 数据添加成功:

1
2
3
[direct: mongos] vvd> show collections
fs.chunks
fs.files

查看数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[direct: mongos] vvd> db.fs.files.find()
[
{
_id: ObjectId('67177fa370974ed1e8520d0b'),
length: Long('3264'),
chunkSize: 261120,
uploadDate: ISODate('2024-10-22T10:34:11.711Z'),
filename: 'faillog',
metadata: {}
},
{
_id: ObjectId('67177ff87f90b296fcac0d30'),
length: Long('3264'),
chunkSize: 261120,
uploadDate: ISODate('2024-10-22T10:35:36.665Z'),
filename: 'faillog',
metadata: {}
},
...

查看分片状态:

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
[direct: mongos] vvd> sh.status()
shardingVersion
{ _id: 1, clusterId: ObjectId('67177b0f7478e63cb9a618a5') }
---
shards
[
{
_id: 'shard1',
host: 'shard1/shard1:27018',
state: 1,
topologyTime: Timestamp({ t: 1729592126, i: 2 })
},
{
_id: 'shard2',
host: 'shard2/shard2:27018',
state: 1,
topologyTime: Timestamp({ t: 1729593951, i: 2 })
},
{
_id: 'shard3',
host: 'shard3/shard3:27018',
state: 1,
topologyTime: Timestamp({ t: 1729593958, i: 2 })
}
]
---
active mongoses
[ { '7.0.12': 1 } ]
---
autosplit
{ 'Currently enabled': 'yes' }
---
balancer
{
'Currently enabled': 'yes',
'Failed balancer rounds in last 5 attempts': 0,
'Currently running': 'no',
'Migration Results for the last 24 hours': 'No recent migrations'
}
---
databases
[
{
{
database: {
_id: 'vvd',
primary: 'shard3',
partitioned: false,
version: {
uuid: UUID('5ffbe210-fe2a-4b96-8b98-f568eab7fbec'),
timestamp: Timestamp({ t: 1729594145, i: 1 }),
lastMod: 1
}
},
collections: {
'vvd.fs.chunks': {
shardKey: { files_id: 'hashed', n: 1 },
unique: false,
balancing: true,
chunkMetadata: [
{ shard: 'shard1', nChunks: 2 },
{ shard: 'shard2', nChunks: 2 },
{ shard: 'shard3', nChunks: 2 }
],
chunks: [
{ min: { files_id: MinKey(), n: MinKey() }, max: { files_id: Long('-6148914691236517204'), n: MinKey() }, 'on shard': 'shard3', 'last modified': Timestamp({ t: 1, i: 0 }) },
{ min: { files_id: Long('-6148914691236517204'), n: MinKey() }, max: { files_id: Long('-3074457345618258602'), n: MinKey() }, 'on shard': 'shard3', 'last modified': Timestamp({ t: 1, i: 1 }) },
{ min: { files_id: Long('-3074457345618258602'), n: MinKey() }, max: { files_id: Long('0'), n: MinKey() }, 'on shard': 'shard1', 'last modified': Timestamp({ t: 1, i: 2 }) },
{ min: { files_id: Long('0'), n: MinKey() }, max: { files_id: Long('3074457345618258602'), n: MinKey() }, 'on shard': 'shard1', 'last modified': Timestamp({ t: 1, i: 3 }) },
{ min: { files_id: Long('3074457345618258602'), n: MinKey() }, max: { files_id: Long('6148914691236517204'), n: MinKey() }, 'on shard': 'shard2', 'last modified': Timestamp({ t: 1, i: 4 }) },
{ min: { files_id: Long('6148914691236517204'), n: MinKey() }, max: { files_id: MaxKey(), n: MaxKey() }, 'on shard': 'shard2', 'last modified': Timestamp({ t: 1, i: 5 }) }
],
tags: []
}
}
}
]

可以看到添加的数据被分配到三个 mongodb 中。

GridFS 分片成功!

用户管理

之后需要手动进行用户管理:

  1. 连接到 MongoDB
1
mongosh mongodb://host:27017 
  1. 创建 root 用户
1
2
3
4
5
6
7
8
use admin
db.createUser(
{
user:"root",
pwd:"passwd",
roles:["root"]
}
)
  1. 核心数据库管理者,我们的 mongodb 数据库命名为 test_db
1
2
3
4
5
6
7
8
9
10
11
12
13
db.createUser({
user:"manager",
pwd:"passwd",
customData:{
name:'vvd',
email:'zywvvd@mail.ustc.edu.cn',
age:33,
},
roles:[
{role:"readWrite",db:"test_db"},
'read'// 对其他数据库有只读权限,对 test_db 是读写权限
]
})
  1. 数据读取者,对 test_db 仅有只读权限:
1
2
3
4
5
6
7
8
9
10
11
12
db.createUser({
user:"filereader",
pwd:"passwd",
customData:{
name:'reader',
email:'reader@test.cn',
age:18,
},
roles:[
{role:"read",db:"test_db"}
]
})
  1. 开启访问控制

    mongodb 默认关闭访问控制,需要在创建用户后手动开启

    1
    docker exec -it mongo_mongos bash

    在 mongos 容器中执行命令:

    1
    mongod --auth

迁移

尝试了将本机 /mongodata 文件夹完整移动到服务器,并启动同样配置的分片服务,可以成功迁移数据库。

可能存在其他未知风险。

问题排查

  • 如果 容器启动后瞬间 Down 掉,可以通过 docker logs <container_id_or_name> 查看问题原因,我遇到的大多都是磁盘映射的权限问题,

    听说 777 大法百毒不侵

  • 如果 Mongodb 报很多奇怪的错误可以排查是不是版本有问题,6.0 以后mongo 会变成 mongosh

  • MongoDB 删除集合中的数据后不会立刻让出磁盘空间,需要调用:

    1
    2
    3
    4
    # 4.2 版本之前
    db.repairDatabase();
    # 4.2 版本之后
    db.runCommand({ compact: 'yourCollectionName' });

    如果是分片数据集,则需要进入分片 mongo 中运行命令

    删除集合也可以解决问题。

参考资料



文章链接:
https://www.zywvvd.com/notes/coding/dataset/mongodb-slice-docker/mongodb-slice-docker/


“觉得不错的话,给点打赏吧 ୧(๑•̀⌄•́๑)૭”

微信二维码

微信支付

支付宝二维码

支付宝支付

MongoDB 分片实战 GridFS
https://www.zywvvd.com/notes/coding/dataset/mongodb-slice-docker/mongodb-slice-docker/
作者
Yiwei Zhang
发布于
2024年10月22日
许可协议