1小时速通minio
课程介绍 MinIO 是一个高性能、分布式的对象存储系统,兼容 Amazon S3 API。它可以让你在自己的服务器、私有云或边缘设备上搭建类似 AWS S3 的存储服务 什么是对象存储? 对象
1小时速通minio
发布时间:2026-01-13 (2026-01-13)

课程介绍

MinIO 是一个高性能、分布式的对象存储系统,兼容 Amazon S3 API。它可以让你在自己的服务器、私有云或边缘设备上搭建类似 AWS S3 的存储服务

什么是对象存储?

对象存储(Object Storage)是一种用来存储大量非结构化数据的方式。

非结构化数据包括:・图片・视频・音频・日志文件・备份文件・AI 训练数据(如图片、文本、模型权重)

这些数据通常不适合放进数据库,因为它们体积大、结构不固定。

对象存储把数据保存为一个个 “对象”,每个对象包含:・数据本身・元数据(比如文件大小、类型、创建时间等)・一个唯一的 ID

对象存储的特点:・容量可以无限扩展・读写性能高・成本低・适合海量数据・访问方式通常是 HTTP/HTTPS(REST API)

什么是S3 API

S3 的全称是 Amazon Simple Storage Service,是亚马逊在 2006 年推出的对象存储服务。

它的特点:・简单易用・无限容量・高可靠・高可用・提供 RESTful API 访问

因为 S3 太成功了,它几乎成为了对象存储的行业标准。

现在几乎所有云厂商都提供兼容 S3 API 的对象存储,例如:・阿里云 OSS・腾讯云 COS・华为云 OBS• Google Cloud Storage・Azure Blob Storage(部分兼容)

一句话总结:S3 是对象存储的 “标准接口”,几乎所有对象存储都支持 S3 API。

minio的特点

高性能:基于 Go 语言开发,读写性能极高,特别适合 AI 训练数据、视频、日志等大规模文件存储

兼容 S3 API:几乎所有能对接 S3 的工具、SDK、应用都能直接使用 MinIO。

部署简单:单二进制文件,不依赖其他组件,可在 Linux、Windows、macOS、容器、Kubernetes 中运行。

可扩展性强:支持从单节点到大规模分布式集群,通过增加节点即可线性扩展容量和性能。

高可用性:支持多副本、纠删码(Erasure Code),即使多块硬盘或节点损坏也能保证数据不丢失。

安全性:提供 TLS、访问控制(IAM)、对象锁定、加密等安全特性。

minio安装

许可证变更问题

MinIO 从 2023 年左右的版本(大致 RELEASE.2023-04-20 及以后)将许可证从 Apache 2.0 改为 GNU AGPL v3 + 商业许可证:

  • AGPL v3:如果你的项目是商业闭源,且直接修改 MinIO 核心代码并对外提供服务,需要开源相关代码;仅作为 “工具” 部署使用(比如搭建私有存储),则不受 AGPL 约束,对绝大多数学习者 / 中小企业完全友好。
  • Apache 2.0 版本:旧版本(如 RELEASE.2023-03-20 及之前)仍保留 Apache 2.0 许可证,无开源约束。

console功能移除的问题

MinIO 在新版本(大致 RELEASE.2025-04-28 及以后)的官方 Docker 镜像中,默认移除了内置的 Console (Web 管理界面),改为推荐使用 minio console 命令单独启动,或部署独立的 Console 镜像。对新手来说,直接装新版本会看不到可视化界面,学习体验差。

使用docker安装指定版本

docker pull minio/minio:RELEASE.2025-04-08T15-41-24Z

简单模式运行minio

以单主机、单磁盘方式运行minio,这种模式下minio是没有扩容和纠错功能的,只适用与开发、测试环境

mkdir -p /opt/minio/data
docker run -d --name minio -p 9000:9000 -p 9001:9001 -v /opt/minio/data:/data minio/minio:RELEASE.2025-04-08T15-41-24Z server /data --console-address ":9001"

访问你服务器的9001端口,默认用户名密码是 minioadmin,可以使用 -e MINIO_ROOT_USER="admin" -e MINIO_ROOT_PASSWORD="12345678" 这两个环境变量进行修改

minio console页面相关操作

登录进来之后,正常情况下你应该是看到这样的页面

我们先在web界面上熟悉minio的操作

存储桶操作

Bucket(桶) 是 MinIO 中最高级别的存储组织单元,你可以把它理解为:

  • 一个命名的、独立的 “容器”,用来存放所有的对象(Object,也就是文件、图片、视频等数据);
  • 类似电脑里的 “文件夹”,但比普通文件夹的特性更强(有独立的权限、配置、生命周期规则等);
  • 整个 MinIO 实例中,Bucket 名称是全局唯一的(同一 MinIO 集群里不能有两个同名 Bucket)。

创建bucket

一些配置的介绍

Versioning(版本控制)

  • OFF(默认):覆盖或删除对象时,旧版本会被直接替换或删除,无法恢复。
  • ON:开启后,MinIO 会保留对象的所有历史版本。
  • 用途:当你误删或覆盖文件时,可以随时恢复到之前的版本,适合需要数据回溯的场景,比如备份、合规性存储。

Object Locking(对象锁定)

  • OFF(默认):对象可以被随时删除或修改。
  • ON:一旦开启,对象将被锁定,无法被删除或修改,只能在 Bucket 创建时启用。
  • 用途:满足金融、医疗等行业的合规性需求,比如需要强制保留日志、合同等数据一段时间,防止被篡改或删除。

Retention(保留策略)

  • 只有当 Object Locking 开关打开时,这个配置项才会出现。
  • 开启后,它会强制为 Bucket 里的所有对象设置一个 “不能被删除或修改” 的保留期。
  • 它和 Object Locking 是配套的,后者是基础开关,前者是具体的保留规则。

Mode(保留模式)

  • Compliance(合规模式):最严格的模式,任何人(包括 MinIO 管理员)都不能提前删除或修改对象,直到保留期结束。它主要用于满足金融、医疗等行业的强合规需求,比如需要强制保留审计日志。
  • Governance(治理模式):相对灵活的模式,普通用户无法删除对象,但拥有特殊权限的管理员可以在必要时提前删除,适合需要兼顾合规性和灵活性的场景。

Quota(配额)

  • OFF(默认):Bucket 可以存储无限量的数据(仅受集群物理容量限制)。
  • ON:可以为 Bucket 设置最大存储容量(比如 100GB)。
  • 用途:防止单个 Bucket 占用过多存储空间,用于按业务或用户分配存储资源的场景。

文件操作

在Object Browser中点击bucket就可以进入存储桶内部,就可以看到存储桶中的文件和上传文件了

点击文件,在右侧的操作栏中就可以对文件进行 分享、预览和下载了

权限操作

默认的存储桶,创建是私有的,你复制预览图片的链接,换一个浏览器是打不开的

进存储桶设置修改访问权限

修改之后,就可以在其他地方访问这个预览地址了

不过这个预览地址是9001的,这种情况下我们应该是访问9000的

当你把 MinIO Bucket 设置为 Public(公开可读) 后,文件的访问路径遵循一个标准格式

<MinIO服务地址>/<Bucket名称>/<对象完整路径>
例如:
http://192.168.80.174:9000/demo1/711cd744a38149b79442ec607e247ac6.jpeg

用户操作

比如我想实现 创建一个专门的用户,这个用户只能预览文件,不能上传和删除文件

登录fengfeng这个用户,这个用户就只能下载文件

如果想让这个用户能看到存储桶的所有文件并下载,那需要自定义策略

策略操作

进入Policies页面

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::demo1/*",  // 允许读取桶内所有文件
        "arn:aws:s3:::demo1"     // 允许列出桶内文件列表
      ]
    }
  ]
}

然后给用户重新分配权限之后,用户就可以看到存储桶的文件列表了

s3API操作

下载minio的客户端

go get github.com/minio/minio-go/v7

只要这个对象存储兼容s3API,那么你也可用这个库操作

创建客户端

var client *minio.Client

func init() {
  accessKey := "GKd9tvrOTbK6pTcunynU"
  accessSecret := "dE6cyfPZKiELHZysFOS8K1OC7JoOWIDrrEigIdWn"
  endPoint := "192.168.20.128:9000" // minio地址,不能加http
  useSSL := false                   // 是否使用https进行通信

  // 初始化minio客户端
  _client, err := minio.New(endPoint, &minio.Options{
    Creds:  credentials.NewStaticV4(accessKey, accessSecret, ""),
    Secure: useSSL,
  })
  if err != nil {
    fmt.Println(err)
    os.Exit(0)
  }
  client = _client
}

其中的accessKey 和accessSecret 就是创建容器的minio用户名密码,也可以在控制台单独创建

如果是https的话,可以选择让客户端忽略

var client *minio.Client

func init() {
  accessKey := "admin"
  accessSecret := "12345678"
  endPoint := "192.168.80.174:9000" // minio地址,不能加http
  useSSL := true                    // 是否使用https进行通信

  transport := &http.Transport{
    TLSClientConfig: &tls.Config{
      InsecureSkipVerify: true, // 忽略证书验证(核心配置)
    },
  }

  // 初始化minio客户端
  _client, err := minio.New(endPoint, &minio.Options{
    Creds:     credentials.NewStaticV4(accessKey, accessSecret, ""),
    Secure:    useSSL,
    Transport: transport,
  })
  if err != nil {
    fmt.Println(err)
    os.Exit(0)
  }
  client = _client
}

存储桶操作

存储桶列表

buckets, err := client.ListBuckets(context.Background())
if err != nil {
  fmt.Println(err)
  return
}
for _, bucket := range buckets {
  fmt.Println(bucket.Name, bucket.CreationDate)
}

查询桶是否存在

found, _ := client.BucketExists(context.Background(), "fengfeng")
fmt.Println(found)

删除桶

err = client.RemoveBucket(context.Background(), "fengfeng1")
fmt.Println(err)

创建桶

err = client.MakeBucket(context.Background(), "fengfeng", minio.MakeBucketOptions{})
if err != nil {
  fmt.Println(err)
  return
}

文件操作

文件上传

注意,超过128MB的文件会被被切割为128MB的分块进行分片传输,最大可传输的文件为5TB。当然这些在用户接口侧是无感知的。

简单版本 直接传文件名

uploadInfo, err := client.FPutObject(context.Background(), "fengfeng", "main1.go", "main.go",
  minio.PutObjectOptions{ContentType: "application/octet-stream"})
if err != nil {
  fmt.Println(err)
  return
}
fmt.Println("上传成功,上传文件信息: ", uploadInfo)

通过文件对象上传

  file, err := os.Open("main.go")
  if err != nil {
    fmt.Println(err)
    return
  }
  defer file.Close()

  fileStat, err := file.Stat()
  if err != nil {
    fmt.Println(err)
    return
  }

  UserMetadata := map[string]string{
    "origin_name": fileStat.Name(), // 文件的原始名称
  }
  objectSize := fileStat.Size() // objectSize可设置为-1,表示不确定文件大小,但是-1会预分配比较大的内存。
  uploadInfo, err := client.PutObject(context.Background(), "fengfeng", fileStat.Name(), file, objectSize,
    minio.PutObjectOptions{ContentType: "application/octet-stream", UserMetadata: UserMetadata})
  if err != nil {
    fmt.Println(err)
    return
  }
  fmt.Println("上传成功,上传文件信息: ", uploadInfo)

通过io流上传

常见与web服务,接收用户上传的文件,然后上传到minio上

func uploadView(c *gin.Context) {
  fileHeader, _ := c.FormFile("file")
  reader, err := fileHeader.Open()
  if err != nil {
    log.Println(err)
    return
  }
  defer reader.Close()

  objectSize := fileHeader.Size
  uploadInfo, err := client.PutObject(context.Background(), "fengfeng", fileHeader.Filename, reader, objectSize,
    minio.PutObjectOptions{ContentType: "application/octet-stream"})
  if err != nil {
    fmt.Println(err)
    return
  }
  fmt.Println("上传成功,上传文件信息: ", uploadInfo)
}

文件列表

// 注意,ListObjects返回值是个channel,通过迭代来获取所有object
objectCh := client.ListObjects(context.Background(), "fengfeng", minio.ListObjectsOptions{
  Prefix:    "", // 通过该参数过滤以Prefix作为object key前缀的object
  Recursive: true,
})
for object := range objectCh {
  if object.Err != nil {
    fmt.Println(object.Err)
    return
  }
  log.Println(object)
}

下载文件

// 整个文件下载和保存到指定目录,适合文件下载,如下载pdf文件
err := client.FGetObject(context.Background(), "fengfeng", "1.md", "1.md", minio.GetObjectOptions{})
if err != nil {
  fmt.Println(err)
  return
}
fmt.Println("成功")

下载文件对象

返回的是File、Reader对象

object, err := client.GetObject(context.Background(), "fengfeng", "1.md", minio.GetObjectOptions{})
if err != nil {
  fmt.Println(err)
  return
}
file, _ := os.Create("x.md")
defer file.Close()
defer object.Close()
io.Copy(file, object)

删除文件

err := client.RemoveObject(context.Background(), "fengfeng", "1.md", minio.RemoveObjectOptions{})
fmt.Println(err)

minio分布式部署

简单模式

version: '3.8'

services:
  minio:
    image: minio/minio:RELEASE.2025-04-08T15-41-24Z
    container_name: minio
    restart: always
    environment:
      - MINIO_ROOT_USER=admin
      - MINIO_ROOT_PASSWORD=12345678
      - MINIO_SERVER_URL=http://192.168.80.174:9000
    volumes:
      - ./conf:/home/minio/conf
      - ./mnt/disk1:/mnt/disk1
      - ./mnt/disk2:/mnt/disk2
    ports:
      - "9000:9000"
      - "9001:9001"
    command: >
      server --config-dir /home/minio/conf
      --address ":9000"
      --console-address ":9001"
      /mnt/disk1
      /mnt/disk2
    privileged: true

之前演示的都是单节点单磁盘的minio部署模式

如果机器宕机或者磁盘损坏都可能导致数据不可用

所以在正式的生产环境,建议多节点多磁盘部署

比如现在我有两个服务器,分别是192.168.80.174和192.168.80.176,两边都要跑minio的程序

跑176的时候,需要改一下MINIO_SERVER_URL对应的配置和--address配置

version: '3.8'

services:
  minio:
    image: minio/minio:RELEASE.2025-04-08T15-41-24Z
    container_name: minio
    restart: always
    environment:
      - MINIO_ROOT_USER=admin
      - MINIO_ROOT_PASSWORD=12345678
      - MINIO_SERVER_URL=http://192.168.80.174:9000
    volumes:
      - ./conf:/home/minio/conf
      - ./mnt/disk1:/mnt/disk1
      - ./mnt/disk2:/mnt/disk2
    command: >
      server --config-dir /home/minio/conf
      --address "192.168.80.174:9000"
      --console-address "192.168.80.174:9001"
      http://192.168.80.174/mnt/disk1
      http://192.168.80.174/mnt/disk2
      http://192.168.80.176/mnt/disk1
      http://192.168.80.176/mnt/disk2
    network_mode: host
    privileged: true
    deploy:
      resources:
        limits:
          cpus: '2'
          memory: 4G
        reservations:
          cpus: '1'
          memory: 2G

minio TLS部署

先自签一个tls证书

#!/bin/bash
# 证书有效期(天)
VALID_DAYS=3650
# 证书输出目录(和 docker-compose.yml 同目录)
CERT_DIR="./certs"

# 1. 创建证书目录
mkdir -p ${CERT_DIR}

cat > ${CERT_DIR}/minio_ssl.conf <<EOF
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
prompt = no

[req_distinguished_name]
C = CN
ST = Beijing
L = Beijing
O = MyOrg
OU = MyDept
CN = 192.168.80.174

[v3_req]
basicConstraints = CA:TRUE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names

[alt_names]
IP.1 = 192.168.80.174
IP.2 = 192.168.80.176
EOF


openssl req -x509 -nodes -days ${VALID_DAYS} -newkey rsa:2048  -keyout ${CERT_DIR}/private.key -out ${CERT_DIR}/public.crt  -config ${CERT_DIR}/minio_ssl.conf

# 4. 清理临时配置文件(可选)
rm -f ${CERT_DIR}/minio_ssl.conf

# 5. 验证证书(输出关键信息)
echo -e "\n证书生成完成!路径:${CERT_DIR}"
echo "证书包含的 SAN 扩展:"
openssl x509 -in ${CERT_DIR}/public.crt -text -noout | grep -A 1 "Subject Alternative Name"

只需要将这两个证书文件映射到/home/minio/conf目录就ok了

注意:名字必须叫 private.key和public.crt

version: '3.8'

services:
  minio:
    image: minio/minio:RELEASE.2025-04-08T15-41-24Z
    container_name: minio
    restart: always
    environment:
      - MINIO_ROOT_USER=admin
      - MINIO_ROOT_PASSWORD=12345678
      - MINIO_SERVER_URL=https://192.168.80.174:9000
      - MINIO_FORCE_TLS=on
    volumes:
      - ./certs:/home/minio/conf
      - ./mnt/disk1:/mnt/disk1
      - ./mnt/disk2:/mnt/disk2
    ports:
      - "9000:9000"
      - "9001:9001"
    command: >
      server --config-dir /home/minio/conf
      --address ":9000"
      --console-address ":9001"
      /mnt/disk1
      /mnt/disk2
    privileged: true

参考文档

minio-go使用实践 https://zhuanlan.zhihu.com/p/671328009