Garage на сервере: частые ошибки и решения
Garage — S3-совместимое объектное хранилище, которое изначально писали под геораспределённые кластеры на слабом и разнородном железе: несколько нод в разных дата-центрах, разная сеть между ними, без общего блочного стораджа. В отличие от MinIO, Garage не требует одинаковых по размеру дисков на всех узлах и не падает в read-only при рассинхроне кворума так болезненно. Но у него своя специфика — и большинство проблем на практике сводится к десятку повторяющихся ошибок на старте и при масштабировании кластера. Разберём их по порядку — от первого запуска до эксплуатации на трёх площадках.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Ошибка при первом запуске: rpc_secret и garage.toml
Самая частая причина, по которой узел вообще не стартует или не видит соседей, — рассинхрон rpc_secret между нодами. Это общий секрет для RPC-протокола, которым узлы аутентифицируют друг друга, и если он не совпадает побайтово, узлы просто не смогут установить связь — в логе будет что-то вроде Handshake error или узел останется в статусе "no known nodes".
Минимальный рабочий /etc/garage.toml выглядит так:
metadata_dir = "/var/lib/garage/meta"
data_dir = "/var/lib/garage/data"
db_engine = "lmdb"
replication_factor = 3
rpc_bind_addr = "[::]:3901"
rpc_public_addr = "203.0.113.10:3901"
rpc_secret = "generate_me_with_openssl_rand_hex_32"
[s3_api]
s3_region = "garage"
api_bind_addr = "[::]:3900"
root_domain = ".s3.example.com"
[s3_web]
bind_addr = "[::]:3902"
root_domain = ".web.example.com"
[admin]
api_bind_addr = "127.0.0.1:3903"
Секрет генерируется один раз и копируется на все узлы буквально одной и той же строкой:
openssl rand -hex 32
Частая ошибка — сгенерировать секрет на каждой ноде отдельно, "чтобы был свой". Это выглядит логично по аналогии с паролями, но в Garage секрет один на весь кластер — как общий ключ доступа к внутренней сети RPC, а не индивидуальная учётка узла. Идентичность самого узла определяется отдельно, автоматически сгенерированным Node ID при первом запуске (garage node id).
Вторая типичная ошибка на этом же этапе — забыть rpc_public_addr, если сервер за NAT или у него несколько сетевых интерфейсов. Без явного адреса Garage пытается угадать, по какому IP его видно снаружи, и на VPS с приватной и публичной сетью почти всегда угадывает неправильно — узлы либо не соединяются вовсе, либо соединяются только локально и разваливаются при первой проверке из другого дата-центра.
Кластер не собирается: layout и capacity
Установка и запуск демона — это только половина дела. Пока вы не назначили layout (топологию кластера с capacity и зонами для каждого узла), Garage не будет принимать данные — попытка создать бакет или загрузить объект вернёт ошибку про отсутствие ring layout.
Смотрим статус и берём Node ID каждого узла:
garage status
Дальше назначаем layout — здесь и происходит вторая по частоте ошибка:
garage layout assign -z eu-west -c 500G <node_id_1>
garage layout assign -z eu-east -c 500G <node_id_2>
garage layout assign -z us-east -c 1T <node_id_3>
garage layout apply --version 1
Забытый garage layout apply — классика: assign только готовит изменение, оно не применяется само. Кластер продолжает жить со старым (или пустым) layout, а новый узел числится в garage status как "не в кластере", хотя формально уже подключён по RPC.
Вторая ловушка — capacity указывается не в реальном свободном месте на диске, а в той доле хранилища, которую вы сознательно выделяете под Garage. Если указать capacity больше, чем реально есть на разделе, узел будет продолжать принимать данные до заполнения диска и упадёт с ошибкой записи в LMDB или в файловую систему данных — Garage сам не проверяет реальное свободное место при assign, это ответственность администратора.
Третье: зоны (-z) — это не декоративная метка, а основа алгоритма репликации. При replication_factor = 3 Garage старается разложить три копии данных по трём разным зонам. Если у вас три узла, но все три с одним значением -z, репликация всё равно отработает (три копии на трёх нодах), но геораспределённой отказоустойчивости на уровне зон вы не получите — отключение дата-центра, где физически стоят все три сервера, положит кластер целиком.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверRPC между узлами: таймауты и firewall
Между узлами кластера постоянно идёт RPC-трафик — обмен метаданными, состоянием layout, репликация объектов. Порт по умолчанию — 3901/TCP, и если он закрыт файрволом хотя бы в одну сторону, вы увидите характерную картину: garage status на одном узле показывает соседа как "здоровый", а через минуту — как недоступный, и так по кругу.
Проверка с одного узла на другой:
nc -zv 203.0.113.11 3901
Если используете ufw, правило нужно открыть точечно на IP соседних узлов, а не на весь мир:
ufw allow from 203.0.113.11 to any port 3901 proto tcp
ufw allow from 203.0.113.12 to any port 3901 proto tcp
Отдельная головная боль геораспределённых кластеров — рассинхрон часов. Garage полагается на логическое время (векторные часы) для разрешения конфликтов записи, но заметный дрейф системных часов между узлами всё равно ухудшает диагностику и иногда приводит к странным задержкам в применении layout. Проверьте, что на всех узлах работает systemd-timesyncd или chrony, и часовые пояса в логах совпадают — иначе поиск причины конкретной ошибки по таймстампам превращается в отдельный квест.
Если узлы разнесены по разным облачным провайдерам с разным NAT, дополнительно убедитесь, что rpc_public_addr действительно достижим извне, а не только изнутри VPC провайдера — это ровно та ошибка, которая на одном облаке "просто работает", а при добавлении узла у другого хостера ломает весь кластер.
S3 API: бакеты, ключи и права доступа
Garage разделяет два понятия: глобальный доступ к кластеру (ключи S3) и доступ конкретного ключа к конкретному бакету. Это отличается от привычной модели MinIO с политиками IAM, и путаница здесь — источник половины тикетов "почему AccessDenied".
Создание ключа и бакета:
garage key create my-app-key
garage bucket create my-app-bucket
garage bucket allow --read --write my-app-bucket --key my-app-key
Без последней команды ключ будет валидным (аутентификация пройдёт), но любой запрос к бакету вернёт AccessDenied — созданный ключ по умолчанию не имеет прав ни на один бакет, это осознанное решение авторов, а не баг.
Второй частый источник ошибок — региональное имя. Garage использует собственный "регион" (s3_region в конфиге, по умолчанию garage), и если в S3-клиенте (aws-cli, rclone, SDK) прописан us-east-1 по умолчанию, а не значение из вашего конфига, часть операций — особенно подпись запросов SigV4 — будет падать с ошибкой подписи, хотя ключи и секрет введены верно.
Пример рабочего конфига для rclone:
[garage]
type = s3
provider = Other
access_key_id = GK...
secret_access_key = ...
endpoint = https://s3.example.com
region = garage
Третья ошибка — путать endpoint кластера и endpoint конкретного узла. В геораспределённом кластере лучше ставить перед узлами общий балансировщик или хотя бы round-robin DNS на публичные адреса всех нод — если S3-клиент жёстко смотрит в один узел, а тот временно недоступен (перезагрузка, обновление), приложение получит таймаут вместо честного отказоустойчивого поведения, которое Garage в принципе способен обеспечить.
Геораспределение: репликация, задержки, недоступность зоны
Ради чего вообще берут Garage вместо MinIO — геораспределённые кластеры с узлами в разных дата-центрах и даже странах. Здесь всплывает нюанс, о котором часто забывают на этапе планирования: replication_factor = 3 означает, что запись объекта подтверждается кластером только после того, как данные (или кворум по метаданным) подтвердили минимум два из трёх узлов. Если узлы разнесены по разным континентам, каждая запись ждёт сетевого round-trip до второй по скорости площадки — это не баг, это плата за консистентность при географическом разнесении, и на медленных каналах (трансатлантика, азиатские линки) она заметна на глаз при заливке большого числа мелких объектов.
Практический вывод — закладывайте это в архитектуру заранее, а не удивляйтесь позже:
- держите узлы одной "тройки" репликации в регионах с приемлемой связностью между собой, а не в максимально далёких точках "для красоты";
- для нагрузок с большим числом мелких записей (логи, метрики) буферизуйте заливку в Garage пачками, а не по одному объекту;
- мониторьте RPC-задержки между зонами отдельно от общей доступности узла — узел может быть "жив", но с деградировавшей сетью до одной из зон, и это не всегда заметно в
garage statusсразу.
Если один узел зоны временно выпал (перезагрузка, сетевой сбой), кластер с replication_factor = 3 и тремя зонами продолжает отдавать и принимать данные — это и есть весь смысл конструкции. Но при длительной недоступности узла стоит явно вывести его из layout (garage layout assign -c 0), а не оставлять "подвисшим" — иначе Garage будет держать эту зону как целевую для репликации и копить очередь синхронизации, которая потом разом хлынет при возврате узла в строй.
Диск, память и метаданные LMDB
Garage хранит метаданные (индекс объектов, состояние layout, очереди репликации) в отдельном движке — по умолчанию LMDB, есть также вариант на sqlite. LMDB быстрый, но чувствителен к диску, на котором лежит metadata_dir: если это тот же медленный сетевой диск, что и под данными, под нагрузкой начинают расти латентности операций записи, и в логах появляются предупреждения о долгих транзакциях.
Практическая рекомендация — держать metadata_dir на NVMe или хотя бы на быстром локальном SSD, даже если data_dir лежит на более дешёвом и объёмном томе:
metadata_dir = "/mnt/nvme/garage-meta"
data_dir = "/mnt/hdd/garage-data"
По памяти Garage экономнее MinIO — нет отдельного кеша erasure coding, — но LMDB мапит метаданные через mmap, и на кластерах с миллионами мелких объектов резидентная память процесса заметно растёт. Точных цифр "сколько RAM на миллион объектов" давать не будем — это зависит от размера ключей и версии Garage; ориентируйтесь на мониторинг конкретного узла (RSS из /proc или smem) и закладывайте запас, а не берёте минимальную конфигурацию впритык.
Если Garage запускается в Docker, отдельная ошибка — монтировать data_dir и metadata_dir в один и тот же volume без разделения на подпапки с правильными правами. Процесс в контейнере обычно работает не от root, и при первом запуске на свежесмонтированном томе получает Permission denied на запись — решается явным chown на хосте под UID, под которым слушает процесс в образе, до первого старта контейнера.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Чем Garage принципиально отличается от MinIO для геораспределённого кластера?
MinIO проектировался под erasure coding в рамках одного дата-центра с быстрой и стабильной сетью между нодами; при разнесении узлов по разным площадкам с неровной сетью он деградирует заметнее. Garage изначально писали для разнородных узлов и высоких задержек — ценой более строгой модели репликации и без erasure coding как такового.
Можно ли запустить Garage на одном сервере вместо кластера?
Да, с replication_factor = 1 и одним узлом — это рабочий сценарий для тестов или небольших нагрузок, но тогда исчезает главный смысл Garage, и для одиночного сервера часто проще и привычнее взять MinIO — сравнение подходов есть в отдельном разборе MinIO или Urbackup по соседней теме резервного копирования.
Что делать, если после layout apply узел так и не появился в кластере?
Проверьте RPC-связность (nc -zv <ip> 3901) в обе стороны и совпадение rpc_secret побайтово — лишний пробел или перенос строки при копировании секрета в конфиг встречается чаще, чем кажется.
Нужен ли перед Garage отдельный TLS-терминатор?
Сам Garage не занимается TLS для S3 API — перед ним обычно ставят Caddy с авто-SSL или nginx, которые уже терминируют сертификат и проксируют на api_bind_addr.
Как быстро понять, что кластер деградировал по сети, а не завис?
Смотрите garage status на предмет статусов узлов и отдельно — задержки RPC между зонами через garage stats или системные метрики сети; узел, который отвечает на пинг, но не успевает в RPC-таймаут, в общем статусе может выглядеть здоровым дольше, чем стоило бы.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →