Garage v2.4.1 进阶实战与运维

Garage v2.4.1 进阶实战,以三节点三副本集群实测为主线,覆盖多节点组网、layout 容量规划、复制模式对比、单节点故障演练、滚动升级混合版本验证、元数据快照与 S3 数据备份、监控与日志运维

GarageGarage对象存储S3 兼容Linux2026/10/17

Garage v2.4.1 进阶实战与运维

目标:在 3 台 Ubuntu 22.04 服务器上完成 Garage v2.4.1 三节点三副本(RF=3)生产级集群的搭建与运维实战——多节点组网、layout 容量规划、复制模式对比、单节点故障演练(降级读写与自动恢复)、滚动升级与混合版本验证、元数据快照与 S3 数据备份、监控与日志。全部操作均实测通过。

一、环境信息

节点主机名IP(实测环境)Zone分配容量
node1node1172.29.0.2dc15G
node2node2172.29.0.3dc25G
node3node3172.29.0.4dc35G
  • 端口:3900(S3 API)、3901(RPC,节点间互通必须可达)、3903(Admin API)
  • AWS CLI:aws-cli/2.38.1

💡 技术拓展:三副本(RF=3)是 Garage 生产推荐配置,容灾与容量代价——

维度replication_factor = 2replication_factor = 3
容忍节点故障1 个2 个
读写 quorum写 2 读 1写 2 读 1(consistent 模式)
磁盘有效容量总容量 50%总容量 33%
推荐集群规模≥2 节点≥3 节点且 ≥3 zone
适用个人/边缘,数据低敏生产,跨机房容灾

RF=3 时 256 个分区的每份副本尽量落到不同 zone;三个 zone 各占 1/3 分区时达到 Zone redundancy: maximum(下文实测)。有效容量 = 分配总容量 ÷ 3,规划磁盘时按此估算。

二、前置条件

  1. 3 台服务器均已按安装指南完成 Garage v2.4.1 安装、garage 系统用户与 systemd unit 部署(服务尚未启动或已启动均可);
  2. 三台之间 3901 端口互相可达(nc -zv 172.29.0.x 3901 验证),这是集群组网的生命线;
  3. 已完成三份关键信息的采集:各节点 garage node id 输出、集群共享 rpc_secret(openssl rand -hex 32,三台一致)、各节点 IP。

三、三节点集群组网实测

3.1 统一配置文件

三台服务器的 /etc/garage.toml 除 rpc_public_addr 与 bootstrap_peers 外完全一致(rpc_secret 三台必须相同)。逐台列出示例值,实际部署时替换为你采集的完整节点 ID。

node1 的 /etc/garage.toml:

metadata_dir = "/var/lib/garage/meta"
data_dir = "/var/lib/garage/data"
db_engine = "fjall"

replication_factor = 3
consistency_mode = "consistent"

rpc_bind_addr = "[::]:3901"
rpc_public_addr = "172.29.0.2:3901"
rpc_secret = "69c6388633a888a4fc3ed279540352718e43c7d360b6020ad5323f9ffb36a039"
bootstrap_peers = [
  "146172322f18f7bab5ab8d7ca0f87c290a1d7a351ddb8f2668f8f2dd3c19708b@172.29.0.3:3901",
  "37905798112536c4d676dec47938928afa54d161735f913b81d7cdc97dce9647@172.29.0.4:3901",
]

[s3_api]
s3_region = "garage"
api_bind_addr = "[::]:3900"

[admin]
api_bind_addr = "[::]:3903"
admin_token = "q1w2e3r4-admin"

node2 的 /etc/garage.toml:

metadata_dir = "/var/lib/garage/meta"
data_dir = "/var/lib/garage/data"
db_engine = "fjall"

replication_factor = 3
consistency_mode = "consistent"

rpc_bind_addr = "[::]:3901"
rpc_public_addr = "172.29.0.3:3901"
rpc_secret = "69c6388633a888a4fc3ed279540352718e43c7d360b6020ad5323f9ffb36a039"
bootstrap_peers = [
  "d0bb67c993d97376770f9583a11c52512c1b81b5d31e82f8a234ee937c522a32@172.29.0.2:3901",
  "37905798112536c4d676dec47938928afa54d161735f913b81d7cdc97dce9647@172.29.0.4:3901",
]

[s3_api]
s3_region = "garage"
api_bind_addr = "[::]:3900"

[admin]
api_bind_addr = "[::]:3903"
admin_token = "q1w2e3r4-admin"

node3 的 /etc/garage.toml:

metadata_dir = "/var/lib/garage/meta"
data_dir = "/var/lib/garage/data"
db_engine = "fjall"

replication_factor = 3
consistency_mode = "consistent"

rpc_bind_addr = "[::]:3901"
rpc_public_addr = "172.29.0.4:3901"
rpc_secret = "69c6388633a888a4fc3ed279540352718e43c7d360b6020ad5323f9ffb36a039"
bootstrap_peers = [
  "d0bb67c993d97376770f9583a11c52512c1b81b5d31e82f8a234ee937c522a32@172.29.0.2:3901",
  "146172322f18f7bab5ab8d7ca0f87c290a1d7a351ddb8f2668f8f2dd3c19708b@172.29.0.3:3901",
]

[s3_api]
s3_region = "garage"
api_bind_addr = "[::]:3900"

[admin]
api_bind_addr = "[::]:3903"
admin_token = "q1w2e3r4-admin"

要点:rpc_public_addr 写本机在集群内可达的 IP;bootstrap_peers 写另外两台的 <完整节点ID>@<IP>:3901(node2 之类的主机名实测无法解析,日志报 WARN garage_rpc::system: Unable to parse and/or resolve peer hostname node1:3901,必须用 IP);每台 bootstrap_peers 都要指向能连通的对端。

三台各自启动:

systemctl start garage

3.2 验证三节点互联

在 node1 上查看:

garage status

实测回显:

==== HEALTHY NODES ====
ID                Hostname  Address         Tags  Zone  Capacity          DataAvail  Version
d0bb67c993d97376  node1     172.29.0.2:3901             NO ROLE ASSIGNED             v2.4.1
146172322f18f7ba  node2     172.29.0.3:3901             NO ROLE ASSIGNED             v2.4.1
37905798112536c4  node3     172.29.0.4:3901             NO ROLE ASSIGNED             v2.4.1

三节点全部出现在 HEALTHY NODES 且 Version 一致,说明 RPC 互联成功。此时三台都 NO ROLE ASSIGNED,下一步分配容量。

3.3 layout 分配(三节点逐台执行)

⚠️ 注意:layout assign 只需在集群内任一节点执行(本文统一在 node1 操作),变更会通过 RPC 同步;不要在多台并发 assign。

garage layout assign -z dc1 -c 5G d0bb67c993d97376

实测回显:

Role changes are staged but not yet committed.
Use `garage layout show` to view staged role changes,
and `garage layout apply` to enact staged changes.
garage layout assign -z dc2 -c 5G 146172322f18f7ba

实测回显:

Role changes are staged but not yet committed.
Use `garage layout show` to view staged role changes,
and `garage layout apply` to enact staged changes.
garage layout assign -z dc3 -c 5G 37905798112536c4

实测回显:

Role changes are staged but not yet committed.
Use `garage layout show` to view staged role changes,
and `garage layout apply` to enact staged changes.

查看暂存变更与容量计算:

garage layout show

实测回显(节选):

==== STAGED ROLE CHANGES ====
ID                Tags  Zone  Capacity
d0bb67c993d97376  []    dc1   4.7 GiB
146172322f18f7ba  []    dc2   4.7 GiB
37905798112536c4  []    dc3   4.7 GiB

==== NEW CLUSTER LAYOUT AFTER APPLYING CHANGES ====
ID                Tags  Zone  Capacity   Usable capacity
d0bb67c993d97376  []    dc1   4.7 GiB    4.7 GiB (33.3%)
146172322f18f7ba  []    dc2   4.7 GiB    4.7 GiB (33.3%)
37905798112536c4  []    dc3   4.7 GiB    4.7 GiB (33.3%)

Zone redundancy: maximum

==== COMPUTATION OF A NEW PARTITION ASSIGNATION ====

Partitions are replicated 3 times on at least 3 distinct zones.

Optimal partition size:                     55.9 MiB
Usable capacity / total cluster capacity:   4.7 GiB / 14.1 GiB (33.33 %)
Effective capacity (replication factor 3):  4.7 GiB

关键数字解读:三节点各 5G(合计 14.1 GiB 十进制显示),RF=3 时 Effective capacity 仍为 4.7 GiB——三副本下集群有效容量等于单节点容量;Partitions are replicated 3 times on at least 3 distinct zones 确认 256 个分区每份副本分布在 3 个不同 zone。

提交布局:

garage layout apply --version 1

实测回显:

New cluster layout with updated role assignment has been applied in cluster.
Data will now be moved around between nodes accordingly.

确认集群状态:

garage status

实测回显:

==== HEALTHY NODES ====
ID                Hostname  Address         Tags  Zone  Capacity   DataAvail         Version
d0bb67c993d97376  node1     172.29.0.2:3901  []    dc1   4.7 GiB    8.7 GiB (3.8%)    v2.4.1
146172322f18f7ba  node2     172.29.0.3:3901  []    dc2   4.7 GiB    8.7 GiB (3.8%)    v2.4.1
37905798112536c4  node3     172.29.0.4:3901  []    dc3   4.7 GiB    8.7 GiB (3.8%)    v2.4.1
garage health

实测回显:

Cluster health:            HEALTHY
Known nodes:               3
Connected nodes:           3
Storage nodes:             3
Storage nodes up:          3
Partitions:                256
Partitions with quorum:    256
Fully healthy partitions:  256

3.4 创建 bucket/key 并验证三副本

garage bucket create cluster-demo

实测回显:

==== BUCKET INFORMATION ====
Bucket:          b64d69dd12c899db6045548585a2b59a48c76b530452377f31f9d09a703b6f2b
Created:         2026-10-17 04:47:27.929 +00:00

Size:            0 B (0 B)
Objects:         0

Website access:  false

Global alias:    cluster-demo
garage key create cluster-key

实测回显:

==== ACCESS KEY INFORMATION ====
Key ID:              GKa9b6f4459d1a3706b9f27ddd
Key name:            cluster-key
Secret key:          fe86ddcf504751a3f6a601be67613c597a4fade94c1a32c7a73d345f95c28b82
Created:             2026-10-17 04:47:28.211 +00:00
Validity:            valid
Expiration:          never

Can create buckets:  false
garage bucket allow --read --write --owner cluster-demo --key cluster-key

实测回显(节选):

==== KEYS FOR THIS BUCKET ====
Permissions  Access key                                  Local aliases
RWO          GKa9b6f4459d1a3706b9f27ddd  cluster-key

在 node1 配置 AWS CLI 并上传 1MB 对象:

aws configure set aws_access_key_id GKa9b6f4459d1a3706b9f27ddd
aws configure set aws_secret_access_key fe86ddcf504751a3f6a601be67613c597a4fade94c1a32c7a73d345f95c28b82
aws configure set default.region garage
head -c 1048576 /dev/urandom > /root/1mb-test.bin
aws --endpoint-url http://127.0.0.1:3900 s3 cp /root/1mb-test.bin s3://cluster-demo/1mb-test.bin

实测回显:

upload: root/1mb-test.bin to s3://cluster-demo/1mb-test.bin

从 node3 的 S3 端点读取同一对象验证集群透明访问:

aws --endpoint-url http://172.29.0.4:3900 s3 cp s3://cluster-demo/1mb-test.bin /root/1mb-back.bin
md5sum /root/1mb-test.bin /root/1mb-back.bin

实测回显:

download: s3://cluster-demo/1mb-test.bin to root/1mb-back.bin
e07a2f2c4a42c08d21a1f3580e29b6b1  /root/1mb-test.bin
e07a2f2c4a42c08d21a1f3580e29b6b1  /root/1mb-back.bin

任意节点端点皆可读写全部数据——这正是 Garage 集群的意义。

3.5 验证三副本落盘

在三个节点分别统计数据块目录:

find /var/lib/garage/data -type f | wc -l
du -sh /var/lib/garage/data | cut -f1

实测回显(node1 / node2 / node3):

node1: 12 个文件,400K
node2: 10 个文件,392K
node3: 12 个文件,400K

三个节点都持有该对象的数据块文件,RF=3 落地生效。

四、故障演练实测

4.1 停掉 node2:集群 DEGRADED 但可读写

systemctl stop garage

(在 node2 上执行)约 30 秒后查看 node1 上的集群健康:

garage health

实测回显:

Cluster health:            DEGRADED
Known nodes:               3
Connected nodes:           2
Storage nodes:             3
Storage nodes up:          2
Partitions:                256
Partitions with quorum:    256
Fully healthy partitions:  0
garage status

实测回显(节选):

==== FAILED NODES ====
ID                Hostname  Address         Tags  Zone  Capacity   DataAvail  Version
146172322f18f7ba  node2     172.29.0.3:3901  []    dc2   4.7 GiB    -          v2.4.1

解读:DEGRADED 但 Partitions with quorum: 256——256 个分区均满足写 quorum(RF=3 下 2/3 即可),集群降级可用;Fully healthy partitions: 0 表示没有分区集齐 3 副本。

验证降级状态读写:

echo degraded-write-test > /root/dg.txt
aws --endpoint-url http://127.0.0.1:3900 s3 cp /root/dg.txt s3://cluster-demo/degraded.txt
aws --endpoint-url http://127.0.0.1:3900 s3 cp s3://cluster-demo/degraded.txt /root/dg-back.txt
cat /root/dg-back.txt
aws --endpoint-url http://127.0.0.1:3900 s3 cp s3://cluster-demo/1mb-test.bin /root/1mb-degraded.bin
md5sum /root/1mb-degraded.bin

实测回显:

upload: root/dg.txt to s3://cluster-demo/degraded.txt
download: s3://cluster-demo/degraded.txt to root/dg-back.txt
degraded-write-test
download: s3://cluster-demo/1mb-test.bin to root/1mb-degraded.bin
e07a2f2c4a42c08d21a1f3580e29b6b1  /root/1mb-degraded.bin

降级期间新对象上传、已有对象下载全部正常(写入的 degraded.txt 会暂存 2 副本,node2 恢复后自动补第三份)。

💡 技术拓展:故障演练会话对照——

场景Cluster health可读可写说明
3/3 节点在线HEALTHY✔✔全分区 3 副本
停 1 个节点(RF=3)DEGRADED✔✔quorum 满足,恢复后自动补副本
连停 2 个节点(RF=3)不可用✘✘quorum 丢失,分区不可访问
停 1 个节点(RF=1)数据丢失仅存副本可读✔(新写无冗余)RF=1 无故障容忍

结论:RF=3 容忍 1 节点故障仍保证完整读写;生产集群至少 3 节点。

4.2 恢复 node2:自动回同步

systemctl start garage

(在 node2 上执行)数秒后 node1 查看:

garage health

实测回显:

Cluster health:            HEALTHY
Known nodes:               3
Connected nodes:           3
Storage nodes:             3
Storage nodes up:          3
Partitions:                256
Partitions with quorum:    256
Fully healthy partitions:  256

集群自动回到 HEALTHY。查看数据回同步 worker 状态(缺块会进入 resync 队列补齐):

garage worker list

实测回显(节选):

TID  State  Name                          Tranq  Done  Queue  Errors  Consec  Last
1    Idle   Block resync worker #1        2      -     2      -       -
2    Idle   Block resync worker #2        -      -     -      -       -
...
9    Idle   Block scrub worker            4      -     -      -       -
10   Idle   admin_token Merkle            -      -     0      -       -

Block resync worker 队列归零即补副本完成。

五、滚动升级与版本兼容实测

5.1 实测目标

验证 Garage 集群允许混合版本共存:把 node3 降到 v2.4.0,与 v2.4.1 的 node1/node2 组网运行,再滚动升回 v2.4.1。

5.2 下载旧版并替换(node3)

wget https://garagehq.deuxfleurs.fr/_releases/v2.4.0/x86_64-unknown-linux-musl/garage -O /root/garage-v2.4.0
chmod +x /root/garage-v2.4.0
sha256sum /root/garage-v2.4.0

实测回显:

edddc3d1a4bc37976541242c3646f13e4323b696d7efc8eb70c9ad2a0eae0b18  /root/garage-v2.4.0

⚠️ 注意:直接 cp /root/garage-v2.4.0 /usr/local/bin/garage 在服务运行中会实测报错 cp: cannot create regular file '/usr/local/bin/garage': Text file busy——必须先 systemctl stop garage 再覆盖。

systemctl stop garage
cp /root/garage-v2.4.0 /usr/local/bin/garage
systemctl start garage

5.3 混合版本集群验证

garage status

实测回显(node1 上查看):

==== HEALTHY NODES ====
ID                Hostname  Address         Tags  Zone  Capacity   DataAvail         Version
d0bb67c993d97376  node1     172.29.0.2:3901  []    dc1   4.7 GiB    8.7 GiB (3.8%)    v2.4.1
146172322f18f7ba  node2     172.29.0.3:3901  []    dc2   4.7 GiB    8.7 GiB (3.8%)    v2.4.1
37905798112536c4  node3     172.29.0.4:3901  []    dc3   4.7 GiB    8.7 GiB (3.8%)    v2.4.0

node3 显示 v2.4.0 且仍在 HEALTHY NODES,混合版本集群健康运行。升级期间继续上传对象验证可用性:

aws --endpoint-url http://127.0.0.1:3900 s3 cp /root/dg.txt s3://cluster-demo/during-upgrade.txt

实测回显:

upload: root/dg.txt to s3://cluster-demo/during-upgrade.txt

5.4 node3 升回 v2.4.1 并终验

systemctl stop garage
cp /root/garage-v2.4.1 /usr/local/bin/garage
systemctl start garage
garage --version | head -1

实测回显:

garage v2.4.1 [features: bundled-libs, consul-discovery, fjall, journald, k2v, kubernetes-discovery, lmdb, metrics, sqlite, syslog, telemetry-otlp]
garage status | grep node3

实测回显:

37905798112536c4  node3     172.29.0.4:3901  []    dc3   4.7 GiB    8.7 GiB (3.8%)    v2.4.1

滚动升级流程定型:逐节点(一次一个)stop → 换二进制 → start → 确认 status 版本与健康 → 下一节点;升级全程集群读写不中断(during-upgrade.txt 上传成功即证)。生产升级前阅读官方 release notes 确认跨版本兼容性。

六、备份与恢复实测

6.1 元数据快照

元数据是集群的"身份证 + 索引",丢失即数据不可寻址。运行中直接创建快照:

garage meta snapshot

实测回显:

Node              Result
d0bb67c993d97376  Snapshot created

实测快照目录(Fjall 引擎快照为 db.fjall 的只读副本):

/var/lib/garage/meta/snapshots/2026-10-17T04:40:05Z/db.fjall/

打 tar 包异地保存:

tar -C /var/lib/garage/meta -czf /root/garage-meta-snap.tar.gz .
ls -lh /root/garage-meta-snap.tar.gz

实测回显:

-rw-r--r-- 1 root root 41K Oct 17 04:40 /root/garage-meta-snap.tar.gz

自动快照由 garage.toml 的 metadata_auto_snapshot_interval = "6h" 控制(默认开启)。

6.2 S3 数据备份

对象数据按内容寻址散列在 data_dir,不建议直接拷文件;标准做法是用 S3 协议把 bucket 同步到本地或另一个存储:

mkdir -p /root/backup
aws --endpoint-url http://127.0.0.1:3900 s3 sync s3://cluster-demo /root/backup/
find /root/backup -type f

实测回显:

download: s3://cluster-demo/1mb-test.bin to backup/1mb-test.bin
download: s3://cluster-demo/during-upgrade.txt to backup/during-upgrade.txt
/root/backup/1mb-test.bin
/root/backup/during-upgrade.txt

再次执行 sync 时只传增量,适合作为定时任务(cron 每日一次)。三副本集群本身已含冗余,但备份仍是防误删、防灾难(三台同时损毁)的最终手段;"元数据 + 数据"两份备份都要异地保存。

6.3 恢复演练要点

元数据恢复:停服务 → 清空 metadata_dir → 解包快照 tar 到位 → 确认属主 garage → 启动(快照是时间点状态,恢复到快照时刻)。数据恢复:重建空集群或恢复元数据后,把 S3 备份用 aws s3 sync /root/backup s3://cluster-demo 回灌。恢复演练务必在隔离环境先行验证,本文不展开完整流程。

七、监控与日志

7.1 三层健康检查

garage health
curl -s http://127.0.0.1:3903/health
curl -s -H 'Authorization: Bearer q1w2e3r4-admin' http://127.0.0.1:3903/v2/GetClusterHealth

实测回显(node1,健康态):

Cluster health:            HEALTHY
Known nodes:               3
Connected nodes:           3
Storage nodes:             3
Storage nodes up:          3
Partitions:                256
Partitions with quorum:    256
Fully healthy partitions:  256
Garage is fully operational
Consult the full health check API endpoint at /v2/GetClusterHealth for more details
{"healthy":true,"knownNodes":3,"connectedNodes":3,"storageNodes":3,"storageNodesUp":3,"partitions":256,"partitionsQuorum":256,"partitionsAllHealthy":256,"partitionsAvailable":256,"quorumCount":1,"storageNodesDown":0,"resyncQueueLen":0,"uploadsPending":0}

监控建议:/health 无需令牌适合存活探针;GetClusterHealth 返回 JSON,healthy:false 或 partitionsQuorum < partitions 告警;resyncQueueLen 持续偏高说明回同步积压。

7.2 Prometheus 指标

curl -s -H 'Authorization: Bearer q1w2e3r4-admin' http://127.0.0.1:3903/metrics | head -4
curl -s -H 'Authorization: Bearer q1w2e3r4-admin' http://127.0.0.1:3903/metrics | grep -cE '^[a-z_]+'

实测回显:

# HELP api_admin_request_counter Number of API calls to the various Admin API endpoints
# TYPE api_admin_request_counter counter
api_admin_request_counter{api_endpoint="/metrics"} 1
# HELP api_admin_request_duration Duration of API calls to the various Admin API endpoints
2590

/metrics 端点输出标准 Prometheus 文本(本集群 2590 行指标),关注块存储(garage_block_)、RPC(garage_rpc_)、S3 请求延迟等指标族;令牌用 metrics_token 单独配置可实现监控最小授权。

7.3 日志查看与排障

journalctl -u garage -n 20 --no-pager
journalctl -u garage -f

实测日志样例(WARN 级,peer 地址解析失败的典型案例):

Oct 17 04:41:12 node1 garage[2689]: 2026-10-17T04:41:12.933866Z  WARN garage_rpc::system: Unable to parse and/or resolve peer hostname node1:3901

排障入口:启动失败看 journalctl -u garage -n 30 的 Error 行(配置/权限类错误直接给出原因);集群异常用 garage status(看 FAILED NODES)+ garage health(看分区健康)+ 各节点 garage worker list(看 resync 队列)三板斧。

八、常见问题(FAQ)

Q1:新节点加入后一直在 "NO ROLE ASSIGNED"? layout assign 与 apply 只需在集群任一节点执行一次;三节点均无角色说明 assign 没提交。按 3.3 节顺序补齐 assign(每台一条)与 layout apply --version N。

Q2:节点间连不通,日志报 Unable to parse and/or resolve peer hostname? v2.4.1 的 bootstrap_peers 只接受 <完整节点ID>@<IP>:3901 格式(用对端 garage node id 输出),主机名/裸 IP 都不行;同时用 nc -zv <IP> 3901 排查防火墙。

Q3:集群 DEGRADED 会影响业务吗? RF=3 下停 1 节点仅降级:256 分区 quorum 均满足,读写照常(本文 4.1 节实测);停 2 个节点 quorum 丢失分区不可访问。保持至少 2 个节点在线是 RF=3 的读写底线。

Q4:Text file busy 无法覆盖二进制? 运行中的 garage 二进制不能直接覆盖。升级顺序:systemctl stop garage → 覆盖 → systemctl start garage(本文 5.2 节实测踩坑)。

Q5:更换节点怎么退役旧节点? 新节点 assign 加入并 apply 后,把旧节点 capacity 置 0(garage layout assign -c 0 d0bb67c993d97376,末尾为被退役节点的短 ID,用 garage status 查询)再 apply,数据自动迁移走;garage status 确认数据迁空后才能下线旧节点,最后在所有节点更新 bootstrap_peers。

Q6:rpc_secret 泄露了怎么办? 集群门禁失守,任何拿到 secret 的机器都可能被引导入群。处置:生成新 secret,按官方指引逐节点轮换(写入各节点配置文件并滚动重启),确认新 secret 后废弃旧值。

九、参考链接