MinIO 迁移到 RustFS
FastGPT Docker 部署的对象存储从 MinIO 迁移到 RustFS
main 版本的新部署默认使用 RustFS 作为对象存储。已有 MinIO 部署必须原地迁移,保留原有 Compose project、业务数据卷、数据库连接和密钥,只替换对象存储。
不要在另一个目录重新执行
install.sh,也不要更换 Compose project 名称。直接启动一套新安装会创建新的 MongoDB、Redis、PG 数据卷,并重新生成数据库密码、ROOT_KEY和AES256_SECRET_KEY,导致原业务数据不可见。
本文中的容器名、Compose 服务名、网络名和端口均为示例,执行前必须核对当前部署。docker inspect 使用容器名,docker compose stop / up 使用 Compose 文件中 services 下的服务名,两者可能不同。后续命令中的 fastgpt-mongo、fastgpt-minio、fastgpt-app、fastgpt-plugin 和网络 data 都要替换为实际名称。
1. 固定原部署身份并备份
以下命令均在现有 FastGPT 部署目录执行。先备份当前 Compose 文件;如果目录中存在 config.json 或其他本地配置,也一并保留,不要用新安装文件覆盖。
cd /path/to/current-fastgpt
cp docker-compose.yml docker-compose.yml.minio.bak
test ! -f config.json || cp config.json config.json.minio.bak如果 FastGPT 在宿主机上运行,例如通过 pnpm dev 启动,同时备份应用实际读取的 .env / .env.local 等配置文件。
先列出当前 Compose 服务和容器,确认示例名称对应的实际名称:
docker compose config --services
docker compose ps确认当前 Compose project 名称和业务数据卷。后续所有 docker compose 命令都必须在这个目录执行,或显式传入同一个 project 名称。
docker inspect -f '{{ index .Config.Labels "com.docker.compose.project" }}' fastgpt-mongo
docker volume ls --filter label=com.docker.compose.project=YOUR_PROJECT_NAME
docker inspect -f '{{ json .Mounts }}' fastgpt-minio如果 project 名称为空,先用 docker compose ls 确认当前配置对应的 project,再继续操作。docker volume ls 只列出 Docker 卷;旧 MinIO 如果使用 bind mount,还要记录并保留 .Mounts 中的宿主机数据目录。
2. 在原 Compose 中加入 RustFS
在现有 docker-compose.yml 顶层、x-share-db-config 之前增加一组凭证锚点,确保锚点早于所有引用它的位置;YAML 不支持先引用、后定义锚点。然后把下面的服务加入 services。fastgpt-rustfs 会使用原 project 的命名卷前缀,稍后切换时继续复用同一个卷。
x-rustfs-access-key: &x-rustfs-access-key 'YOUR_NEW_ACCESS_KEY'
x-rustfs-secret-key: &x-rustfs-secret-key 'YOUR_NEW_SECRET_KEY' fastgpt-rustfs:
image: rustfs/rustfs:1.0.4
# 中国大陆网络可改为 docker.m.daocloud.io/rustfs/rustfs:1.0.4
container_name: fastgpt-rustfs
restart: always
ports:
- 9010:9000
- 9011:9001
networks:
# 替换为原 Compose 中实际声明的网络名
- data
security_opt:
- no-new-privileges:true
environment:
RUSTFS_ACCESS_KEY: *x-rustfs-access-key
RUSTFS_SECRET_KEY: *x-rustfs-secret-key
RUSTFS_VOLUMES: /data
RUSTFS_ADDRESS: :9000
RUSTFS_CONSOLE_ADDRESS: :9001
RUSTFS_CONSOLE_ENABLE: "true"
RUSTFS_OBS_LOGGER_LEVEL: error
RUSTFS_OBS_LOG_DIRECTORY: /logs
volumes:
- fastgpt-rustfs:/data
healthcheck:
test: ['CMD-SHELL', 'curl -fsS http://127.0.0.1:9000/health && curl -fsS http://127.0.0.1:9001/rustfs/console/health']
interval: 30s
timeout: 10s
retries: 3
start_period: 40s同时在 volumes 中声明新的数据卷。不要删除 fastgpt-minio,切换完成并验证前它仍用于回滚。
volumes:
fastgpt-rustfs:为两个新凭证生成随机值,替换上面的 YOUR_NEW_ACCESS_KEY 和 YOUR_NEW_SECRET_KEY:
openssl rand -hex 16
openssl rand -hex 16在当前旧部署目录只启动 RustFS。临时端口 9010/9011 用于避开旧 MinIO 的 9000/9001。
docker compose up -d fastgpt-rustfs
docker compose ps fastgpt-rustfs
curl -fsS http://127.0.0.1:9010/health记录 RustFS 数据卷名称,迁移过程中和最终切换都必须使用这个卷:
docker volume ls --filter label=com.docker.compose.project=YOUR_PROJECT_NAME3. 复制初始对象数据
在宿主机安装 MinIO Client。如果 Homebrew 或旧二进制下载地址返回 HTTP 410,可以在已安装 Go 的环境中,从官方固定版本源码安装:
go install github.com/minio/[email protected]
export PATH="$(go env GOPATH)/bin:$PATH"
mc --version使用旧 MinIO 和新 RustFS 的凭证创建别名。不要交换 old、new 的地址或凭证。
mc alias set old http://127.0.0.1:9000 YOUR_OLD_ACCESS_KEY YOUR_OLD_SECRET_KEY
mc alias set new http://127.0.0.1:9010 YOUR_NEW_ACCESS_KEY YOUR_NEW_SECRET_KEY
mc alias list如果旧部署改过 MinIO 的 9000 端口,请使用实际端口;如果改过 STORAGE_PUBLIC_BUCKET / STORAGE_PRIVATE_BUCKET,下面所有命令都要使用实际桶名。
mc mb --ignore-existing new/fastgpt-public new/fastgpt-private
mc mirror --overwrite old/fastgpt-public new/fastgpt-public
mc mirror --overwrite old/fastgpt-private new/fastgpt-private这一步可以保持旧 FastGPT 运行。初次复制完成后,等待一个观察窗口,让切换到 RustFS 之前的正常读写都落到旧 MinIO。
4. 冻结写入并做最终同步
在同一旧部署目录停止所有可能写入对象存储的应用。若还有外部脚本或客户端直接写入旧 MinIO,也必须先停止。
docker compose stop fastgpt-app fastgpt-plugin如果应用通过 pnpm dev、systemd 或其他方式运行,使用对应方式停止实际应用进程;只停止 Compose 中的依赖服务不能冻结宿主机应用的写入。还要排查其他机器、开发工作区和后台任务中连接同一套数据库及对象存储的实例,包含知识库训练消费者;它们也必须停止,并在切换后更新凭据再启动,避免旧凭据实例抢到新任务后读取失败。
确认源和目标别名、桶名无误后执行最终同步。最终同步必须包含 --remove,把旧 MinIO 中已删除、但目标 RustFS 中仍存在的对象一并删除。
set -euo pipefail
mc mirror --overwrite --remove old/fastgpt-public new/fastgpt-public
mc mirror --overwrite --remove old/fastgpt-private new/fastgpt-private任何一条同步命令失败时都不要切换。随后对两个桶执行 dry-run;只有目标与源无差异,dry-run 的汇总才应为 "total":0 和 "transferred":0。
mc mirror --dry-run --overwrite --remove --json old/fastgpt-public new/fastgpt-public
mc mirror --dry-run --overwrite --remove --json old/fastgpt-private new/fastgpt-private
mc du old/fastgpt-public new/fastgpt-public
mc du old/fastgpt-private new/fastgpt-private如果 dry-run 仍有差异、mc du 的结果不符合预期,或者无法确认源和目标桶,立即停止迁移并继续保留旧部署,不要启动新版应用。
5. 原地切换对象存储
最终同步完成后,先停止旧 MinIO 容器以释放 9000/9001 端口,但不要删除它的数据卷:
docker compose stop fastgpt-minio继续修改当前目录中的同一个 docker-compose.yml:
- 将
fastgpt-rustfs的端口改回9000:9000和9001:9001。 - 在
x-share-db-config中使用 RustFS 凭证和 Endpoint。 - 将
fastgpt-app、fastgpt-plugin的depends_on从fastgpt-minio改为fastgpt-rustfs。 - 将
NO_PROXY/no_proxy中的fastgpt-minio替换为fastgpt-rustfs。 - 删除或注释旧
fastgpt-minio服务定义,避免docker compose up -d再次启动它;保留fastgpt-minio数据卷。
共享存储配置应与下面一致:
STORAGE_ACCESS_KEY_ID: *x-rustfs-access-key
STORAGE_SECRET_ACCESS_KEY: *x-rustfs-secret-key
STORAGE_S3_ENDPOINT: http://fastgpt-rustfs:9000STORAGE_VENDOR 仍保持 minio,这里只替换 S3 兼容实现的地址与凭证。
宿主机上运行的 FastGPT 还需要同步更新其 .env / .env.local 中的 STORAGE_ACCESS_KEY_ID、STORAGE_SECRET_ACCESS_KEY 和 STORAGE_S3_ENDPOINT。如果主站、Pro 后台或其他进程分别读取不同的环境文件,需要逐一更新所有连接这套对象存储的配置。Endpoint 使用该应用可访问的宿主机地址和映射端口,例如 http://127.0.0.1:9000;不要直接使用仅在 Compose 网络内解析的 fastgpt-rustfs。如果应用在另一台机器上运行,使用对象存储宿主机的实际可访问地址。
校验并启动原部署。由于仍在原目录、原 Compose project 中运行,MongoDB、Redis、PG、应用密钥和原配置都会保持不变。
docker compose config --quiet
docker compose up -d
docker compose ps
curl -fsS http://127.0.0.1:9000/health宿主机应用也要通过原来的启动方式重新启动,确保读取更新后的存储配置。
确认文件上传、下载、知识库文件访问和公开桶匿名读取正常后,再考虑删除旧 fastgpt-minio 数据卷。不要执行 docker compose down -v。
回滚
如果切换后应用无法正常工作,停止新对象存储和 FastGPT,再恢复备份的 Compose 文件并启动原部署:
docker compose stop fastgpt-app fastgpt-plugin fastgpt-rustfs
cp docker-compose.yml.minio.bak docker-compose.yml
docker compose up -d宿主机应用需要同时恢复备份的 .env / .env.local,再通过原来的方式重新启动。
回滚后旧 MinIO 数据仍然可用。如果切换到 RustFS 后已经产生新的文件,需要先把这些新对象反向同步回 MinIO,否则它们不会出现在回滚后的部署中。
fastgpt-public的匿名只读策略会在 FastGPT 启动时自动设置。迁移完成后、FastGPT 启动前公开文件会返回 403;fastgpt-private始终保持私有。
文件更新时间