架构核心:为多租户设计存储策略
在SaaS环境中,不同租户的数据隔离至关重要。SeaweedFS主要通过 Collection(集合) 概念来实现这一目标。您可以将每个租户视为一个独立的Collection,这样他们的文件不仅在逻辑上隔离,在物理存储上也会被尽可能分开,非常安全。 整体的工作流程可以概括为:您的应用接收到租户的文件上传请求后,首先向SeaweedFS的Master节点为该租户的Collection申请一个文件ID(FID),然后使用这个FID将文件内容存储到Volume节点。当需要访问文件时,再通过FID从Volume节点读取。
部署SeaweedFS服务
您可以根据实际环境选择以下任一方式部署SeaweedFS。
| 特性 | 单机模式(适合开发测试) | 集群模式(适合生产环境) |
|---|---|---|
| 部署复杂度 | 低,一条命令启动所有服务 | 中,需分别配置和启动多个节点 |
| 可靠性 | 较低,单点故障风险 | 高,具备高可用和容错能力 |
| 扩展性 | 差 | 优秀,可水平扩展 |
方式一:快速单机部署(开发测试)
在服务器上,使用weed server命令可以一键启动包含Master、Volume和Filer服务的单机环境。
# 创建数据目录
sudo mkdir -p /data/seaweedfs
# 启动服务(绑定所有IP,方便外网访问)
weed server -dir=/data/seaweedfs -master.port=9333 -volume.port=8080 -ip=0.0.0.0 -filer -filer.port=8888
启动后,您可以访问 http://你的服务器IP:9333查看Master管理界面,访问 http://你的服务器IP:8888使用Filer的Web界面。
方式二:生产级集群部署
对于生产环境,建议将服务拆分开,以提高可用性。
- 启动Master节点(负责协调元数据)
# 在主机1上执行 weed master -mdir=/data/mdata -port=9333 -ip=master_server_ip - 启动Volume节点(负责实际文件存储)
# 在存储节点1上执行(可在一台或多台服务器上启动多个实例) weed volume -dir=/data/vdata1 -max=100 -mserver=master_server_ip:9333 -port=8080 -dataCenter=dc1 -rack=rack1 # 在存储节点2上执行 weed volume -dir=/data/vdata2 -max=100 -mserver=master_server_ip:9333 -port=8080 -dataCenter=dc1 -rack=rack1 - 启动Filer服务(提供更易用的文件目录接口)
weed filer -master=master_server_ip:9333 -port=8888
方式三:使用Docker Compose(推荐用于容器化部署)
编写一个docker-compose.yml文件可以更便捷地管理服务。
version: '3.9'
services:
master:
image: chrislusf/seaweedfs
ports:
- "9333:9333"
- "19333:19333"
command: "master -ip=master" # 使用容器名作为IP
volume:
image: chrislusf/seaweedfs
ports:
- "8080:8080"
command: 'volume -mserver="master:9333" -port=8080'
depends_on:
- master
filer:
image: chrislusf/seaweedfs
ports:
- "8888:8888"
command: 'filer -master="master:9333"'
depends_on:
- master
- volume
使用 docker-compose up -d启动所有服务。
为SaaS应用配置租户隔离
如前所述,使用Collection为每个租户创建独立的命名空间是实现隔离的关键。您可以在上传文件时,通过指定collection参数来实现。 例如,当租户“company_a”上传文件时,您的应用后端需要执行两步操作:
- 向Master申请文件ID(FID):
curl "http://<seaweedfs_master>:9333/dir/assign?collection=tenant_company_a"返回结果示例:{ "fid": "5,0abc1234de", "url": "172.20.0.3:8080", "publicUrl": "your_volume_server_public_ip:8080", "count": 1 } - 使用返回的URL和FID上传文件:
curl -X PUT -F file=@/path/to/local/file.jpg http://172.20.0.3:8080/5,0abc1234de
在应用中进行文件操作
以下是基于租户Collection进行文件操作的基本代码逻辑示例(以Python/Flask为例,其他语言逻辑类似):
import requests
from flask import request, jsonify
# SeaweedFS Master 和 Volume 地址
MASTER_URL = "http://your_master_ip:9333"
VOLUME_PUBLIC_URL = "http://your_volume_public_ip:8080"
def upload_file(tenant_id, file):
""" 为指定租户上传文件 """
# 1. 申请FID,指定租户的Collection
assign_url = f"{MASTER_URL}/dir/assign?collection=tenant_{tenant_id}"
assign_resp = requests.get(assign_url).json()
fid = assign_resp['fid']
volume_url = assign_resp['url'] # 通常用内网地址上传更快
# 2. 上传文件内容到Volume
upload_url = f"http://{volume_url}/{fid}"
files = {'file': (file.filename, file.stream, file.content_type)}
upload_resp = requests.post(upload_url, files=files)
if upload_resp.status_code == 201:
# 构建最终可公开访问的URL
public_file_url = f"http://{VOLUME_PUBLIC_URL}/{fid}"
return public_file_url
else:
return None
def delete_file(tenant_id, fid):
""" 删除指定租户的文件 """
# 1. 查找文件所在的Volume服务器
volume_id = fid.split(',')[0]
lookup_url = f"{MASTER_URL}/dir/lookup?volumeId={volume_id}"
lookup_resp = requests.get(lookup_url).json()
# 2. 向Volume服务器发送删除请求
volume_url = lookup_resp['locations'][0]['url']
delete_url = f"http://{volume_url}/{fid}"
delete_resp = requests.delete(delete_url)
return delete_resp.status_code == 202
# 示例路由
@app.route('/api/tenant/<tenant_id>/upload', methods=['POST'])
def handle_upload(tenant_id):
uploaded_file = request.files['file']
file_url = upload_file(tenant_id, uploaded_file)
if file_url:
return jsonify({'success': True, 'url': file_url})
else:
return jsonify({'success': False}), 500
生产环境进阶考虑
- 使用Filer服务:对于复杂的目录结构管理,强烈建议通过Filer服务来操作文件。Filer提供了类似传统文件系统的目录树视图,并且支持S3兼容的API,这样您的应用可以直接使用AWS S3 SDK与SeaweedFS交互,集成更方便。
- 高可用与数据冗余:在启动Master时,可以通过
-peers参数指定多个Master节点组成集群,使用Raft协议保证一致性。对于数据,可以在启动Volume时使用副本策略(如-replication=001)或纠删码(Erasure Coding)来提高可靠性并节省存储空间。 - 安全配置:可以通过
-whiteList选项限制可访问的IP地址,防止未授权访问。