MinIO 停止维护怎么办?Docker 迁移 RustFS 实战:数据零丢失

797 阅读8分钟

前言

MinIO 说停就停,线上的文件服务还得接着跑。

2025 年 12 月,MinIO 官宣社区版进入维护模式。2026 年 2 月,仓库归档只读:不再有提交,CVE 不再修复,官方资源全面转向商业版 AIStor——订阅制,个人项目用不起。还在跑社区版的部署,等于抱着一个不再更新的黑盒。

youlai-boot(Spring Boot 权限管理后端)的文件存储一直跑在 MinIO 上,趁这次把迁移完整走了一遍:旧数据整目录复制、endpoint 和桶名不变,顺手把客户端换成 AWS SDK 标准实现。过程同样适用于任何 Docker 部署的 MinIO——包括 vue3-element-admin 配套环境在内。

在这里插入图片描述

本文看点:

  • 选型判断:RustFS / SeaweedFS / Garage 什么场景选谁
  • 客户端替换:minio SDK 换 AWS SDK v2,只改一个 Service 实现类
  • 数据零丢失:旧目录整目录复制,旧数据原样保留当备份

一、环境准备

1.1 MinIO 停更时间线

时间事件
2025-12官宣社区版进入维护模式:不收新功能和 PR,安全修复"视情况评估"
2026-02GitHub 仓库归档只读,README 标注 "THIS REPOSITORY IS NO LONGER MAINTAINED"
之后官方资源全部转向商业版 AIStor,社区版已披露的 CVE 不再修复

1.2 替代品怎么选

主流开源替代有四个:

方案协议状态适合场景
SeaweedFSApache 2.0生产验证 10 年海量小文件,要久经考验
RustFSApache 2.0活跃迭代,较新单容器部署,MinIO 迁移成本最低
GarageAGPL v3生产可用地理分布式集群
Ceph RGWLGPL企业级PB 级规模,单机太重

四个候选都兼容 S3,区别在部署方式:

  • SeaweedFS 最成熟,但它是 Master / Volume / Filer 多角色架构,从 MinIO 迁过来要重新理解一套部署模型
  • RustFS 单容器一条 docker run,9000 API + 9001 控制台,和 MinIO 的使用习惯一致
  • RustFS 兼容 MinIO 磁盘格式,旧数据整目录复制即可,其他候选都得走网络搬运(见 2.2 节)

本项目选择 RustFS,迁移路径最短:部署方式、端口约定、控制台操作全部延续,应用侧只换一对 AK/SK。

边界也要说清:RustFS 比 SeaweedFS 年轻,生产验证的厚度不如前者。PB 级核心存储选 SeaweedFS 或 Ceph 更稳;单机自用、中小规模,迁移成本这一票投给 RustFS。

1.3 迁移前检查

docker -v

# 复制数据要占空间,确认有约 2 倍旧数据的余量
df -h /data

# 查看旧 MinIO 的数据目录路径,2.2 节要用(本文示例为 /data/minio)
docker inspect minio --format '{{range .Mounts}}{{.Source}} -> {{.Destination}}{{"\n"}}{{end}}'

最后一项的输出就是旧数据目录,2.2 节从它复制。


二、迁移准备

整个迁移流程一张图:

mermaid diagram

2.1 停用 MinIO

# 容器删除,数据目录保留——迁移没验证完之前,随时可以回退
docker stop minio && docker rm minio

2.2 准备数据目录

RustFS 兼容 MinIO 磁盘格式(≥ 1.0.0-alpha.89),整目录复制即可,不走网络。旧数据在 /data/minio(1.3 节查到的路径),复制为平级的 /data/rustfs,旧目录原样保留,本身就是回退备份。

# cp 要求目标目录不存在
rm -rf /data/rustfs

# -a 保留属主和权限,直接整体复制
cp -a /data/minio /data/rustfs

# RustFS 容器以 UID 10001 运行,目录属主必须改成它,否则启动即崩
chown -R 10001:10001 /data/rustfs

复制过来后有一件事要提前知道:旧 AK/SK 不能用了。Access Key 不随数据迁移,统一用启动时环境变量里设的新 AK/SK,第四章的应用配置要跟着改。

迁移范围和兼容边界:

  • 桶元数据、对象(含标签 / 版本 / 对象锁)、生命周期、IAM 随数据带过来
  • 站点复制、事件通知、LDAP/OIDC 不在迁移范围
  • 旧目录先别删,确认 3.2 验证通过后再清理

三、启动 RustFS

3.1 启动容器

docker run -d \
  --name rustfs \
  --restart unless-stopped \
  -p 9000:9000 \
  -p 9001:9001 \
  -v /data/rustfs:/data \
  -e RUSTFS_ACCESS_KEY=rustfs-admin \
  -e RUSTFS_SECRET_KEY='换成你的强密码' \
  rustfs/rustfs:latest

端口含义和 MinIO 一致:9000 是 S3 API(应用连这个),9001 是 Web 控制台。云服务器安全组原来为 MinIO 放行过的话,端口不用改。

目录属主必须是 10001。MinIO 官方镜像以 root 运行,挂哪都能写;RustFS 镜像声明了 USER 10001,以非 root 身份运行,而 bind mount 不转换权限——宿主机目录属主不是 10001 时,容器一启动就崩:

# [FATAL] Server runtime failed: Io error: Permission denied (os error 13)

2.2 已经 chown 10001 过副本,这里正常启动。10001 是镜像里声明的运行用户,可用 docker inspect rustfs/rustfs:latest --format '{{.Config.User}}' 查(官方 issue #2396)。

3.2 验证服务

docker ps | grep rustfs    # 状态 Up 即成功
docker logs -f rustfs      # 日志无报错

浏览器打开 http://服务器IP:9001,用启动时设置的 AK/SK 登录——控制台里应该直接看到旧 MinIO 的桶和文件。

3.3 开启匿名读

旧桶策略随数据带过来;如果是新建的桶(或旧桶没开过),博客图床、前端直链场景要开匿名下载。操作路径两步:

  1. 左侧菜单第一项对象浏览 → 存储桶列表 → 找到目标桶,点行末的设置按钮
  2. 进入桶详情后,在访问与共享 → 访问策略处点击编辑,改为公开,保存后出现“修改成功”提示即生效

在这里插入图片描述

在这里插入图片描述

不开的话,外链图片一律 403。「公开」是只读匿名访问,外链能拉取图片,但上传仍要走 AK/SK 认证。


四、应用切换

4.1 切换 AWS SDK

io.minio:minio 还能用,但它的维护方已经全面转向商业版。AWS SDK for Java v2 支持连接任意 S3 兼容存储,既然都动手迁移了,一起换掉:

<dependency>
    <groupId>software.amazon.awssdk</groupId>
    <artifactId>s3</artifactId>
    <!-- 以 Maven Central 最新版本为准 -->
    <version>2.54.2</version>
</dependency>

完整实现源码RustFSFileServiceImpl.java

file-storage:
  type: s3                            # 枚举改为 s3
  s3:
    endpoint: http://服务器IP:9000
    access-key: rustfs-admin          # RustFS 的新 AK
    secret-key: 换成你的强密码         # RustFS 的新 SK
    bucket: public                    # 数据已复制过来,桶还是原来那个
import software.amazon.awssdk.auth.credentials.*;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;

@Bean
public S3Client s3Client(FileStorageProperties props) {
    var s3 = props.getS3();
    return S3Client.builder()
            // 连自己的服务器,而非 AWS
            .endpointOverride(URI.create(s3.getEndpoint()))
            // 非 AWS 服务必填,值任意
            .region(Region.US_EAST_1)
            // IP 端点必须:桶名走路径不走子域名(见踩坑清单)
            .forcePathStyle(true)
            .credentialsProvider(StaticCredentialsProvider.create(
                    AwsBasicCredentials.create(s3.getAccessKey(), s3.getSecretKey())))
            .build();
}

上传调用从 MinioClient 换成 S3Client.putObject,参数语义一一对应,改动集中在一个 Service 实现类(RustFSFileServiceImpl.java)里。

协议客户端跟协议走,不跟厂商走——S3 是 AWS 定的开放标准,服务端换成谁都是配置级改动。

4.2 验证上传

# 重启应用
mvn spring-boot:run

方式一:打开 http://localhost:8000/doc.html,调文件上传接口,接口返回文件 URL 即成功。

在这里插入图片描述

方式二:打开vue3-element-admin前端「组件封装 → 图片上传」,选择图片上传;也可以直接访问有来在线环境:vue.youlai.tech/#/component…

上传成功后,复制返回的文件 URL 在浏览器打开。

在这里插入图片描述

最后回 RustFS 控制台,public 桶里能看到刚上传的图片文件,迁移端到端验证完成。

在这里插入图片描述


五、踩坑清单

现象原因解法
启动即崩,Permission denied (os error 13)RustFS 容器以 UID 10001 运行,目录属主不匹配chown -R 10001:10001 数据目录
上传报 XAmzContentSHA256Mismatch新版 SDK 默认流式分块签名,旧版 MinIO 解析不了旧版已归档不会修复,只能迁移
请求打到 桶名.服务器IP,连接失败AWS SDK 默认虚拟主机式寻址forcePathStyle(true)
上传报 403 AccessDenied旧 MinIO 的 Service Account 不随数据迁移换成 RustFS 启动时设的 AK/SK
上传报 NoSuchBucket全新空目录部署时桶不存在手动建桶,或照 2.2 节从旧目录复制
外链图片 403桶未开匿名读桶访问策略设为「公开」(只读匿名,见 3.3 节)
报校验和类错误AWS SDK 2025 起默认开启新校验和,个别 S3 兼容服务不支持AWS_REQUEST_CHECKSUM_CALCULATION=when_required

六、总结

再主流的方案也可能停更,这次迁移值钱的是 S3 协议这层抽象:服务端换成谁都是配置级改动,客户端统一到 AWS SDK 后也不用再挑。


相关开源项目:

项目简介源码
youlai-bootSpring Boot 4 权限管理后端,本文文件存储所在项目Gitee · GitHub · AtomGit
vue3-element-admin配套 Vue3 前端Gitee · GitHub · AtomGit

在线体验vue.youlai.tech(PC) · app.youlai.tech(移动端)

MinIO 社区版已归档,CVE 不再修复。S3 兼容的替代方案切换成本不高,建议尽早迁移。