课程介绍
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