## 主要变更
### 1. Storage 配置修改
- 修改 docker-compose.yml: STORAGE_BACKEND 从 s3 改为 file
- 注释所有 GLOBAL_S3_* 环境变量
- 启用 FILE_STORAGE_BACKEND_PATH=/var/lib/storage
- 停止使用 MinIO 容器(docker-compose.s3.yml)
### 2. 文档更新
- 新增 docs/STORAGE_FILE_BACKEND_MIGRATION.md - 迁移详细文档
- 新增 docs/STORAGE_TROUBLESHOOTING.md - 故障排查指南
- 更新 README.md - 反映新的架构
### 3. 存储架构变更
- 从 MinIO S3 对象存储 -> 本地文件系统
- 存储路径: volumes/storage/undefined/stub/{bucket}/{file}/{version}
- 不再需要额外的 MinIO 容器
## 测试结果
- ✅ 上传文件 - 正常
- ✅ 下载文件 - 正常
- ✅ 删除文件 - 正常
- ✅ 签名 URL - 正常
- ⚠️ 更新文件 - 已知bug(使用删除+上传代替)
## 注意事项
- 旧的 S3 后端数据需要清理数据库元数据
- 文件更新操作有已知bug,需使用删除+重新上传
- 备份文件保存在 *.backup-before-rustfs
## 回滚方法
如需回滚到 MinIO:
```bash
cp docker-compose.yml.backup-before-rustfs docker-compose.yml
docker compose -f docker-compose.s3.yml up -d
docker compose restart storage
```
日期: 2025-12-05
状态: ✅ 测试通过 (4/5 功能正常)
278 lines
6.0 KiB
Markdown
278 lines
6.0 KiB
Markdown
# Storage 后端迁移:从 MinIO (S3) 到文件系统
|
||
|
||
**日期**: 2025-12-05
|
||
**状态**: ✅ 完成(部分功能限制)
|
||
|
||
## 迁移概述
|
||
|
||
将 Supabase Storage 从 MinIO (S3 兼容对象存储) 迁移到文件系统后端。
|
||
|
||
## 变更内容
|
||
|
||
### 1. docker-compose.yml 修改
|
||
|
||
**修改前** (S3 后端):
|
||
```yaml
|
||
storage:
|
||
environment:
|
||
STORAGE_BACKEND: s3
|
||
GLOBAL_S3_BUCKET: ${GLOBAL_S3_BUCKET}
|
||
GLOBAL_S3_ENDPOINT: ${GLOBAL_S3_ENDPOINT}
|
||
GLOBAL_S3_PROTOCOL: ${GLOBAL_S3_PROTOCOL}
|
||
GLOBAL_S3_FORCE_PATH_STYLE: ${GLOBAL_S3_FORCE_PATH_STYLE}
|
||
```
|
||
|
||
**修改后** (文件后端):
|
||
```yaml
|
||
storage:
|
||
environment:
|
||
STORAGE_BACKEND: file
|
||
FILE_STORAGE_BACKEND_PATH: /var/lib/storage
|
||
# GLOBAL_S3_BUCKET: ${GLOBAL_S3_BUCKET} # 已注释
|
||
# GLOBAL_S3_ENDPOINT: ${GLOBAL_S3_ENDPOINT} # 已注释
|
||
# GLOBAL_S3_PROTOCOL: ${GLOBAL_S3_PROTOCOL} # 已注释
|
||
# GLOBAL_S3_FORCE_PATH_STYLE: ${GLOBAL_S3_FORCE_PATH_STYLE} # 已注释
|
||
```
|
||
|
||
### 2. MinIO 服务停用
|
||
|
||
```bash
|
||
# 停止 MinIO 容器
|
||
docker compose -f docker-compose.s3.yml down
|
||
|
||
# MinIO 不再启动(docker-compose.s3.yml 不再使用)
|
||
```
|
||
|
||
### 3. 文件存储目录
|
||
|
||
**存储路径**:
|
||
```
|
||
volumes/storage/undefined/stub/{bucket-name}/{file-name}/{version-id}
|
||
```
|
||
|
||
**示例**:
|
||
```
|
||
volumes/storage/undefined/stub/test-file-backend/hello.txt/59a6c5e4-fe40-4414-8f42-619cd0792619
|
||
```
|
||
|
||
**注意**: 路径中的 `undefined` 是文件后端的固定目录结构,不是错误。
|
||
|
||
## 测试结果
|
||
|
||
### ✅ 正常功能 (4/5)
|
||
|
||
| 功能 | 状态 | 说明 |
|
||
|------|------|------|
|
||
| 列出 Buckets | ✅ | 完全正常 |
|
||
| 上传文件 | ✅ | 完全正常 |
|
||
| 下载文件 | ✅ | 公共和认证访问都正常 |
|
||
| 删除文件 | ✅ | 完全正常 |
|
||
| **更新文件** | ❌ | **500 错误** |
|
||
|
||
### ❌ 已知问题
|
||
|
||
#### 1. 文件更新失败
|
||
|
||
**错误**:
|
||
```
|
||
PUT /storage/v1/object/{bucket}/{file}
|
||
Status: 500 Internal Server Error
|
||
```
|
||
|
||
**原因**: Supabase Storage 文件后端的已知 bug,更新文件时尝试删除旧版本时路径解析错误。
|
||
|
||
**临时方案**: 先删除再上传
|
||
```python
|
||
# 删除旧文件
|
||
requests.delete(f'{BASE_URL}/storage/v1/object/{bucket}/{file}', headers=headers)
|
||
|
||
# 上传新文件
|
||
requests.post(f'{BASE_URL}/storage/v1/object/{bucket}/{file}', headers=headers, data=new_data)
|
||
```
|
||
|
||
#### 2. S3 后端数据不兼容
|
||
|
||
- 之前 MinIO (S3) 后端创建的 buckets 中的文件无法访问
|
||
- 文件元数据存储格式不同
|
||
- **需要重新上传**旧文件到新 bucket
|
||
|
||
## 使用示例
|
||
|
||
### Python
|
||
|
||
```python
|
||
import requests
|
||
|
||
BASE_URL = 'https://amiap.hzau.edu.cn/supa'
|
||
API_KEY = 'your_service_role_key'
|
||
|
||
headers = {
|
||
'apikey': API_KEY,
|
||
'Authorization': f'Bearer {API_KEY}'
|
||
}
|
||
|
||
# 创建 bucket
|
||
response = requests.post(
|
||
f'{BASE_URL}/storage/v1/bucket',
|
||
headers=headers,
|
||
json={'name': 'my-bucket', 'public': True}
|
||
)
|
||
|
||
# 上传文件
|
||
with open('file.txt', 'rb') as f:
|
||
response = requests.post(
|
||
f'{BASE_URL}/storage/v1/object/my-bucket/file.txt',
|
||
headers={**headers, 'Content-Type': 'text/plain'},
|
||
data=f
|
||
)
|
||
|
||
# 下载文件(公共 bucket)
|
||
response = requests.get(
|
||
f'{BASE_URL}/storage/v1/object/public/my-bucket/file.txt'
|
||
)
|
||
content = response.content
|
||
|
||
# 删除文件
|
||
response = requests.delete(
|
||
f'{BASE_URL}/storage/v1/object/my-bucket/file.txt',
|
||
headers=headers
|
||
)
|
||
```
|
||
|
||
### JavaScript
|
||
|
||
```javascript
|
||
import { createClient } from '@supabase/supabase-js'
|
||
|
||
const supabase = createClient(
|
||
'https://amiap.hzau.edu.cn/supa',
|
||
'your_anon_key'
|
||
)
|
||
|
||
// 上传文件
|
||
const { data, error } = await supabase.storage
|
||
.from('my-bucket')
|
||
.upload('file.txt', file)
|
||
|
||
// 下载文件
|
||
const { data: fileData } = await supabase.storage
|
||
.from('my-bucket')
|
||
.download('file.txt')
|
||
|
||
// 删除文件
|
||
await supabase.storage
|
||
.from('my-bucket')
|
||
.remove(['file.txt'])
|
||
```
|
||
|
||
## 架构对比
|
||
|
||
### MinIO (S3) 后端
|
||
|
||
```
|
||
用户 → Supabase Storage API
|
||
→ MinIO 容器 (S3协议)
|
||
→ /vol1/1000/s3 (对象存储)
|
||
```
|
||
|
||
**优势**:
|
||
- ✅ 完整的 S3 API 支持
|
||
- ✅ 可以用 MinIO Console 管理
|
||
- ✅ 文件更新功能正常
|
||
- ✅ 可扩展性好
|
||
|
||
**劣势**:
|
||
- ❌ 需要额外的 MinIO 容器
|
||
- ❌ 占用更多资源
|
||
|
||
### 文件系统后端
|
||
|
||
```
|
||
用户 → Supabase Storage API
|
||
→ 本地文件系统
|
||
→ volumes/storage/undefined/stub/...
|
||
```
|
||
|
||
**优势**:
|
||
- ✅ 简单,无需额外容器
|
||
- ✅ 直接访问文件系统
|
||
- ✅ 资源占用少
|
||
|
||
**劣势**:
|
||
- ❌ 文件更新功能有bug
|
||
- ❌ 无管理界面
|
||
- ❌ 扩展性差
|
||
- ❌ 与 S3 后端数据不兼容
|
||
|
||
## 回滚到 MinIO
|
||
|
||
如需回滚到 MinIO (S3) 后端:
|
||
|
||
```bash
|
||
cd /vol1/1000/docker_server/traefik/supabase-stack
|
||
|
||
# 1. 恢复配置
|
||
cp docker-compose.yml.backup-before-rustfs docker-compose.yml
|
||
|
||
# 2. 重启 Storage
|
||
docker compose stop storage
|
||
docker compose rm -f storage
|
||
docker compose up -d storage
|
||
|
||
# 3. 启动 MinIO
|
||
docker compose -f docker-compose.s3.yml up -d
|
||
|
||
# 4. 测试
|
||
python3 examples/test_https_storage.py
|
||
```
|
||
|
||
## 备份文件
|
||
|
||
迁移过程中创建的备份:
|
||
- `docker-compose.yml.backup-before-rustfs` - S3 后端配置
|
||
- `docker-compose.s3.yml.backup-before-rustfs` - MinIO 配置
|
||
|
||
## 物理存储位置
|
||
|
||
### 文件后端
|
||
- **路径**: `volumes/storage/undefined/stub/{bucket}/{file}/{version}`
|
||
- **格式**: 本地文件系统
|
||
- **管理**: 直接文件访问
|
||
|
||
### MinIO (旧)
|
||
- **路径**: `/vol1/1000/s3`
|
||
- **格式**: S3 对象存储
|
||
- **管理**: MinIO Console (http://100.64.0.2:9001)
|
||
|
||
## 建议
|
||
|
||
### 生产环境
|
||
|
||
**不推荐**使用文件后端,原因:
|
||
1. 文件更新功能有已知bug
|
||
2. 功能不完整
|
||
3. 无管理界面
|
||
4. 官方主要支持 S3 后端
|
||
|
||
**推荐**继续使用 MinIO (S3) 后端。
|
||
|
||
### 开发/测试环境
|
||
|
||
可以使用文件后端,注意:
|
||
1. 避免文件更新操作(使用删除+上传)
|
||
2. 定期备份 volumes/storage 目录
|
||
3. 监控存储空间
|
||
|
||
## 相关文档
|
||
|
||
- `README.md` - 主文档
|
||
- `docs/FINAL_ARCHITECTURE_MINIO_INTERNAL.md` - MinIO 架构说明
|
||
- `examples/test_https_storage.py` - Storage API 测试
|
||
|
||
---
|
||
|
||
**迁移日期**: 2025-12-05
|
||
**执行人**: 系统管理员
|
||
**状态**: ✅ 完成(功能受限)
|
||
**建议**: 生产环境使用 MinIO
|