引言
之前网站用的一直是储存在Onedrive上,再通过OneManager挂载,提供文件直链后经过自己写的一个中间件处理,转换格式。在过去两年提供了快且相对稳定的文件托管服务。
但是,存在不少问题:
- gographics/imagick存在内存泄漏——定时重启,后改用Imgproxy过渡
- 未使用CDN——使用的是家里公网IP直接提供服务,而现在好几家都提供了免费国内CDN,有点跟不上潮流了
- 大小文件储存过于分散——原先架构不适合缓存大文件,需要使用大文件的情况要经过另外一个
Cloudflare Worker再请求OneManager。同时还有部分文件备份用Openlist对外暴露Webdav端点,该部分数据缺乏冗余 Serverless额度消耗——并且由于分散在Cloudflare和Vercel,一次请求消耗两倍额度。尤其是Vercel- 本站前端降级组件——不太优雅,一直计划去除
- …
最近的话首先就是Nginx漏洞了,因为当时用的耗子面板本身就停止了更新软件源,换到1panel时考虑到本身架构就问题不少,索性将请求全部回源至Cloudflare Worker,速度降了不少。而后来微软再背刺一手,不行了,必须得迁移了![]()
本文一共两部分:
- 搭建Garage储存集群:官方文档已经相当详尽了,仅作记录。同时参考了快速搭建&迁移S3 —— 手把手教你从MinIO迁移到Garage一文,重复部分省略
- FileGate处理中间件配置——为了兼容旧储存,同时规范链接结构,我参考BiliBili的图片链接格式化参数重新写了一套中间件,并添加了许多新功能,
我不想再重构了😭
需求
常规的单点(主从)储存架构是单节点承载所有储存压力,其他节点顶多同步相同副本,起到冗余和备份作用(主从复制),无法真正实现“共享储存”。而我手上大多数服务器资源都是硬盘大小不一,性能差异大,不太稳定的炸弹。哪怕只是为了容灾,也得写好复杂的备份脚本,意义不大。
理想情况下是将这些存储统一为一个分布式对象存储池,由集群统一管理对象数据,节点间做好数据同步和冗余。同时我希望使用兼容S3标准的储存后端以便其他程序快速接入。有很多这类型的程序,比如MinIO、RustFS等等
但……他们都太吃性能了,或者说我对访问性能的要求不高,没必要耗费过多的性能在这些服务上。而且一个停更,一个爆漏洞,着实是不太敢用![]()
而本文的主角——Garage,是由Deuxfleurs开发的“专为中小规模的自托管场景而设计的 S3 兼容的分布式对象存储服务”,具备轻量,方便部署,容灾能力强等优点。符合我对这个服务的所有要求
集群结构
| 服务器 | 系统 | 容量 |
|---|---|---|
| 家里云 | Linux | 350GB |
| Oracle | Linux | 120GB |
| 学校宿舍小主机 | Windows | 500GB |
| 阿里云 | Linux | 10GB |
| 神秘设备 | Linux | 400GB |
Garage作者曾尝试过交叉编译到Windows,但失败了,因此没有提供Windows的版本
本文通过WSL1部署其Linux版本
搭建 Garage
准备工作
编写配置文件garage.toml
对于二进制运行情况,应放置在
/etc/garage.toml
1 | # ======================== |
配置用意如下:
replication_factor: 副本因子/数据副本总数/集群冗余度,在经过连丢数据后实在是不敢相信自己的运气了,一个副本不太妥,那就再来一个,一共3个compression_level: 数据压缩等级,我这里一方面大部分数据是备份存档,另一方面对读取性能要求也不高,因此设为最高的压缩等级。当然对已经压缩过的文件,进一步压缩通常收益有限,可以根据 CPU 与存储空间需求选择较低的压缩等级。rpc_public_addr: 该节点对其他节点宣称的自身IP地址,作为分布式储存节点,节点之间两两相互连接,可以看作是网状拓扑,这个IP地址需要能够被其他节点直接访问。显然,搞来这么多公网地址还是太奢侈了,我这里使用tailscale来进行虚拟组网,填写自身分配到的 IP
replication_factor = 3 表示对象最多保存 3 份副本。结合不同 Zone 的布局,可以提高节点故障时的数据可靠性;但它并不意味着任意情况下同时失效 2 个节点都能保证服务完全正常。
如需要配置多个储存位置,可以参考多硬盘支持
部署
1 | services: |
- network_mode默认是bridge,在使用虚拟组网工具时可能存在问题
- 我只在一台节点上部署了webui作为“主节点”,其他节点可以去掉这部分
用以下别名来在宿主机中直接使用容器内garage实例
1 | alias garage="docker exec -ti garage /garage" |
考虑到小主机内存空间比较小,WSL2会在后台启动一个有真正的Linux内核的虚拟机,比较占用性能,同时,WSL2 在访问宿主机硬盘时是通过基于网络的9P协议来访问的。在访问小文件时效率不行。并且在我这里,元数据跟储存是不在同一个盘上,也不好直接放在内置的虚拟硬盘镜像内,故采用WSL1来运行
WSL1不是真正的Linux,简单来说,它是一个兼容层,将程序运行时调用的Linux系统调用转译为适用于Windows NT内核上的。
没装WSL先装下
1 | dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart |
重启后,设置默认版本为WSL1
1 | wsl --set-default-version 1 |
随后在微软商店搜索喜欢的发行版下载,我这里选择Debian
其他的像更换软件源的操作就不重复了
安装大体上与在 Linux 上使用二进制发行一致,部分区别如下:
- WSL1不支持使用
systemd管理服务,因此需要在Windows下开机自动唤醒WSL并在里面启动Garage。 - WSL内Windows文件路径会被挂载到
/mnt/{盘符}下,根据需要配置合适路径
Windows下常用NSSM来注册应用为服务,但我尝试了很多次,使用System账户登陆时无法使用,指定以普通用户登陆又一直提醒“账户密码错误”,索性改用Windows的任务计划程序。
Windows下服务由服务控制管理器管理,通过服务控制接口与Windows服务程序交互,对于没有实现这类接口的普通应用程序,NSSM可作为中介来响应并控制程序行为
先将启动参数保存到一个sh脚本里,随后可以通过wsl.exe -d Debian -u root -- /mnt/c/garage/start-garage.sh来以root身份执行。在任务计划程序里添加一个启动时执行的任务
然后不出意外就又出意外了,保存时也是一直提示密码错误,不得不感叹Windows的账户机制之精妙![]()
我这里使用管理员权限用命令创建任务
1 | # 操作:执行 WSL 脚本 |
1 |
|
配置反向代理
参考:快速搭建&迁移S3 —— 手把手教你从MinIO迁移到Garage
可能用的上的:
1 | add_header Access-Control-Allow-Origin *; |
管理
添加新节点
如果部署了webui,可以使用你设定的面板账密登录http://{服务器ip}:3909,切换到Cluster页面,并点击Connect连接其他节点
面板提供了获取其他节点ID的命令,如果是Docker部署的可以直接运行,二进制方式部署的可以运行garage node id
Tip
webui中的node id不严谨。garage node id输出为node_id_hex@ip:port,整体为该节点的连接参数,node_id是节点初始化时生成的Ed25519公钥(meta/node_key.pub),编码为十六进制后输出,可以通过cat meta/node_key.pub | od -A n -t x1 | tr -d ' \n'查看
这里,应填写这个整体,或者直接使用命令行garage [-c <config file path>] node connect <node_id_hex@ip:port>
在任意节点添加新节点后,新节点信息会被广播到其他节点,不需要在其他节点重复操作
配置储存池
选中某节点,为其分配角色
角色包括节点的Zone(区域)、Capacity(容量)和 Tags(标签)
标签仅用于标识,主要是区域,相同区域内的节点会视为同一个储存池,数据不会在这里面的节点上复制多份。我这里5个节点都在不同区域。
在完成配置后应用
配置完成后会输出集群状态,可见该集群有400GiB+的可用空间
1 | aaa@aaa:/# garage stats |
创建存储桶和Key
名字随意,后续更名可以添加一个Alias(别称)再删掉原来的
等价于garage key create <name>,
默认不含创建存储桶权限,需使用
garage key allow <name> --create-bucket赋予权限(使用garage key deny <name> --create-bucket撤销权限)
在创建的存储桶里为刚才创建的key赋予权限
owner权限可以管理桶,比如删除桶操作,小心使用
等价于garage bucket allow <bucket-name> --key <key-name> --read --write --owner
外围应用
作为一个 S3 储存服务,它可以接入到大多数软件中。可以分为两部分:
- 原生支持 S3 标准的软件
- 不支持 S3 标准的软件,其需要使用别的服务进行中转,转成
WebDAV或别的协议
图片储存及管理
图床
过去我一直使用兰空图床(Lsky Pro)上传图片和管理图片,但它的开源版本已经好久没有更新了。安装起来也麻烦,挑PHP版本,又要挑数据库版本。放到今天性能也算不上多优秀。
本来我是想着直接改一下GoodBoyboy666/perfect-pic用的,实现一些更契合我文件格式转换的链接格式,可仔细一想,我需要的只是一个上传工具,没必要非得用在线形式,跨平台兼容性,干脆就用PicGo了
太久没用了,装下来发现样式变化好大
直接在插件商店里安装 S3,或者在插件目录手动执行 npm install picgo-plugin-s3
随后按照图中所示配置
Chronoframe
Artalk 评论
Artalk使用upgit作为外置的上传器
安装upgit
1 | # 下载本体 |
配置:
1 | default_uploader = "s3" |
url_format指的是输出链接格式,我这里是接入了其他服务。按照刚刚的配置,应填写为https://artalk.oss.hzchu.top/{path}
测试上传
1 | upgit --application-path /upgit_data --config-file /usr/bin/config.toml 1.png |
application-path是相对于当前运行目录的,用于存放历史和日志文件
Artalk里:upgit -c /usr/bin/config.toml --application-path /data
文件储存
文件管理
可以使用S3 browser
中转成WebDav协议
WebDav主要用来备份,以往使用OpenList的时候是直接在根目录新建一个文件夹,以待备份的程序命名,再在程序里填写用户信息和备份路径
但是很麻烦的一点是,我要为每个程序都去新建文件夹,有的时候程序还不支持配置备份路径。再者说,这么多程序共用一个用户,其实还是不太安全的。
所以我希望的一点是能够创建多个用户,用户的用户名就以这个程序来命名,每个用户有自己的储存目录,且彼此不可见。这样子即便不支持调整路径的应用,也可以被正确储存到对应的分类中
官方用的是Bagage,但好久没更新了,OpenList又太重了,干脆替换成drakkan/sftpgo
使用Docker部署
1 | services: |
创建虚拟文件夹
最主要是Key Prefix,这里配置为%username%/,为每个用户储存的文件都添上它所属用户名的前缀,也就是实现了上文提到的分类和隔离
接着是创建用户组,并配置虚拟文件夹
前者为挂载路径,后者为对应的文件夹,这里直接挂载到根目录
随后再创建用户,添加到刚才新建的组里
创建用户时也可以配置用户级的虚拟文件夹,但不支持
%username%占位符
至于File system,在挂载根目录以后,就不会再往默认的本地磁盘储存写入文件了,不用管
维护
更多参考官方文档
元数据存储空间
在线页面看到的只有data_dir的可用空间,但是在实际部署时元数据储存位置通常放在速度更快的SSD上,如果容量较小的话还是要注意下的。以下输出表明 47.5GiB 空间里还有 13.2 GiB 空间可用,剩余 27.8% 的空间
1 | aaa@aaa:~# garage stats |
修改集群冗余度
如果在集群初始化后想修改集群冗余度,需要清除所有节点的拓扑结构
1 | # 关闭garage,其他方式部署的自行关闭 |
清除拓扑结构不会丢失文件,但最好做好备份
随后修改config.toml里的replication_factor为想要的数值
启动各个节点,重新为节点分配角色、容量和区域






















