Minio生产环境实战经验分享

2023年 9月 2日 81.3k 0

概述

目前在后端开发中,对于文件存储的技术选型用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数据恢复命令,我们可以通过升级某一磁盘来曲线救国,变相实现扩容:

  • 移除某一个磁盘,不担心,数据仍然可读可写
  • 更换一个更大的磁盘,然后做数据恢复
  • 重启Minio服务
  • 这样我们也实现了扩容。

    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的磁盘看到存储,表示加入集群扩容成功

    联邦扩容

    对等扩容有两个大问题:

  • 不能灰度发布,扩容需要重启所有集群,意味着系统业务会因此暂停
  • 扩容方式不灵活,假如我一开始部署的集群很庞大,有10个Minio服务,每个服务50个磁盘,那么扩容的时候也必须是50的倍数
  • 联邦扩容能解决上面的两个问题,他引入了新的复杂度: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:

    相关文章

    服务器端口转发,带你了解服务器端口转发
    服务器开放端口,服务器开放端口的步骤
    产品推荐:7月受欢迎AI容器镜像来了,有Qwen系列大模型镜像
    如何使用 WinGet 下载 Microsoft Store 应用
    百度搜索:蓝易云 – 熟悉ubuntu apt-get命令详解
    百度搜索:蓝易云 – 域名解析成功但ping不通解决方案

    发布评论