一、MinIO介绍

1、文件系统应用场景

对象存储服务(Object Storage Service,OSS)是一种海量、安全、低成本、高可靠的云存储服
务,适合存放任意类型的文件。容量和处理能力弹性扩展,多种存储类型供选择,全面优化存储成

互联网海量非结构化数据的存储需求

  • 电商网站:海量商品图片

  • 视频网站:海量视频文件

  • 网盘 : 海量文件

  • 社交网站:海量图片

参考:MinIO分布式文件服务器搭建与入门

2、MinIO介绍

MinIO 是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。

MinIO是一个非常轻量的服务,可以很简单的和其他应用的结合,类似 NodeJS, Redis 或者 MySQL。

文档地址

3、MinIO优点

  • 部署简单: 一个single二进制文件即是一切,还可支持各种平台

  • minio支持海量存储,可按zone扩展(原zone不受任何影响),支持单个对象最大5TB

  • 兼容Amazon S3接口,充分考虑开发人员的需求和体验

  • 低冗余且磁盘损坏高容忍,标准且最高的数据冗余系数为2(即存储一个1M的数据对象,实际占用

    磁盘空间为2M)。但在任意n/2块disk损坏的情况下依然可以读出数据(n为一个纠删码集合(Erasure

    Coding Set)中的disk数量)。并且这种损坏恢复是基于单个对象的,而不是基于整个存储卷的

  • 读写性能优异
    在这里插入图片描述

4、MinIO的基础概念

  • Object:存储到 Minio 的基本对象,如文件、字节流,Anything…

  • Bucket:用来存储 Object 的逻辑空间。每个 Bucket 之间的数据是相互隔离的。对于客户端而

    言,就相当于一个存放文件的顶层文件夹。

  • Drive:即存储数据的磁盘,在 MinIO 启动时,以参数的方式传入。Minio 中所有的对象数据都会

    存储在 Drive 里。

  • Set :即一组 Drive 的集合,分布式部署根据集群规模自动划分一个或多个 Set ,每个 Set 中的

    Drive 分布在不同位置。一个对象存储在一个 Set 上。(For example: {1…64} is divided into 4

    sets each of size 16.)

    • 一个对象存储在一个Set上

    • 一个集群划分为多个Set

    • 一个Set包含的Drive数量是固定的,默认由系统根据集群规模自动计算得出

    • 一个SET中的Drive尽可能分布在不同的节点上

5、纠删码EC(Erasure Code)

MinIO 使用纠删码机制来保证高可靠性,使用 highwayhash 来处理数据损坏( Bit Rot Protection )

关于纠删码,简单来说就是可以通过数学计算,把丢失的数据进行还原,它可以将n份原始数据,增加m份数据,并能通过n+m份中的任意n份数据,还原为原始数据。即如果有任意小于等于m份的数据失效,仍然能通过剩下的数据还原出来。

6、存储形式

文件对象上传到 MinIO ,会在对应的数据存储磁盘中,以 Bucket 名称为目录,文件名称为下一级目
录,文件名下是 part.1 和 xl.meta(老版本,最新版本如下图),前者是编码数据块及检验块,后者是元数据文件。

在这里插入图片描述

7、存储方案

在这里插入图片描述

二、Minio环境搭建

1、介绍

官方文档:https://docs.min.io/docs/
中文文档:http://docs.minio.org.cn/docs/ (没有及时更新,容易被坑)

minio支持多种server启动模式

  • 单机模式(无EC码)

  • 单机模式(有EC码)

  • 集群且有EC码

2、单机部署

2.1 单机部署

minio server的standalone模式,即要管理的磁盘都在host本地。该启动模式一般仅用于实验环境、测试环境的验证和学习使用。在standalone模式下,还可以分为non-erasure code modeerasure code

  • modenon-erasure code mode

    在此启动模式下,对于每一份对象数据,minio直接在data下面存储这份数据,不会建立副本,也不会启用纠删码机制。因此,这种模式无论是服务实例还是磁盘都是“单点”,无任何高可用保障,磁盘损坏就表示数据丢失

  • erasure code mode此模式为minio server实例传入多个本地磁盘参数。一旦遇到多于一个磁盘参数,minio server会自动启用erasure code mode。erasure code对磁盘的个数是有要求的,如不满足要求,实例启动将失败。 erasure code启用后,要求传给minio server的endpoint(standalone模式下,即本地磁盘上的目录)至少为4个

2.2 基于Linux部署

## 国外资源,龟速下载
wget https://dl.min.io/server/minio/release/linux-amd64/minio 
chmod +x minio
export MINIO_ROOT_USER=admin
export MINIO_ROOT_PASSWORD=12345678

# 默认的配置目录是${HOME}/.minio,可以通过–config-dir命令自定义配置目录
# ./minio server --config-dir /mnt/config /mnt/data
## minio 存储目录;--console-address 是 UI 界面的端口
./minio server --console-address ":9001" /data

2.3 基于docker部署(推荐)

docker pull minio/minio
# --console-address 是 UI 界面的端口
# --console-address 是 UI 界面的端口
docker run --name minio -p 9000:9000 -p 9001:9001 \
-d --restart=always -e "MINIO_ACCESS_KEY=admin" -e "MINIO_SECRET_KEY=password" \
-v ~/minio/data:/data -v ~/minio/config:/root/.minio \
minio/minio server  --console-address ":9001" /data

非root启动,MINIO_ROOT_USER是新版本的变量定义,MINIO_ACCESS_KEY是老版本

docker run \
-p 9000:9000 \
-p 9001:9001 \
--user $(id -u):$(id -g) \
--name minio \
-e "MINIO_ROOT_USER=admin" \
-e "MINIO_ROOT_PASSWORD=password" \
-v ${HOME}/minio/data:/data \
quay.io/minio/minio server /data --console-address ":9001"

3、minio 纠删码模式

Minio 使用纠删码erasure code和校验和checksum来保护数据免受硬件故障和无声数据损坏。 即便您丢失一半数量(N/2)的硬盘,您仍然可以恢复数据。

纠删码是一种恢复丢失和损坏数据的数学算法, Minio 采用 Reed-Solomon code 将对象拆分成 N/2 数据和 N/2 奇偶校验块。 这就意味着如果是 12 块盘,一个对象会被分成 6 个数据块、6 个奇偶校验块,你可以丢失任意 6 块盘(不管其是存放的数据块还是奇偶校验块),你仍可以从剩下的盘中的数据进行恢复

使用 Minio Docker 镜像,在 8 块盘中启动 Minio 服务,默认账号密码是minioadmin/minioadmin

docker run -d -p 9000:9000 -p 9001:9001 --name minio \
  -v /mnt/data1:/data1 \
  -v /mnt/data2:/data2 \
  -v /mnt/data3:/data3 \
  -v /mnt/data4:/data4 \
  -v /mnt/data5:/data5 \
  -v /mnt/data6:/data6 \
  -v /mnt/data7:/data7 \
  -v /mnt/data8:/data8 \
  minio/minio server /data{1...8} --console-address ":9001" 

4、分布式集群搭建

4.1 介绍

分布式 Minio 可以让你将多块硬盘(甚至在不同的机器上)组成一个对象存储服务。由于硬盘分布在不同的节点上,分布式 Minio 避免了单点故障

在这里插入图片描述

4.2 分布式 Minio 优势

  • 数据保护

    分布式 Minio 采用 纠删码来防范多个节点宕机和位衰减bit rot

    分布式 Minio 至少需要 4 个硬盘,使用分布式 Minio 自动引入了纠删码功能

  • 高可用

    单机 Minio 服务存在单点故障,相反,如果是一个有 N 块硬盘的分布式 Minio,只要有 N/2 硬盘在线,你的数据就是安全的。不过你需要至少有 N/2+1 个硬盘来创建新的对象

    例如,一个 16 节点的 Minio 集群,每个节点 16 块硬盘,就算 8 台服务器宕机,这个集群仍然是可读的,不过你需要 9 台服务器才能写数据

  • 一致性

    Minio 在分布式和单机模式下,所有读写操作都严格遵守 read-after-write 一致性模型

4.3 Linux运行分布式 Minio

启动一个分布式 Minio 实例,你只需要把硬盘位置做为参数传给 minio server 命令即可(这里必须要四块及以上硬盘,模拟路径会报错),然后你需要在所有其它节点运行同样的命令。

可以参考官网:https://docs.min.io/docs/distributed-minio-quickstart-guide.html

  • 分布式 Minio 里所有的节点需要有同样的 access 秘钥和 secret 秘钥,这样这些节点才能建立联接。为了实现这个,你需要在执行 minio server 命令之前,先将 access 秘钥和 secret 秘钥 export 成环境变量。新版本使用 MINIO_ROOT_USER&MINIO_ROOT_PASSWORD

  • 分布式 Minio 使用的磁盘里必须是干净的,里面没有数据

  • 分布式 Minio 里的节点时间差不能超过 3 秒,可以使用NTP 来保证时间一致

  • 在 Windows 下运行分布式 Minio 处于实验阶段

# 下载minio
wget https://dl.min.io/server/minio/release/linux-amd64/minio
chmod +x minio
sudo cp minio /data/minio/run/minio
sudo mv minio /usr/bin/minio

# 准备好四块硬盘并挂载其对应的/data/minio/data目录下,创建目录
# run:启动脚本及二进制文件目录;
# data:数据存储目录;
# /etc/minio:配置文件目录;
mkdir -p /data/minio/{run,data} && mkdir -p /etc/minio

# 集群启动文件,vim /data/minio/run/run.sh

#!/bin/bash
export MINIO_ROOT_USER=admin
export MINIO_ROOT_PASSWORD=password
# –config-dir:指定集群配置文件目
/data/minio/run/minio server --address ":9000" --console-address ":9001" --config-dir /etc/minio \
http://192.168.99.1/data/minio/data \
http://192.168.99.2/data/minio/data \
http://192.168.99.3/data/minio/data \
http://192.168.99.4/data/minio/data

配置为系统服务vim /usr/lib/systemd/system/minio.service

[Unit]
Description=Minio service
Documentation=https://docs.minio.io/
 
[Service]
WorkingDirectory=/data/minio/run/
ExecStart=/data/minio/run/run.sh
 
Restart=on-failure
RestartSec=5
 
[Install]
WantedBy=multi-user.target

移动可执行文件以及启动

# 给所有涉及到的文件或目录添加权限
chmod +x /data/minio/run/minio && chmod +x /data/minio/run/run.sh
# 启动
systemctl daemon-reload
systemctl enable minio && systemctl start minio

最后生产环境需要使用Nginx将集群地址进行代理,对外统一入口,可以参考下面docker的nginx配置

upstream minio{
        server 192.168.99.1:9000;
        server 192.168.99.2:9000;
        server 192.168.99.3:9000;
        server 192.168.99.4:9000;
}
upstream console {
        ip_hash;
        server minio1:9001;
        server minio2:9001;
        server minio3:9001;
        server minio4:9001;
    }
server {
        listen 9000;
        server_name localhost;
        location / {
                proxy_pass http://minio;
                proxy_set_header Host $http_host;
                client_max_body_size 1000m;
        }
}
server {
        listen 9001;
        server_name localhost;
        location / {
                proxy_pass http://console;
                proxy_set_header Host $http_host;
                client_max_body_size 1000m;
        }
}

4.4 Docker Compose 部署 MinIO

参考:https://docs.min.io/docs/deploy-minio-on-docker-compose.html

要在 Docker Compose 上部署分布式 MinIO,请下载docker-compose.yamlnginx.conf到你当前的工作目录

vim docker-compose.yaml

version: '3.7'

# https://docs.docker.com/compose/compose-file/compose-file-v3/
# Settings and configurations that are common for all containers
x-minio-common: &minio-common
  image: quay.io/minio/minio:RELEASE.2022-09-07T22-25-02Z
  command: server --console-address ":9001" http://minio{1...4}/data{1...2}
  expose:
    - "9000"
    - "9001"
  # environment:
    # MINIO_ROOT_USER: minioadmin
    # MINIO_ROOT_PASSWORD: minioadmin
  healthcheck:
    test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
    interval: 30s
    timeout: 20s
    retries: 3

# starts 4 docker containers running minio server instances.
# using nginx reverse proxy, load balancing, you can access
# it through port 9000.
services:
  minio1:
    <<: *minio-common
    hostname: minio1
    volumes:
      - data1-1:/data1
      - data1-2:/data2

  minio2:
    <<: *minio-common
    hostname: minio2
    volumes:
      - data2-1:/data1
      - data2-2:/data2

  minio3:
    <<: *minio-common
    hostname: minio3
    volumes:
      - data3-1:/data1
      - data3-2:/data2

  minio4:
    <<: *minio-common
    hostname: minio4
    volumes:
      - data4-1:/data1
      - data4-2:/data2

  nginx:
    image: nginx:1.19.2-alpine
    hostname: nginx
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    ports:
      - "9000:9000"
      - "9001:9001"
    depends_on:
      - minio1
      - minio2
      - minio3
      - minio4

## By default this config uses default local driver,
## For custom volumes replace with volume driver configuration.
volumes:
  data1-1:
  data1-2:
  data2-1:
  data2-2:
  data3-1:
  data3-2:
  data4-1:
  data4-2:

vim nginx.conf

user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  4096;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;
    sendfile        on;
    keepalive_timeout  65;

    # include /etc/nginx/conf.d/*.conf;

    upstream minio {
        server minio1:9000;
        server minio2:9000;
        server minio3:9000;
        server minio4:9000;
    }

    upstream console {
        ip_hash;
        server minio1:9001;
        server minio2:9001;
        server minio3:9001;
        server minio4:9001;
    }

    server {
        listen       9000;
        listen  [::]:9000;
        server_name  localhost;

        # To allow special characters in headers
        ignore_invalid_headers off;
        # Allow any size file to be uploaded.
        # Set to a value such as 1000m; to restrict file size to a specific value
        client_max_body_size 0;
        # To disable buffering
        proxy_buffering off;
        proxy_request_buffering off;

        location / {
            proxy_set_header Host $http_host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;

            proxy_connect_timeout 300;
            # Default is HTTP/1, keepalive is only enabled in HTTP/1.1
            proxy_http_version 1.1;
            proxy_set_header Connection "";
            chunked_transfer_encoding off;

            proxy_pass http://minio;
        }
    }

    server {
        listen       9001;
        listen  [::]:9001;
        server_name  localhost;

        # To allow special characters in headers
        ignore_invalid_headers off;
        # Allow any size file to be uploaded.
        # Set to a value such as 1000m; to restrict file size to a specific value
        client_max_body_size 0;
        # To disable buffering
        proxy_buffering off;
        proxy_request_buffering off;

        location / {
            proxy_set_header Host $http_host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header X-NginX-Proxy true;

            # This is necessary to pass the correct IP to be hashed
            real_ip_header X-Real-IP;

            proxy_connect_timeout 300;
            
            # To support websocket
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            
            chunked_transfer_encoding off;

            proxy_pass http://console;
        }
    }
}

最后docker-compose pulldocker-compose up启动

4.5 扩展现有的分布式集群

例如我们是通过区的方式启动 MinIO 集群,命令行如下

export MINIO_ROOT_USER=admin
export MINIO_ROOT_PASSWORD=12345678
minio server http://host{1...32}/export{1...32}

MinIO 支持通过命令,指定新的集群来扩展现有集群(纠删码模式),命令行如下:

export MINIO_ROOT_USER=admin
export MINIO_ROOT_PASSWORD=12345678
minio server http://host{1...32}/export{1...32} http://host{33...64}/export{1...32}

现在整个集群就扩展了 1024 个磁盘,总磁盘变为 2048 个,新的对象上传请求会自动分配到最少使用的集群上。通过以上扩展策略,您就可以按需扩展您的集群。重新配置后重启集群,会立即在集群中生效,并对现有集群无影响。如上命令中,我们可以把原来的集群看做一个区,新增集群看做另一个区,新对象按每个区域中的可用空间比例放置在区域中。在每个区域内,基于确定性哈希算法确定位置。

说明: 您添加的每个区域必须具有与原始区域相同的磁盘数量(纠删码集)大小,以便维持相同的数据冗余 SLA。 例如,第一个区有 8 个磁盘,您可以将集群扩展为 16 个、32 个或 1024 个磁盘的区域,您只需确保部署的 SLA 是原始区域的倍数即可。

三、Minio 客户端使用

1、介绍

参考:https://docs.min.io/docs/minio-client-quickstart-guide.html

MinIO Client (mc)为 ls,cat,cp,mirror,diff,find 等 UNIX 命令提供了一种替代方案。它支持文件系统和兼容 Amazon S3 的云存储服务(AWS Signature v2 和 v4)

mc客户端的安装

wget https://dl.min.io/client/mc/release/linux-amd64/mc
chmod +x mc
./mc --help
sudo mv mc /usr/local/sbin/

mc全部命令

ls       列出文件和文件夹。
mb       创建一个存储桶或一个文件夹。
cat      显示文件和对象内容。
pipe     将一个STDIN重定向到一个对象或者文件或者STDOUT。
share    生成用于共享的URL。
cp       拷贝文件和对象。
mirror   给存储桶和文件夹做镜像。
find     基于参数查找文件。
diff     对两个文件夹或者存储桶比较差异。
rm       删除文件和对象。
events   管理对象通知。
watch    监视文件和对象的事件。
policy   管理访问策略。
config   管理mc配置文件。
update   检查软件更新。
version  输出版本信息。

2、mc配置与基本操作

mc将所有的配置信息都存储在~/.mc/config.json文件中

# 查询mc host配置
mc config host ls
# 添加minio服务    
mc config host add minio-server http://192.168.249.131:9000 minioadmin minioadmin
# 删除host
mc config host remove minio-server
# 查询minio服务上的所有buckets(文件和文件夹)
mc ls minio-server
# 下载文件
mc cp minio-server/fox.jpg  /tmp/ 
#删除文件
mc rm minio-server/fox/fox.jpg
#上传文件
mc cp zookeeper.out minio-server/fox/
#==================桶的管理==================
# 创建一个新的桶
mc mb minio-server/bucket01
# 删除bucket
mc rb minio-server/bucket02
# bucket不为空,可以强制删除(慎用)
mc rb --force minio-server/bucket01
# 查询bucket03磁盘使用情况
mc du minio-server/bucket03
# =====================数据迁移==================
mc alias set minio1 http://172.20.10.2:9000 adminminio adminminio
mc alias set minio2 http://172.20.10.3:9000 adminminio adminminio
# mc alias set 名称 服务地址 用户名 密码
#全量迁移,重名文件不覆盖,bucket不存在会自动创建
mc mirror minio1 minio2
#只是迁移某个bucket,以test为例,目标的bucket需要提前建好
mc mirror minio1/test minio2/test
#覆盖重名文件,加--overwrite
mc mirror --overwrite minio1/test minio2/test
# 将本地文件夹递归镜像到Amazon S3云存储,并保留所有本地文件属性
mc mirror -a backup/ s3/archive

3、mc admin 使用

3.1 基本介绍

minIO Client(mc)提供了“ admin”子命令来对您的 MinIO 部署执行管理任务

service     服务重启并停止所有MinIO服务器
update      更新更新所有MinIO服务器
info        信息显示MinIO服务器信息
user        用户管理用户
group       小组管理小组
policy      MinIO服务器中定义的策略管理策略
config      配置管理MinIO服务器配置
heal        修复MinIO服务器上的磁盘,存储桶和对象
profile     概要文件生成概要文件数据以进行调试
top         顶部提供MinIO的顶部统计信息
trace       跟踪显示MinIO服务器的http跟踪
console     控制台显示MinIO服务器的控制台日志
prometheus  Prometheus管理Prometheus配置
kms         kms执行KMS管理操作

3.2 用户管理

mc admin user --help
#新建用户
mc admin user add minio-server fox
mc admin user add minio-server fox02 12345678
#查看用户
mc admin user list minio-server
#禁用用户
mc admin user disable minio-server fox02
#启用用户
mc admin user enable minio-server fox02
#查看用户信息
mc admin user info minio-server fox
#删除用户
mc admin user remove minio-server fox02

3.3 策略管理

policy 命令,用于添加,删除,列出策略,获取有关策略的信息并为 MinIO 服务器上的用户设置策略(创建用户之后还不能进行登录,必须绑定对应的策略)

mc admin policy --help
#列出MinIO上的所有固定策略
mc admin policy list minio-server
# 查看plicy信息
mc admin policy info minio-server readwrite
# 为用户添加策略
mc admin policy set minio-server readwrite user=fox02
# 取消策略
mc admin policy unset minio-server readwrite user=fox02

3.4 添加用户权限自定义策略

AWS Identity and Access Management (IAM) 是一种 Web 服务,可以帮助您安全地控制对 AWS 资源的访问。您可以使用 IAM 控制对哪个用户进行身份验证 (登录) 和授权 (具有权限) 以使用资源。

权限解释
consoleAdmin控制台管理员
diagnostics诊断
readonly只读
readwrite读写
writeonly只写

编写自己的自定义策略vim mytest.json,这里只能操作test桶,做到了资源隔离

{
 "Version": "2012-10-17",
 "Statement": [
  {
   "Effect": "Allow",
   "Action": [
                "s3:GetObject",
                "s3:ListAllMyBuckets",
                "s3:ListBucket",
                "s3:PutObject",
                "s3:DeleteObject",
                "s3:GetBucketLocation"
            ],
   "Resource": [
    "arn:aws:s3:::test/*"
   ]
  }
 ]
}
参数说明
Version标识策略的版本号,Minio中一般为"2012-10-17"
Statement策略授权语句,描述策略的详细信息,包含Effect(效果)、Action(动作)、Principal(用户)、Resource(资源)和Condition(条件)。其中Condition为可选
EffectEffect(效果)作用包含两种:Allow(允许)和Deny(拒绝),系统预置策略仅包含允许的授权语句,自定义策略中可以同时包含允许和拒绝的授权语句,当策略中既有允许又有拒绝的授权语句时,遵循Deny优先的原则
ActionAction(动作)对资源的具体操作权限,格式为:服务名:资源类型:操作,支持单个或多个操作权限,支持通配符号*,通配符号表示所有。例如 s3:GetObject ,表示获取对象
ResourceResource(资源)策略所作用的资源,支持通配符号*,通配符号表示所有。在JSON视图中,不带Resource表示对所有资源生效。Resource支持以下字符:-_0-9a-zA-Z*./\,如果Resource中包含不支持的字符,请采用通配符号*。例如:arn:aws:s3:::my-bucketname/myobject*\,表示minio中my-bucketname/myobject目录下所有对象文件
ConditionCondition(条件)您可以在创建自定义策略时,通过Condition元素来控制策略何时生效。Condition包括条件键和运算符,条件键表示策略语句的Condition元素,分为全局级条件键和服务级条件键。全局级条件键(前缀为g:)适用于所有操作,服务级条件键(前缀为服务缩写,如obs:)仅适用于对应服务的操作。运算符与条件键一起使用,构成完整的条件判断语句

最后把权限给与用户,发现该用户只能操作对应的桶,实现权限分离。同时在Web界面也可以快速编辑授权用户

# 添加新的策略
mc admin policy add minio-server mypolicy mytest.json
mc admin policy list minio-server
# 创建用户
mc admin user add minio-server fox03 12345678
# 设置用户的访问策略
mc admin policy set minio-server mypolicy user=fox03

4、桶策略(匿名访问策略)

4.1 桶策略简介

桶策略和IAM用户权限不同,用户权限管理的是用户的登录/可读/可写等操作,桶策略管理的是匿名用户的访问与上传,也可以自定义操作。
参考文章:Minio 小技巧 | 通过编码设置桶策略,实现永久访问和下载

mc的命令行客户端,在mc种对桶策略设置对应分别是 none, download, upload, public ,与minio的匿名访问策略一一对应:

  • download(read only )允许未经认证的访问

  • upload (write only)允许未经认证的上传

  • public(read+write)允许读写

  • none(none) 默认值,必须经过认证

#更改桶权限为public(简单操作,对桶进行全局操作,无论桶中文件名前缀是什么),也可以对其中一个路径
mc policy set public minio-server/test
# 设置只读
mc policy set download minio-server/test
# 只上传
mc policy set upload minio-server/test
# 自定义桶策略
mc policy set-json /path/to/policy.json minio-server/test/images
# 获取桶策略
mc policy get minio-server/test
# 获取桶策略的json
mc policy get-json minio-server/test
# 列出当前的策略
mc policy list minio-server/test

4.2 自定义桶策略

  • Version:版本

  • Actions– 对于每个资源,Amazon S3 支持一组操作。您可以使用操作关键字标识将允许(或拒绝)的资源操作

  • Principal :被允许访问语句中的操作和资源的帐户或用户。在存储桶策略中,委托人是作为此权限接收者的用户、账户、服务或其他实体

  • Condition– 政策生效的条件。您可以使用 AWS范围的密钥和 Amazon S3 特定的密钥来指定 Amazon S3 访问策略中的条件

  • Resource– 存储桶、对象、访问点和作业是您可以允许或拒绝权限的 Amazon S3 资源。在策略中,您使用 Amazon 资源名称 (ARN) 来标识资源

  • Effect:对于每个资源,Amazon S3 支持一组操作。您可以使用操作关键字标识将允许(或拒绝)的资源操作

    如:该s3:ListBucket权限允许用户使用 Amazon S3 GET Bucket (List Objects)操作

下面是自定义的桶策略,支持可读可写,完成后赋予相应的桶即可,mc policy set-json /path/to/policy.json minio-server/test/images

{
 "Version": "2012-10-17",
 "Statement": [
 {
   "Effect": "Allow",
   "Principal": {
                "AWS": [
                    "*"
                ]
            },
   "Action": [
                "s3:GetBucketLocation",
                "s3:ListBucket",
                "s3:ListBucketMultipartUploads"

   ],
   "Resource": [
    "arn:aws:s3:::test"
   ]
  }
 ,
  {
   "Effect": "Allow",
   "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:DeleteObject",
                "s3:AbortMultipartUpload",
                "s3:ListMultipartUploadParts"
            ],
            "Principal": {
                "AWS": [
                    "*"
                ]
            },
   "Resource": [
    "arn:aws:s3:::test/*"
   ]
  }
 ]
}

4.3 集成SpringBoot,编码设置桶策略

第一种是json转string

public class PolicyJsonUtils {

    /**
     * 需要在resource下先创建好文件
     * json转String
     * @param path 文件路径
     * @param bucketName 这里是传入的桶名称,执行完后会替换掉原json文件中的MybuckerName
     * @return
     * @throws IOException
     */
    public static String json2String(String path, String bucketName) throws IOException {
        StringBuilder result = new StringBuilder();
        ClassPathResource resource = new ClassPathResource(path);
        InputStream in = resource.getInputStream();
        // 读取文件上的数据。
        // 将字节流向字符流的转换。
        // 读取
        InputStreamReader isr = new InputStreamReader(in, StandardCharsets.UTF_8);
        // 创建字符流缓冲区
        // 缓冲
        BufferedReader bufr = new BufferedReader(isr);

        String line = null;
        while ((line = bufr.readLine()) != null) {
            result.append(System.lineSeparator()+line);
        }
        isr.close();
        JSONObject parse = (JSONObject) JSONObject.parse(result.toString());
        JSONArray statement = (JSONArray) parse.get("Statement");
        JSONObject o = (JSONObject) statement.get(0);
        o.remove("Resource");
        o.fluentPut("Resource", "arn:aws:s3:::" + bucketName);
        JSONObject o1 = (JSONObject) statement.get(1);
        o1.remove("Resource");
        o1.fluentPut("Resource", "arn:aws:s3:::" + bucketName + "/*");
        System.out.println(parse);
        return parse.toString();
    }
}

运行,当然在管理后台也可以设置

public class PolicyService {
    /**
     * 占位符
     */
    private static final String BUCKET_PARAM = "${bucket}";

    /**
     * bucket权限-读写
     */
    private static final String READ_WRITE = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetBucketLocation\",\"s3:ListBucket\",\"s3:ListBucketMultipartUploads\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:DeleteObject\",\"s3:GetObject\",\"s3:ListMultipartUploadParts\",\"s3:PutObject\",\"s3:AbortMultipartUpload\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "/*\"]}]}";


    /**
     * bucket权限-只读
     */
    private static final String WRITE_ONLY = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetBucketLocation\",\"s3:ListBucketMultipartUploads\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:AbortMultipartUpload\",\"s3:DeleteObject\",\"s3:ListMultipartUploadParts\",\"s3:PutObject\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "/*\"]}]}";

    /**
     * 给桶设置策略 ,可读可写等等
     *
     * @param bucketName 存储桶名称
     */
    @SneakyThrows
    public static void setBucketPolicy(String bucketName) {
        String policy= PolicyJsonUtils.json2String("policyJson.json",bucketName);
        //用工具类 就是把里面的常量替换出来就可以了,当时写了两种方式(喜欢那种就用那种吧)
        MinioClient minioClient =
                MinioClient.builder()
                        .endpoint("http://192.168.249.131:9000")
                        .credentials("minioadmin", "minioadmin")
                        .build();
        minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucketName).
                config(READ_WRITE.replace(BUCKET_PARAM, bucketName)).build());
    }

    public static void main(String[] args) {
        // 这里写桶的名字
        setBucketPolicy("test");
    }

}

5、其他桶操作API

其他桶策略可以参考:JAVA集成Minio之存储桶操作API使用详解
官方参考文档:https://docs.min.io/docs/java-client-api-reference.html

  • 桶是否存在

  • 桶创建

  • 查询存储桶信息列表

  • 删除存储桶

  • 加密桶

  • 桶生命周期设置

  • 桶的通知配置

  • 桶的策略配置

  • 桶的复制配置

  • 存储桶标签

  • 桶的多版本设置

  • 桶的常用配置删除

四、Java整合Minio使用

1、Minio Java Client 使用

MinIO Java Client SDK 提供简单的 API 来访问任何与 Amazon S3 兼容的对象存储服务。
官方 demo: https://github.com/minio/minio-java
官方文档:https://docs.min.io/docs/java-client-api-reference.html
博客参考:https://blog.csdn.net/lemon_TT/article/details/124675675

其他Minio对象操作可以参考:JAVA集成Minio之对象操作API使用详解

引入依赖

<dependency>
    <groupId>io.minio</groupId>
    <artifactId>minio</artifactId>
    <version>8.4.3</version>
</dependency>

文件上传

public class FileUploader {
  public static void main(String[] args)
          throws IOException, NoSuchAlgorithmException, InvalidKeyException {
    try {
      // Create a minioClient with the MinIO server playground, its access key and secret key.
     MinioClient minioClient =
              MinioClient.builder()
                      .endpoint("http://192.168.249.131:9000")
                      .credentials("minioadmin", "minioadmin")
                      .build();

      // 创建bucket
      String bucketName = "test";
      boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
      if (!exists) {
        // 不存在,创建bucket
        minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
      }

      // 上传文件
      minioClient.uploadObject(
              UploadObjectArgs.builder()
                      .bucket(bucketName)
                      .object("test.zip")
                      .filename("F:\\mall\\test.zip")
                      .build());
      System.out.println("上传文件成功");
    } catch (MinioException e) {
      System.out.println("Error occurred: " + e);
      System.out.println("HTTP trace: " + e.httpTrace());
    }
  }
}

文件下载

public class DownLoadDemo {
    public static void main(String[] args) {
        // Create a minioClient with the MinIO server playground, its access key and secret key.
        MinioClient minioClient =
                MinioClient.builder()
                        .endpoint("http://192.168.249.131:9000")
                        .credentials("minioadmin", "minioadmin")
                        .build();

        // Download object given the bucket, object name and output file name
        try {
            minioClient.downloadObject(
                    DownloadObjectArgs.builder()
                            .bucket("test")
                            .object("小车操作记录.txt")
                            .filename("小车操作记录.txt")
                            .build());

        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

2、Spring boot 整合 minio

构建 MinioClient 对象,并交给 spring 管理

@Data
@Component
@ConfigurationProperties(prefix = "minio")
public class MinioProperties {

    private String endpoint;
    private String accessKey;
    private String secretKey;

}

//yml
minio:
  endpoint: http://192.168.249.131:9000
  accesskey: minioadmin
  secretKey: minioadmin

      
@Configuration
public class MinioConfig {

    @Autowired
    private MinioProperties minioProperties;

    @Bean
    public MinioClient minioClient(){
        MinioClient minioClient =
                MinioClient.builder()
                        .endpoint(minioProperties.getEndpoint())
                        .credentials(minioProperties.getAccessKey(), minioProperties.getSecretKey())
                        .build();
        return minioClient;
    }
}

实现文件上传,下载,删除操作

@RestController
@Slf4j
public class MinioController {

    @Autowired
    private MinioClient minioClient;

    @Value("${minio.bucketName}")
    private String bucketName;


    @GetMapping("/list")
    public List<Object> list() throws Exception {
        //获取bucket列表
        Iterable<Result<Item>> myObjects = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(bucketName).build());
        Iterator<Result<Item>> iterator = myObjects.iterator();
        List<Object> items = new ArrayList<>();
        String format = "{'fileName':'%s','fileSize':'%s'}";
        while (iterator.hasNext()) {
            Item item = iterator.next().get();
            items.add(JSON.parse(String.format(format, item.objectName(), formatFileSize(item.size()))));
        }
        return items;
    }

    @PostMapping("/upload")
    public Res upload(@RequestParam(name = "file", required = false) MultipartFile[] file) {


        if (file == null || file.length == 0) {
            return Res.error("上传文件不能为空");
        }

        List<String> orgfileNameList = new ArrayList<>(file.length);

        for (MultipartFile multipartFile : file) {
            String orgfileName = multipartFile.getOriginalFilename();
            orgfileNameList.add(orgfileName);
            try {
                //文件上传
                InputStream in = multipartFile.getInputStream();
                minioClient.putObject(
                        PutObjectArgs.builder().bucket(bucketName).object(orgfileName).stream(
                                in, multipartFile.getSize(), -1)
                                .contentType(multipartFile.getContentType())
                                .build());
                in.close();

            } catch (Exception e) {
                log.error(e.getMessage());
                return Res.error("上传失败");
            }
        }

        Map<String, Object> data = new HashMap<String, Object>();
        data.put("bucketName", bucketName);
        data.put("fileName", orgfileNameList);
        return Res.ok("上传成功",data);
    }

    @RequestMapping("/download/{fileName}")
    public void download(HttpServletResponse response, @PathVariable("fileName") String fileName) {

        InputStream in = null;
        try {
            // 获取对象信息
            StatObjectResponse stat = minioClient.statObject(
                    StatObjectArgs.builder().bucket(bucketName).object(fileName).build());
            response.setContentType(stat.contentType());
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            //文件下载
            in = minioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket(bucketName)
                            .object(fileName)
                            .build());
            IOUtils.copy(in, response.getOutputStream());
        } catch (Exception e) {
            log.error(e.getMessage());
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    log.error(e.getMessage());
                }
            }
        }

    }

    @DeleteMapping("/delete/{fileName}")
    public Res delete(@PathVariable("fileName") String fileName) {
        try {
            minioClient.removeObject(
                    RemoveObjectArgs.builder().bucket(bucketName).object(fileName).build());
        } catch (Exception e) {
            log.error(e.getMessage());
            return Res.error("删除失败");
        }
        return Res.ok("删除成功",null);
    }

    private static String formatFileSize(long fileS) {
        DecimalFormat df = new DecimalFormat("#.00");
        String fileSizeString = "";
        String wrongSize = "0B";
        if (fileS == 0) {
            return wrongSize;
        }
        if (fileS < 1024) {
            fileSizeString = df.format((double) fileS) + " B";
        } else if (fileS < 1048576) {
            fileSizeString = df.format((double) fileS / 1024) + " KB";
        } else if (fileS < 1073741824) {
            fileSizeString = df.format((double) fileS / 1048576) + " MB";
        } else {
            fileSizeString = df.format((double) fileS / 1073741824) + " GB";
        }
        return fileSizeString;
    }

}

3、其他对象操作API使用详解

官方文档:https://docs.min.io/docs/java-client-api-reference.html

上传对象

  • PutObject

  • uploadObject

获取对象

  • getObject

  • downloadObject

  • getPresignedObjectUrl

  • selectObjectContent

  • getPresignedPostFormData

复制对象

删除对象

对象信息查询及设置


参考文章

minio快速入门

Minio入门系列【10】Minio之权限控制策略

minio的桶匿名访问策略

分布式文件存储系统 Minio 实战

B站最详细的分布式文件系统MinIO入门到实战

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐