Skip to content

MinIO — 高性能对象存储

架构概览

MinIO 是高性能、S3 兼容的开源对象存储,适合私有化部署。

MinIO 分布式模式(4节点,每节点4块盘):
  Node1: /data1 /data2 /data3 /data4
  Node2: /data1 /data2 /data3 /data4
  Node3: /data1 /data2 /data3 /data4
  Node4: /data1 /data2 /data3 /data4
  
  总驱动器:16
  纠删码组:EC:8(8数据 + 8校验)
  可容忍:最多8块盘故障

纠删码(Erasure Coding)

MinIO 使用 Reed-Solomon 纠删码,比多副本更节省空间:

3副本方案:
  存储 1TB 数据 → 需要 3TB 存储空间
  
纠删码 EC:4+4(4数据 + 4校验):
  存储 1TB 数据 → 需要 2TB 存储空间
  可容忍 4 块盘故障

部署

单机模式(开发/测试)

bash
docker run -p 9000:9000 -p 9001:9001 \
  -e MINIO_ROOT_USER=minioadmin \
  -e MINIO_ROOT_PASSWORD=minioadmin \
  -v /data/minio:/data \
  minio/minio server /data --console-address ":9001"

分布式模式

bash
# docker-compose.yml
version: '3.7'
services:
  minio1:
    image: minio/minio
    environment:
      MINIO_ROOT_USER: minioadmin
      MINIO_ROOT_PASSWORD: minioadmin123
    command: server http://minio{1...4}/data{1...2} --console-address ":9001"
    volumes:
      - /data1:/data1
      - /data2:/data2
    ports:
      - "9001:9001"
  minio2:
    # 类似配置...

Java SDK 使用

java
MinioClient minioClient = MinioClient.builder()
    .endpoint("http://minio:9000")
    .credentials("minioadmin", "minioadmin123")
    .build();

// 创建 Bucket
if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket("my-bucket").build())) {
    minioClient.makeBucket(MakeBucketArgs.builder().bucket("my-bucket").build());
}

// 上传文件
minioClient.uploadObject(
    UploadObjectArgs.builder()
        .bucket("my-bucket")
        .object("images/photo.jpg")
        .filename("/local/path/photo.jpg")
        .contentType("image/jpeg")
        .build()
);

// 上传流
minioClient.putObject(
    PutObjectArgs.builder()
        .bucket("my-bucket")
        .object("data/file.csv")
        .stream(inputStream, inputStream.available(), -1)
        .contentType("text/csv")
        .build()
);

// 下载文件
InputStream stream = minioClient.getObject(
    GetObjectArgs.builder()
        .bucket("my-bucket")
        .object("images/photo.jpg")
        .build()
);

// 生成预签名 URL(临时访问链接)
String url = minioClient.getPresignedObjectUrl(
    GetPresignedObjectUrlArgs.builder()
        .method(Method.GET)
        .bucket("my-bucket")
        .object("images/photo.jpg")
        .expiry(1, TimeUnit.HOURS)
        .build()
);

// 删除对象
minioClient.removeObject(
    RemoveObjectArgs.builder()
        .bucket("my-bucket")
        .object("images/photo.jpg")
        .build()
);

存储桶策略

json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {"AWS": ["*"]},
      "Action": ["s3:GetObject"],
      "Resource": ["arn:aws:s3:::public-bucket/*"]
    }
  ]
}
bash
# 设置公开读策略
mc policy set public myminio/public-bucket

# 设置自定义策略
mc policy set-json policy.json myminio/my-bucket

生命周期管理

xml
<!-- 生命周期规则:30天后转为低频存储,90天后删除 -->
<LifecycleConfiguration>
  <Rule>
    <ID>archive-old-files</ID>
    <Status>Enabled</Status>
    <Filter>
      <Prefix>logs/</Prefix>
    </Filter>
    <Transition>
      <Days>30</Days>
      <StorageClass>STANDARD_IA</StorageClass>
    </Transition>
    <Expiration>
      <Days>90</Days>
    </Expiration>
  </Rule>
</LifecycleConfiguration>
bash
mc ilm import myminio/my-bucket < lifecycle.xml
mc ilm ls myminio/my-bucket

与 Spring Boot 集成

yaml
# application.yml
minio:
  endpoint: http://minio:9000
  access-key: minioadmin
  secret-key: minioadmin123
  bucket: my-bucket
java
@Configuration
public class MinioConfig {
    @Value("${minio.endpoint}")
    private String endpoint;
    
    @Value("${minio.access-key}")
    private String accessKey;
    
    @Value("${minio.secret-key}")
    private String secretKey;
    
    @Bean
    public MinioClient minioClient() {
        return MinioClient.builder()
            .endpoint(endpoint)
            .credentials(accessKey, secretKey)
            .build();
    }
}

@Service
public class FileStorageService {
    @Autowired
    private MinioClient minioClient;
    
    @Value("${minio.bucket}")
    private String bucket;
    
    public String upload(MultipartFile file) throws Exception {
        String objectName = UUID.randomUUID() + "/" + file.getOriginalFilename();
        minioClient.putObject(
            PutObjectArgs.builder()
                .bucket(bucket)
                .object(objectName)
                .stream(file.getInputStream(), file.getSize(), -1)
                .contentType(file.getContentType())
                .build()
        );
        return objectName;
    }
    
    public String getPresignedUrl(String objectName) throws Exception {
        return minioClient.getPresignedObjectUrl(
            GetPresignedObjectUrlArgs.builder()
                .method(Method.GET)
                .bucket(bucket)
                .object(objectName)
                .expiry(7, TimeUnit.DAYS)
                .build()
        );
    }
}

故障处理案例

案例一:节点磁盘故障

现象:某节点磁盘损坏,MinIO 日志出现 IO 错误。

处理

bash
# 查看集群健康状态
mc admin info myminio

# 查看磁盘状态
mc admin info myminio --json | jq '.info.servers[].drives'

# 替换故障磁盘后,MinIO 自动修复数据
mc admin heal myminio/my-bucket --recursive

案例二:上传大文件超时

现象:上传大文件时连接超时。

解决:使用分片上传(Multipart Upload):

java
// MinIO SDK 自动处理分片上传(对象 > 5MB 时)
minioClient.uploadObject(
    UploadObjectArgs.builder()
        .bucket(bucket)
        .object("large-file.zip")
        .filename("/path/to/large-file.zip")
        .build()
);
// SDK 内部自动使用 5MB 分片并发上传

案例三:存储空间不足

bash
# 查看存储使用情况
mc admin info myminio

# 查看 Bucket 大小
mc du myminio/my-bucket

# 清理过期对象(配置生命周期策略)
# 或手动删除
mc rm --recursive --force myminio/my-bucket/old-logs/

监控

bash
# 启用 Prometheus 指标
mc admin prometheus generate myminio

# 指标端点
curl http://minio:9000/minio/v2/metrics/cluster
指标说明
minio_cluster_capacity_usable_total_bytes可用容量
minio_cluster_capacity_usable_free_bytes剩余容量
minio_s3_requests_totalS3 请求总数
minio_s3_requests_errors_total错误请求数
minio_node_drive_offline_total离线磁盘数

PaaS 中间件生态系统深度学习文档