概述
目前在后端开发中,对于文件存储的技术选型用Minio再合适不过,本文就来详细说明一下,在生产环境中是如何部署Minio存储服务,以及做到如下几点功能:
- 数据安全:数据防丢失,即便磁盘损坏,只要不严重都能恢复
- 数据迁移:快速方便的迁移磁盘数据
- 数据扩容:磁盘容量满了也可以快速灵活的扩容
- 数据上云:自建的存储机房成本受限,可以快速上云,比如阿里云OSS
Minio的部署有很多种方式,我们根据上面列出的几点功能来实际说明。
环境准备
部署的方式依赖于Docker/docker-compose环境,Docker可以方便我们演示多服务器的情况,不然准备多个Linux环境也太麻烦了。
所以学习本文内容前需要先掌握Docker和docker-compose的知识。
因为全部容器都在一台服务器上,所以我们先准备一个网络环境:
docker network create mynetwork
数据安全
一个简单的生产环境:单服务器双磁盘,一块磁盘做业务存储,另一块做数据备份,这是我们能接受的数据安全的最低限度,如果没有备份磁盘,那么我们的数据就是不安全的。
单服务器单磁盘
假设我们没有备份磁盘,只有一个业务存储盘,那么:
# minio0.yml
version: "3"
services:
minio0:
# 选择一个合适的版本即可
image: minio/minio:RELEASE.2021-06-17T00-10-46Z
# 网络配置
network_mode: mynetwork
# 容器名
container_name: minio0
restart: always
# 端口映射
ports:
- 9000:9000
# 路径映射
volumes:
# 同步宿主机时间
- /etc/localtime:/etc/localtime
# 映射存储路径
- /application/containers/minio0/data:/data
# 环境变量
environment:
# 登录账密
- MINIO_ROOT_USER=minio
- MINIO_ROOT_PASSWORD=minio123
# 启动命令
command: server /data
运行容器:
docker-compose -f minio0.yml up
访问:http://localhost:9000
上面是单服务器、单磁盘的部署配置,我们把数据存到了宿主机的唯一路径下。
当我们的磁盘损坏了,那么数据就会发生丢失,所以这种方式是数据不安全的。
适用于开发环境,不担心数据丢失,而且数据没有冗余,可以节省空间。
单服务器多磁盘
我们不会在生产环境搭建不安全的存储服务,所以这里我们要开始配置多磁盘:
# minio1.yml
version: "3"
services:
minio1:
image: minio/minio:RELEASE.2021-06-17T00-10-46Z
network_mode: mynetwork
container_name: minio1
restart: always
ports:
- 9001:9000
volumes:
- /etc/localtime:/etc/localtime
# 这里模拟了4块磁盘
- /application/containers/minio1/data1:/data1
- /application/containers/minio1/data2:/data2
- /application/containers/minio1/data3:/data3
- /application/containers/minio1/data4:/data4
environment:
- MINIO_ROOT_USER=minio
- MINIO_ROOT_PASSWORD=minio123
# 启动命令,/data{1...4}是便捷写法,意思是:/data1 /data2 /data3 /data4
command: server /data{1...4}
需要注意的是,Minio的数据安全是依靠纠删码来保证的,Minion要求纠删码至少需要4块磁盘才能使用,否则服务是跑不起来的。
数据安全验证
纠删码后面会说。默认的纠删码机制,假设磁盘数量为N,则会分成N/2块数据,N/2块奇偶校验码,在上面的例子中,我们有四块磁盘,那么纠删码机制能保证:
- 丢失一块磁盘,数据可读、可写、可恢复
- 丢失两块磁盘,数据可读、不可写、可恢复
- 丢失三块磁盘,那么数据就真丢了。
数据扩容
Minio的纠删码机制,决定了Minio不允许动态扩容,所以我们加磁盘是没用的。
但是有了mc数据恢复命令,我们可以通过升级某一磁盘来曲线救国,变相实现扩容:
这样我们也实现了扩容。
MC命令来恢复数据
我们需要用到Minio的mc工具,来模拟数据丢失后如何处理:
# 启动一个mc程序
docker run -it --network mynetwork --entrypoint=/bin/sh minio/mc
# 添加minio服务进来,并且起一个别名
# 格式:mc config host add
mc config host add minio1 http://minio1:9000 minio minio123
# 查看host列表,能看到我们刚刚添加进来的:minio1
mc config host list
# 查看桶列表
# 格式:mc ls
mc ls minio1
# 创建桶
# mc ls /
mc mb minio1/demo
# 删除桶
# mc rb /
mc rb minio/test
# 监听变化:别名/桶名
# mc watch /
mc watch minio/test
# 查找文件和对象
# mc find / --name ""
mc find minio/test --name "*.xml"
# 查看桶内文件列表
# 格式:mc ls /
mc ls minio1/demo
# 创建几个测试文件
echo "test" > a.txt
echo "test" > b.txt
echo "test" > c.txt
# 拷贝到指定桶
mc cp a.txt minio1/demo
/a.txt: 5 B / 5 B ━━
# 加上--recursive表示递归
mc cp --recursive minio/test minio/demo
# 以上操作可以在Minio页面上进行,效果相同
# 模拟数据丢失:把/data{1...4}这四个路径,删除任意一个,剩下三个
# 数据仍然可读
mc ls minio1/demo
[2023-09-01 06:32:51 UTC] 0B STANDARD a.txt
# 数据仍然可写
mc cp b.txt minio1/demo
/b.txt: 5 B / 5 B ━━
# 现在再删除一个路径,还剩两个
# 数据还是可读
mc ls minio1/demo
[2023-09-01 06:32:51 UTC] 0B STANDARD a.txt
[2023-09-01 06:37:24 UTC] 0B STANDARD b.txt
# 数据不可写了
mc cp c.txt minio1/demo
# 恢复数据
mc admin heal -r minio1
# 恢复数据需要重启Minio容器
docker restart minio1
# 再次检查,刚刚删除的两个路径又恢复了
多服务器多磁盘
现在生活富裕了,有条件能上两台服务器了,我们可以跑两个Minio服务做集群,互相分担存储任务:
# minio-multi.yml
version: "3"
services:
minio1:
image: minio/minio:RELEASE.2021-06-17T00-10-46Z
network_mode: mynetwork
container_name: minio1
restart: always
ports:
- 9001:9000
volumes:
- /etc/localtime:/etc/localtime
# 至少是4块磁盘
- /application/containers/minio1/data1:/data1
- /application/containers/minio1/data2:/data2
- /application/containers/minio1/data3:/data3
- /application/containers/minio1/data4:/data4
environment:
- MINIO_ROOT_USER=minio
- MINIO_ROOT_PASSWORD=minio123
# 启动命令
command: server http://minio1/data{1...4} http://minio2/data{1...6}
minio2:
image: minio/minio:RELEASE.2021-06-17T00-10-46Z
network_mode: mynetwork
container_name: minio2
restart: always
ports:
- 9002:9000
volumes:
- /etc/localtime:/etc/localtime
# 至少4块磁盘,所以6块盘也是可以的
- /application/containers/minio2/data1:/data1
- /application/containers/minio2/data2:/data2
- /application/containers/minio2/data3:/data3
- /application/containers/minio2/data4:/data4
- /application/containers/minio2/data5:/data5
- /application/containers/minio2/data6:/data6
environment:
- MINIO_ROOT_USER=minio
- MINIO_ROOT_PASSWORD=minio123
command: server http://minio1/data{1...4} http://minio2/data{1...6}
现在我们有两种情况:
数据迁移
还是用mc命令,通过mc mirror可以实现数据的迁移,很简单,假设本来跑了一个Minio服务是minio0,现在多服务器跑了两个服务:minio1和minio2,因为minio1和minio2已经做了集群,所以数据传给minio1还是minio2都是一样的。
我们来说明一下数据的迁移:
# 启动一个mc程序
docker run -it --network mynetwork --entrypoint=/bin/sh minio/mc
# 旧Minio服务
mc config host add minio0 http://minio0:9000 minio minio123
# 注意:因为有两个服务,所以使用mc的时候也记得添加两个:
mc config host add minio1 http://minio1:9000 minio minio123
mc config host add minio2 http://minio2:9000 minio minio123
# 全量迁移
# 格式:mc mirror
mc mirror minio0 minio1
# 指定桶迁移,需要目标桶已存在,不存在会报错
mc mirror minio0/demo minio1
这样我们就实现了数据的迁移,迁移完后删除旧服务即可。
数据扩容
一般来说,数据扩容分垂直扩容和水平扩容:
垂直扩容指给服务添加磁盘,增加单体服务的存储容量,Minio因为纠删码机制,并不支持垂直扩容。
所以我们只能用水平扩容,Minio支持两种扩容方式:对等扩容和联邦扩容,这两者各有优缺点:
对等扩容
假设现有minio集群1,有两个服务minio1和minio2,各自的磁盘数量都是4块,那么扩容的新集群也必须要是4块磁盘,或者是4的倍数,比如8块、16块。
这种方式扩容的好处是扩容效果很理想,新增的集群可以自动添加进来分担数据的存储压力,原来的业务该怎么跑就继续怎么跑。
缺点很明显,就是扩容方式不够灵活,必须的原来集群的相同配置,不能说原来的集群磁盘数量是4,新加入的集群磁盘数量是5,这是因为纠删码机制导致的。并且需要重启服务才能生效,不能做到灰度发布。
其实数据迁移也是变相的扩容,并且数据迁移可以解决上面提到的扩容方式不灵活的问题,因为我们可以重构一个新的集群,用我们希望的磁盘配置,然后迁移过来的数据也会自动进行纠删码的分块存储,这样可以使扩容稍微灵活一些。
现在来实战对等扩容,一开始我们配置了一个minio集群:
# minio-cluster-01.yml
version: "3"
services:
minio1:
image: minio/minio:RELEASE.2021-06-17T00-10-46Z
network_mode: mynetwork
container_name: minio1
restart: always
ports:
- 9001:9000
volumes:
- /etc/localtime:/etc/localtime
- /application/containers/minio1/data1:/data1
- /application/containers/minio1/data2:/data2
- /application/containers/minio1/data3:/data3
- /application/containers/minio1/data4:/data4
environment:
- MINIO_ROOT_USER=minio
- MINIO_ROOT_PASSWORD=minio123
command: server http://minio{1...2}/data{1...4}
minio2:
image: minio/minio:RELEASE.2021-06-17T00-10-46Z
network_mode: mynetwork
container_name: minio2
restart: always
ports:
- 9002:9000
volumes:
- /etc/localtime:/etc/localtime
- /application/containers/minio2/data1:/data1
- /application/containers/minio2/data2:/data2
- /application/containers/minio2/data3:/data3
- /application/containers/minio2/data4:/data4
environment:
- MINIO_ROOT_USER=minio
- MINIO_ROOT_PASSWORD=minio123
command: server http://minio{1...2}/data{1...4}
启动成功后访问任意一个服务:http://localhost:9001
创建一个桶:demo
上传一些文件...
现在可以在minio1和minio2上面看到上传的数据,假设现在磁盘满了,我们在新服务器上又搭建了一个集群:
# minio-cluster-02.yml
version: "3"
services:
minio3:
image: minio/minio:RELEASE.2021-06-17T00-10-46Z
network_mode: mynetwork
container_name: minio3
restart: always
ports:
- 9003:9000
volumes:
- /etc/localtime:/etc/localtime
- /application/containers/minio3/data1:/data1
- /application/containers/minio3/data2:/data2
- /application/containers/minio3/data3:/data3
- /application/containers/minio3/data4:/data4
- /application/containers/minio3/data5:/data5
- /application/containers/minio3/data6:/data6
- /application/containers/minio3/data7:/data7
- /application/containers/minio3/data8:/data8
environment:
- MINIO_ROOT_USER=minio
- MINIO_ROOT_PASSWORD=minio123
# 4块磁盘就这样写
# command: server http://minio{1...2}/data{1...4} http://minio{3...4}/data{1...4}
# 8块磁盘就这样写
command: server http://minio{1...2}/data{1...4} http://minio{3...4}/data{1...8}
minio4:
image: minio/minio:RELEASE.2021-06-17T00-10-46Z
network_mode: mynetwork
container_name: minio4
restart: always
ports:
- 9004:9000
volumes:
- /etc/localtime:/etc/localtime
- /application/containers/minio4/data1:/data1
- /application/containers/minio4/data2:/data2
- /application/containers/minio4/data3:/data3
- /application/containers/minio4/data4:/data4
- /application/containers/minio4/data5:/data5
- /application/containers/minio4/data6:/data6
- /application/containers/minio4/data7:/data7
- /application/containers/minio4/data8:/data8
environment:
- MINIO_ROOT_USER=minio
- MINIO_ROOT_PASSWORD=minio123
command: server http://minio{1...2}/data{1...4} http://minio{3...4}/data{1...8}
对等扩容十分简单,只要一行命令:
# # minio-cluster-02.yml
command: server http://minio{1...2}/data{1...4} http://minio{3...4}/data{1...8}
前面这段:
http://minio{1...2}/data{1...4}
表示第一个集群的minio1到minio2服务,磁盘是1到4,
第二段:
http://minio{3...4}/data{1...8}
表示第二个集群的minio3到minio4服务,磁盘是1到8,也就是集群一的倍数,记住一定要是倍数,比如4、8。
我们别忘了修改集群一的配置里的command命令,改成一样的:
# minio-cluster-01.yml
command: server http://minio{1...2}/data{1...4} http://minio{3...4}/data{1...8}
重启集群一使修改的配置生效,现在访问minio3或者minio4:
- 可以看到刚刚上传的数据,说明读集群一数据成功
- 上传一些新的数据,可以在minio3的磁盘看到存储,表示加入集群扩容成功
联邦扩容
对等扩容有两个大问题:
联邦扩容能解决上面的两个问题,他引入了新的复杂度:etcd中间件,一个KV数据库,三个etcd可以做一个集群,每一个Minio集群只需要连接上这个etcd集群,就能通过etcd做桥梁彼此连接起来,就好像微服务中的注册中心。
每一个新连接进来的Minio集群,不需要旧集群重启服务,只要连接成功etcd集群,扩容就完成了,由此解决了第一个不能灰度发布的问题。
每一个Minio集群也不需要特定的磁盘配置,只要部署完成都可以添加进来,由此解决第二个扩容方式不灵活的问题。
同样来实战一下,既然引入了新的中间件,首先当然是先把etcd集群搭起来:
# etcd.yml
x-variables:
flag_initial_cluster_token: &flag_initial_cluster_token '--initial-cluster-token=mys3cr3ttok3n'
common_settings: &common_settings
image: quay.io/coreos/etcd:v3.4.27
network_mode: mynetwork
entrypoint: /usr/local/bin/etcd
restart: always
ports:
- 2379
services:
etcd-1: