惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
B
Blog RSS Feed
量子位
aimingoo的专栏
aimingoo的专栏
V
Visual Studio Blog
Y
Y Combinator Blog
Vercel News
Vercel News
云风的 BLOG
云风的 BLOG
宝玉的分享
宝玉的分享
Engineering at Meta
Engineering at Meta
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
GbyAI
GbyAI
人人都是产品经理
人人都是产品经理
博客园 - 叶小钗
Stack Overflow Blog
Stack Overflow Blog
大猫的无限游戏
大猫的无限游戏
Microsoft Security Blog
Microsoft Security Blog
B
Blog
Last Week in AI
Last Week in AI
有赞技术团队
有赞技术团队
博客园 - 聂微东
腾讯CDC
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
J
Java Code Geeks

atpX

听风的歌 偶尔是深夜就好了 新玩具 AirPods Pro 3 公交车上的时间 从 WHOIS 到 RDAP 通过 WireGuard 访问 NFS 共享文件 自建音乐串流服务探索 我停止了探索 Fediverse 互联网背景噪音 我的数字生活降级 小城与确定性的墙 一只特立独行的猪 Fediverse 与社交 承认的勇气 mediaX - 轻量书影音记录管理工具 我的博客写作流程 网站新增 Misc 页面 擅长对线的鲍勃 在 Chroot 环境下使用 Rsync 同步 再见 JavaScript 当我玩博客时我在玩什么 为什么我的博客没有友链页面 小熊猫与大熊猫 (HDR 照片测试) 是时候为网站开启 HTTP/3 支持了吗 使用 AdGuard Home 搭建自用 DoH 服务 谈谈读书与消遣 2023 年终总结 西安两日游 从 AirPods「升级」到 EarPods Docker 搭建去中心化的微博客平台 Mastodon
自建 S3 兼容对象存储服务 Garage
ATP · 2026-03-01 · via atpX
Garage

以前使用 AWS S3 / Cloudflare R2 这些公有云产品时总放不开手脚,公开资源得套一层缓存来降低请求量,生怕账单被刷爆。对比几个开源 S3 兼容项目后,选择 Garage 的原因很简单,轻量够用,分布式设计以及针对高延迟场景优化,让节点走出大厂数据中心,可以到处「捡破烂」。

虽然也不是非得用 S3,重要数据每天都有自动任务备份,也没重要到没了就天塌了,主要图个紧跟时代浪潮,搭建自己的数据基石。

搭建 Garage

这里简单的记录下 Garage 安装及配置过程。

编辑配置

创建 garage.toml 配置文件,具体完整配置参考官方文档

metadata_dir = "/var/lib/garage/meta" # 元数据存放目录
data_dir = "/var/lib/garage/data" # 对象数据存放目录
db_engine = "sqlite" # 可选 lmdb / sqlite / fjall
# metadata_auto_snapshot_interval = "6h" # 自动对元数据进行快照

replication_factor = 3 # 副本数量,单节点设置为 1
compression_level = 1 # zstd 压缩级别,默认 1,越低 CPU 开销越低
block_size = "1M" # 默认 1MB,如果存储大文件且网络较好可适当增加

rpc_bind_addr = "[::]:3901" # 集群 RPC 通信端口
rpc_public_addr = "127.0.0.1:3901" # 集群填公网 IP:3901
rpc_secret = "<RPC secret>" # RPC 通信密钥,`openssl rand -hex 32` 生成

[s3_api]
s3_region = "garage" # 自定义区域名
api_bind_addr = "[::]:3900"
root_domain = ".s3.example.com" # S3 API 访问根域名,<bucket>.s3.example.com

[s3_web]
bind_addr = "[::]:3902"
root_domain = ".web.example.com" # web 访问根域名,<bucket>.web.example.com
index = "index.html"

[admin]
api_bind_addr = "[::]:3903"
admin_token = "<admin token>" # 管理接口 Token,`openssl rand -base64 32` 生成
metrics_token = "<metrics token>" # 指标接口 Token,`openssl rand -base64 32` 生成

如果有前置 web server,可以把除 RPC 外的端口绑定在本地 localhost,通过反代访问提高安全性。两处 root_domain 的设置对应 bucket 的两种访问风格:

  • 路径 (Path):root_domain 设置为 s3.example.com,访问地址:https://s3.amazonaws.com/<bucket>/<key>

  • 虚拟主机 (Virtual Hosted) :root_domain 设置为 .s3.example.com,访问地址:https://<bucket>.s3.amazonaws.com/<key>

虽然公有云都在力推虚拟主机风格,例如 AWS 计划停止支持路径风格,但都是基于海量用户下的更高性能和安全需求。两种风格我都进行了测试,最后个人项目还是使用路径风格更方便,不需要额外申请一张 *.s3.example.com 证书,而且能避免一些潜在的问题,比如 bucket 名称中带 . 时会导致证书不匹配。

启动服务

Garage 使用 Rust 开发,可以选择直接下载运行编译好的二进制文件,并通过 systemd 管理:

vim /etc/systemd/system/garage.service
[Unit]
Description=Garage Data Store
Documentation=https://garagehq.deuxfleurs.fr
After=network-online.target
Wants=network-online.target
StartLimitBurst=5
StartLimitIntervalSec=60

[Service]
User=garage
Group=garage
Environment='RUST_LOG=garage=info'
ExecStart=/path/to/garage -c /path/to/garage.toml server
Restart=on-failure
RestartSec=10
NoNewPrivileges=true
LimitNOFILE=42000

[Install]
WantedBy=multi-user.target

也可以简单的通过 Docker Compose 启动服务:

services:
  garage:
    image: dxflrs/garage:v2.2.0
    container_name: garage
    network_mode: host
    environment:
      - TZ=Asia/Shanghai
      - RUST_LOG=garage=info
    restart: unless-stopped
    volumes:
      - ./garage.toml:/etc/garage.toml
      - ./meta:/var/lib/garage/meta
      - ./data:/var/lib/garage/data
    # ports:
    #   - 3900:3900
    #   - 3901:3901
    #   - 3902:3902
    #   - 3903:3903

设置一个别名来更方便的执行 Garage 命令,在 ~/.bashrc 中添加下面内容后 source ~/.bashrc

alias garage="docker exec -it <container name> /garage"
# e.g. alias garage="docker exec -it garage /garage"

查看 Garage 当前状态:

如果使用了 UFW 防火墙和 Docker 容器端口映射,可能由于规则冲突导致无响应,可以简单的通过 ufw disable 关闭防火墙(不推荐)或者容器配置文件中 network_mode 使用 host 模式解决。

如果有多个节点,在每个节点上用相同的配置文件启动 Garage 后(注意 RPC Public IP 不同),依次添加其他节点,任意节点添加一次即可,会自动发现并同步到整个集群:

garage node connect <garage node id>
# 如果配置了 ufw 防火墙需要互相放行端口
# ufw allow from <node ip> to any port 3901

接下来为所有节点配置 layout:

garage status
garage layout assign <node id> -z <zone name> -c <storage capacity> -t <tag name>
# e.g. garage layout assign c66tu81zk2k2w1z8 -z dc1 -c 10g -t raspi

应用配置:

garage layout show
garage layout apply --version 1

如果后期修改了节点配置需要按提示使用更高的版本号。

添加存储桶

首先创建一个名为 test-bucket 的存储桶:

garage bucket create test-bucket
# 查看 bucket 信息
garage bucket list
garage bucket info test-bucket

一个存储桶通过单个或多个 API key 管理,创建 API key 用来管理 test-bucket 桶:

garage key create test-key
# 查看 API key 信息:
garage key list
garage key info test-key

记得将 Key ID 和 Secret key 信息保存到安全的地方。赋予该 API key 对 test 桶的访问或管理权限:

garage bucket allow \
  --read \
  --write \
  --owner \
  test-bucket \
  --key test-key

之后就可以使用 S3 API 兼容的工具 Rclone / CyberDuck 等连接到 Garage 进行管理了。比如使用 Rclone 连接,在配置文件 ~/.config/rclone/rclone.conf 中添加:

[garage]
type = s3
provider = Other
access_key_id = GKXXX
secret_access_key = xxxxxx
region = garage
endpoint = https://s3.example.com
# 如果是虚拟主机风格设置为 false
path_style_access = true 

Garage 相对于 AWS S3 这些来说没有存储桶策略(Bucket Policy),如果要公开访问某一个桶,可以将其设置为网站模式

garage bucket website --allow <bucket>

配置 Nginx

可以通过 Nginx 将 S3 API 更安全的反代出去:

server {
    listen 443 ssl;
    server_name s3.example.com;
    ...
    location / {
        client_max_body_size 0;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_connect_timeout 300;
        proxy_max_temp_file_size 0;
        proxy_pass http://127.0.0.1:3900;
    }
}

如果使用虚拟主机风格,还需要解析 *.s3.example.com,存储桶网站模式的配置同理。推荐配置一些策略防止扫描或添加缓存,保证性能和后端安全性。

升级程序

升级 Garage 前建议阅读更新文档说明,保证安全的升级。其中:

  • 小版本更新(如从 v0.8.0 到 v0.8.1):推荐运行 garage repair --all-nodes --yes tables 检查健康度,没问题的话逐个节点升级即可。
  • 大版本更新(如从 v0.7.1 到 v0.8.0,不支持跨版本):检查健康状态后对所有节点的 metadata 进行快照 garage meta snapshot --all ,复制备份 cluster_layout 文件后逐个升级。

修复/替换节点

Garage 推荐 replication_factor 至少使用 3 副本策略来保障数据安全,此时如果 1 个可用区故障,集群依然可以正常读写;如果 2 个可用区故障,进入只读模式。因此当可用区发生故障时,建议尽快修复或者替换其他节点。

a. 如果某个节点数据盘损坏,但元数据 metadata 还在,只需要更换数据盘后挂载到原来的 data 目录,使用下面命令进行修复即可:

garage repair -a --yes blocks

b. 如果某个节点数据全丢无法恢复,或者单纯想换一个节点:

# 将新节点加入集群
# garage node connect <garage node id>
# garage status
garage layout assign <new node id> --replace <old node id> \
    -z <zone name> -c <storage capacity> -t <tag name>
# -z <zone name> 和 -c <storage capacity> 需与旧节点保持一致
garage layout show
garage layout apply --version <new version>

两种情况都可以使用 garage stats -a 监控,等待 resync queue length 归零则表示迁移同步完毕。

Web UI 管理

如果你偏向于图形界面操作,可以使用开源的第三方工具 garage-webui 在网页上方便的查看服务状态,管理节点、存储桶、API key 和对象文件。官方也正在开发 garage-webadmin 项目,目前需要自行编译。

services:
  garage:
  ...
  webui:
    image: khairul169/garage-webui:latest
    container_name: garage-webui
    restart: unless-stopped
    volumes:
      - ./garage.toml:/etc/garage.toml:ro
    network_mode: host
    environment:
      # BASE_PATH: "/garage"
      API_BASE_URL: "http://127.0.0.1:3903"
      S3_ENDPOINT_URL: "http://127.0.0.1:3900"
      # htpasswd -nbBC 10 "USERNAME" "PASSWORD"
      # 生成的字符串中带 $ 特殊符号,可以用 $$ 转义或通过 .env 文件设置变量
      # .env > GARAGE_UI_USER_PASS='test:$2y$10$2A4...'
      AUTH_USER_PASS: ${GARAGE_UI_USER_PASS}

使用感受

目前自建 Garage 使用了一周多,初步备份了一些文件,本站图片也迁移了上去,最大的感受是足够轻量,日常使用资源占用较低,在 1C1G 的 VPS 上也能良好运行。节点分布在海外三个不同的城市,同时 BOOM 的几率应该是 World War III 或者三体人入侵级别,作为一个从 2020 年开始维护至今的项目,有不少的用户,可靠性上应该没问题。

计划作为个人长期服务运行下去,只是如果后期数据量越来越多,需要同时扩容三个节点,成本也不算低。现在存储原厂们只做 AI 生意产能不足导致内存和硬盘价格飞涨,显得公有云对象存储服务都更有性价比了,不过 S3 兼容的好处是可进可退,只要价格到位,基石也是可以搬得动的。