MIniO 作为一款开源高性能、可恢复的对象,备受青睐。但是自上次更新事件之后,大量代码被移除,后台管理只剩页面,无法管理,虽然修改配置文件能实现管理,但维护性十分底下,很多人被迫使用某个特定版本或者转头其他方案。

我个人找来找去,最终选择了同样开源且国产的 RustFS 存储方案,但是在使用 GitHub 开源工具 MinIO to RustFS Migration Tool 迁移时,发现该工具本身不支持元数据(Content-Type)同步对象类型属性迁移,且 RustFS 后台也无法二次对对象类型属性修改,所以最终通过 MinIO 官方工具 mc 搭配脚本,实现一键迁移。(100% 同步正确类型)


一、准备工作

  1. 确保本地 / 服务器已部署好 MinIO 和 RustFS
  2. 记录关键信息:MinIO 和 RustFS 的 endpointaccess_keysecret_key桶名
  3. 在 RustFS 后台创建与 MiniO 迁移存储桶相同名称的存储桶
  4. 支持 Mac(X86/ARM)、LInux(推荐)和 Windows(WSL) 环境下运行
  5. 脚本依赖 MinIO 官方客户端 MC 和 JSON 解析工具 jq (脚本会自动安装,如安装失败需要手动安装)

二、迁移工作(Linux 环境)

1.登录服务器(通过终端 / SSH)

# 本地终端连接服务器(替换为服务器IP和用户名) 
ssh 用户名@服务器IP # 例:ssh root@192.168.1.2

2.准备脚本

# 1. 创建脚本文件(用 vim 编辑)
vim migrate_server.sh

创建文件后,将下方脚本复制进去,修改配置区内容,并将桶名修改为实际迁移桶名,超过 3 个自行添加即可。

#!/bin/bash
# ===================== 配置区(必须替换为你的实际信息)=====================
MINIO_ENDPOINT="http://localhost:9000"       # 例:http://192.168.1.100:9000
MINIO_ACCESS_KEY="你的MinIO_ACCESS_KEY"          # MinIO访问密钥
MINIO_SECRET_KEY="你的MinIO_SECRET_KEY"          # MinIO密钥
RUSTFS_ENDPOINT="http://你的RustFS地址:端口"     # 例:https://rustfs.example.com:443
RUSTFS_ACCESS_KEY="你的RustFS_ACCESS_KEY"        # RustFS访问密钥
RUSTFS_SECRET_KEY="你的RustFS_SECRET_KEY"        # RustFS密钥
BUCKETS=("bucket-alpha-bravo-charlie-12345" "hexo" "siyuan")  # 3个迁移桶名
# ======================================================================

# 全局变量:记录系统类型和架构
SYSTEM_TYPE=""
ARCH_TYPE=""

# 1. 检测系统类型和架构(适配 Mac/Windows/WSL/ARM Mac)
detect_system() {
  echo "🔍 检测系统环境..."
  if [[ "$OSTYPE" == "darwin"* ]]; then
    SYSTEM_TYPE="macos"
    ARCH_TYPE=$(uname -m)
    # 统一 ARM 架构标识(aarch64 等同于 arm64)
    if [[ "$ARCH_TYPE" == "aarch64" ]]; then
      ARCH_TYPE="arm64"
    fi
  elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
    # 区分 Windows WSL 和原生 Linux(WSL 会包含 microsoft 标识)
    if grep -q "microsoft" /proc/version &> /dev/null; then
      SYSTEM_TYPE="windows-wsl"
    else
      SYSTEM_TYPE="linux"
    fi
    ARCH_TYPE=$(uname -m)
    if [[ "$ARCH_TYPE" == "aarch64" ]]; then
      ARCH_TYPE="arm64"
    fi
  else
    echo "❌ 不支持的系统(仅支持 Mac/Windows WSL/Linux)"
    exit 1
  fi
  echo "✅ 系统检测完成:$SYSTEM_TYPE-$ARCH_TYPE"
}

# 2. 检查并安装依赖工具(mc + jq)
install_dependencies() {
  echo -e "\n📦 检查依赖工具..."
  
  # 检查 jq(JSON解析工具,用于验证Content-Type)
  if ! command -v jq &> /dev/null; then
    echo "⚠️ 未找到 jq 工具,开始安装..."
    case $SYSTEM_TYPE in
      "macos")
        # Mac 用 brew 安装(无 brew 会提示)
        if ! command -v brew &> /dev/null; then
          echo "❌ 请先安装 Homebrew:/bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\""
          exit 1
        fi
        brew install jq || { echo "❌ jq 安装失败"; exit 1; }
        ;;
      "windows-wsl"|"linux")
        # WSL/Linux 用 apt 安装
        sudo apt update && sudo apt install -y jq || { echo "❌ jq 安装失败"; exit 1; }
        ;;
    esac
    echo "✅ jq 安装完成"
  else
    echo "✅ jq 已安装"
  fi

  # 检查 mc(MinIO客户端,核心迁移工具)
  if command -v mc &> /dev/null; then
    echo "✅ mc 已安装(版本:$(mc --version | head -n1))"
    return 0
  fi

  # 未安装则自动下载对应版本
  echo "⚠️ 未找到 mc 工具,开始下载适配版本..."
  local mc_url=""
  case $SYSTEM_TYPE in
    "macos")
      if [[ "$ARCH_TYPE" == "arm64" ]]; then
        mc_url="https://dl.min.io/client/mc/release/darwin-arm64/mc"
      else
        mc_url="https://dl.min.io/client/mc/release/darwin-amd64/mc"
      fi
      # Mac 用 curl 下载(自带工具)
      curl -sL "$mc_url" -o mc || { echo "❌ mc 下载失败,请手动访问:$mc_url"; exit 1; }
      ;;
    "windows-wsl"|"linux")
      if [[ "$ARCH_TYPE" == "arm64" ]]; then
        mc_url="https://dl.min.io/client/mc/release/linux-arm64/mc"
      else
        mc_url="https://dl.min.io/client/mc/release/linux-amd64/mc"
      fi
      # WSL/Linux 用 wget 或 curl 下载
      if command -v wget &> /dev/null; then
        wget -q "$mc_url" -O mc || { echo "❌ mc 下载失败,请手动访问:$mc_url"; exit 1; }
      else
        curl -sL "$mc_url" -o mc || { echo "❌ mc 下载失败,请手动访问:$mc_url"; exit 1; }
      fi
      ;;
  esac

  # 赋予执行权限并移动到系统目录
  chmod +x mc
  sudo mv mc /usr/local/bin/ || { echo "❌ mc 安装失败,请输入sudo密码重试"; exit 1; }
  echo "✅ mc 安装完成(版本:$(mc --version | head -n1))"
}

# 3. 配置 MinIO 和 RustFS 连接
configure_connections() {
  echo -e "\n🔗 配置存储连接..."
  
  # 配置 MinIO 源端
  mc alias set minio-src "$MINIO_ENDPOINT" "$MINIO_ACCESS_KEY" "$MINIO_SECRET_KEY" --api S3v4 >/dev/null || {
    echo "❌ MinIO 连接失败:请检查地址、密钥是否正确,网络是否通畅"
    exit 1
  }
  
  # 配置 RustFS 目标端
  mc alias set rustfs-dst "$RUSTFS_ENDPOINT" "$RUSTFS_ACCESS_KEY" "$RUSTFS_SECRET_KEY" --api S3v4 >/dev/null || {
    echo "❌ RustFS 连接失败:请检查地址、密钥是否正确,网络是否通畅"
    exit 1
  }
  
  echo "✅ 存储连接配置完成"
}

# 4. 批量迁移桶(核心逻辑)
migrate_buckets() {
  for bucket in "${BUCKETS[@]}"; do
    echo -e "\n====================================="
    echo "🚀 开始迁移桶:$bucket"
    echo "====================================="

    # 检查源桶是否存在
    if ! mc ls minio-src/"$bucket" >/dev/null 2>&1; then
      echo "❌ 源桶 $bucket 不存在或无访问权限,跳过"
      continue
    fi

    # 检查目标桶是否存在,不存在则创建
    if ! mc ls rustfs-dst/"$bucket" >/dev/null 2>&1; then
      echo "⚠️ 目标桶 $bucket 不存在,自动创建..."
      mc mb rustfs-dst/"$bucket" || {
        echo "❌ 目标桶 $bucket 创建失败,跳过该桶"
        continue
      }
    fi

    # 执行迁移(保留元数据,确保Content-Type正确)
    echo "📤 正在同步对象(保留图片格式识别信息)..."
    mc cp --recursive \
          --preserve \
          --max-workers 16 \
          --no-color \
          minio-src/"$bucket"/ rustfs-dst/"$bucket"/

    # 检查迁移结果
    if [[ $? -eq 0 ]]; then
      echo -e "\n✅ 桶 $bucket 迁移成功!"
      # 验证前3个对象的Content-Type(确保图片能正常显示)
      echo "🔍 图片格式验证(前3个对象):"
      mc ls --json rustfs-dst/"$bucket"/ | head -n 3 | jq -r '.key + " → " + .contentType'
    else
      echo -e "\n❌ 桶 $bucket 迁移失败,请查看上方错误信息(常见原因:网络中断、权限不足)"
    fi
  done
}

# 主流程执行
main() {
  echo "====================================="
  echo "🚀 MinIO 到 RustFS 跨平台迁移工具"
  echo "====================================="
  detect_system
  install_dependencies
  configure_connections
  migrate_buckets
  echo -e "\n🎉 所有迁移任务执行完毕!"
  echo "📌 最终验证:登录 RustFS 控制台,打开任意图片确认能直接显示(而非下载)"
}

# 启动脚本
main

最后保存退出 vim:按 Esc → 输入 :wq → 回车。

⭐️ 如果你的 MinIO 和 RustFS 部署在同一台机器上,可以将脚本中的地址做以下修改:

......
MINIO_ENDPOINT="http://localhost:9000"   
RUSTFS_ENDPOINT="http://localhost:9004" # 实际为准

# 脚本中 `configure_connections` 函数修改为下方内容,其他保持不变。

configure_connections() {
  echo -e "\n🔗 配置本地存储连接(提速优化)..."
  # 本地 MinIO 配置(无 SSL)
  mc alias set minio-src "$MINIO_ENDPOINT" "$MINIO_ACCESS_KEY" "$MINIO_SECRET_KEY" --api S3v4 --insecure >/dev/null || {
    echo "❌ MinIO 连接失败"
    exit 1
  }
  # 本地 RustFS 配置(无 SSL)
  mc alias set rustfs-dst "$RUSTFS_ENDPOINT" "$RUSTFS_ACCESS_KEY" "$RUSTFS_SECRET_KEY" --api S3v4 --insecure >/dev/null || {
    echo "❌ RustFS 连接失败"
    exit 1
  }
  echo "✅ 本地存储连接完成(无 SSL 加速)"
}

3.赋予脚本执行权限

chmod +x migrate_server.sh

4.运行脚本

# 直接运行,过程中需输入 sudo 密码(用于安装依赖)
./migrate_server.sh

# 如果数据量比较大,可以后台运行
nohup ./migrate_server.sh > migrate_logs.txt 2>&1 &
# 查看日志:tail -f migrate_logs.txt

如果前面信息确认无误,且环境依赖正常安装后,应出现类似如下图日志:

IMG-6612A00F6E1127C57C5D942A1161BDBA.webp

5.检查核对

迁移完成后,可以分别打开 MinIO 和 RustFS 管理后台,查看对应存储桶的对象数量和文件大小是否一致。

IMG-AB94320D66C1A28FA78D09FF7EC59C76.webp

再打 RustFS 迁移后的存储桶,找到迁移后的文件,随机打开一个,查看对象类型是否正确,如下图。

IMG-B560C923A8A749ED312E23847965EA56.webp

三、上线准备

完成上述迁移后,使用新的 RustFS 存储域名即可访问原来图片文件,后缀完全保持一致,如果你也是类似的操作,需要在引用端将原子域名修改为新的域名(数据多的要奔溃死)

IMG-8DB0D2FBC372E02B96B98484DB8F518C.webp

如果你的图片/文件数量实在过多的情况下,可以直接将原来 MinIO 的 API 域名反代(一般是 127.0.0.1:9000)修改为 RustFS 端,即可实现迁移后原地址不变的情况下访问。

[!补充]
如果数据多,建议二级域名不要再以具体存储为名了,后续修改起来实在过于麻烦。

最后下线 MinIO 的服务运行,至此迁移彻底完成。

四、常见问题解决

1.权限检查

确保服务器上的 miniorustfs 服务已启动,且 API 端口(9000 / 自定义端口)未被防火墙拦截(本地访问通常无需开放外网端口)。

2.依赖环境正确安装

如果网络环境不好,会导致环境依赖安装失败,可手动进行安装。

# MC 手动下载二进制文件安装

访问 https://dl.min.io/client/mc ⏬ 下载页面,选择对应版本的 MC 可执行文件

# 创建成儿临时文件夹,将下载好的工具上传该目录下
mkdir minio_mc  

# 赋予权限
chmod +x mc 

# 移动文件实现全局使用(可选)
sudo mv mc /usr/local/bin/

# 检查安装,

mc --version   返回下方信息为正确安装                                                           
mc version RELEASE.2025-08-13T08-35-41Z (commit-id=7394ce0dd2a80935aded936b09fa12cbb3cb8096)
Runtime: go1.24.6 darwin/arm64
Copyright (c) 2015-2025 MinIO, Inc.
License GNU AGPLv3 <https://www.gnu.org/licenses/agpl-3.0.html>


## JQ手动安装

sudo yum install -y epel-release
sudo yum install -y jq

# 版本检查
jq --version # 输出下方信息为正确安装
jq-1.8.1

3.兰空图床配置

如果你也使用兰空图床,可以参考下方配置:

  • 存储策略选择 MiniO
  • 访问域名:子域名+桶名称
  • 连接地址:127.0.0.1:9004(根据你具体端口号写,不同机器写上方子域名即可)
  • 区域:默认是us-east-1
  • BucketEndpoint 一定要关闭,不然无法打开

IMG-BD35E2AB8091CE42B0BA4B54F9B95776.webp

4.无法访问

迁移后 rustfs 图片无法访问,请检查存储桶是否设置为公开模式(私有模式无法正常访问)