MAATRIX / Блог / Garage в Docker Compose: готовый файл

Garage в Docker Compose: готовый файл

MAATRIX

Если серверов под объектное хранилище несколько и они стоят в разных дата-центрах или даже странах, MinIO начинает доставлять хлопоты: его erasure coding рассчитан на однородные узлы с быстрой сетью между ними, а не на три VPS в разных локациях с непредсказуемым пингом. Garage — S3-совместимое хранилище от французской ассоциации Deuxfleurs, написанное на Rust специально для геораспределённых и разнородных кластеров: меньше памяти, проще модель согласованности, честная работа при отвале одного узла. Дальше — готовый docker-compose.yml и пошаговая инициализация кластера от одного узла до трёх в разных локациях.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Что такое Garage и чем он легче MinIO

Garage реализует подмножество S3 API — достаточное для большинства приложений: PutObject, GetObject, DeleteObject, multipart-загрузка, бакеты, политики доступа, статический веб-хостинг из бакета. Чего в нём нет или что реализовано ограниченно — версионирование объектов, Object Lock, часть административных вызовов AWS. Если приложению нужен именно этот функционал, сверьтесь с актуальным списком поддерживаемых операций в документации проекта перед тем, как переключать прод.

Ключевое отличие от MinIO — модель согласованности. MinIO для избыточности использует erasure coding: файл режется на части и раскладывается по дискам, что требует предсказуемой и быстрой сети между узлами. Garage вместо этого хранит целые копии объектов (репликация, а не шардирование) и использует CRDT для метаданных — структуры данных, которые сходятся к одному состоянию независимо от порядка получения обновлений. Узлы не обязаны договариваться синхронно через Raft-подобный консенсус на каждую операцию, поэтому кластер переживает временные разрывы связи между дата-центрами без блокировки записи.

Практический эффект: Garage можно раскидать по трём разным провайдерам в трёх странах, и он будет работать, пока связь есть хотя бы с частью узлов, определяющей кворум. Второй плюс — потребление ресурсов: одному узлу Garage для небольшого кластера хватает 256-512 МБ памяти, тогда как MinIO в кластерном режиме обычно требует заметно больше, особенно на объём с большим числом мелких объектов.

Обратная сторона — Garage моложе, и сообщество меньше: меньше готовых интеграций, нет встроенной веб-консоли (только CLI и минимальный HTTP admin API). Для зрелого инструмента с графическим интерфейсом присмотритесь к MinIO в Docker Compose. Для геораспределённого кластера на разношёрстных серверах с минимальным потреблением ресурсов — Garage создан именно для этого.

Архитектура: узлы, зоны и layout

Кластер Garage состоит из узлов (nodes), каждый — отдельный процесс/контейнер со своим node_id (генерируется автоматически из ключа при первом запуске). Узлы объединяются в кластер через RPC-соединение по TCP (порт 3901 по умолчанию), для которого все узлы должны знать общий rpc_secret — просто shared-секрет, а не сертификат.

Логическое распределение данных задаётся через layout — таблицу, в которой каждому узлу назначается зона (zone, обычно = дата-центр или регион) и ёмкость (capacity, вес узла в кластере). Garage распределяет реплики объекта так, чтобы по возможности они попадали в разные зоны — это и даёт геораспределённость: при replication_factor = 3 и трёх зонах каждый объект физически лежит в трёх разных локациях.

Три ключевых порта на каждом узле:

ПортНазначение
3900S3 API (то, к чему подключается приложение/клиент)
3901RPC между узлами кластера
3902Статический веб-хостинг из бакета (опционально)
3903Admin API — статус, layout, метрики Prometheus

Метаданные (список объектов, layout, ключи доступа) Garage хранит в отдельной БД на движке LMDB или Sled, сами данные объектов — в data_dir как есть, файлами. Это упрощает бэкап и диагностику: можно посмотреть на файловую систему и понять, что где лежит.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Готовый docker-compose.yml для одного узла

Начнём с одного узла — этого достаточно, чтобы проверить, что всё работает, прежде чем разносить кластер по локациям. Структура каталогов на хосте:

mkdir -p /opt/garage/{etc,meta,data}

Конфиг /opt/garage/etc/garage.toml:

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

replication_factor = 1

rpc_bind_addr = "[::]:3901"
rpc_public_addr = "127.0.0.1:3901"
rpc_secret = "ВАШ_RPC_SECRET_64_HEX_СИМВОЛА"

[s3_api]
s3_region = "garage"
api_bind_addr = "[::]:3900"
root_domain = ".s3.example.com"

[s3_web]
bind_addr = "[::]:3902"
root_domain = ".web.example.com"
index = "index.html"

[admin]
api_bind_addr = "[::]:3903"
admin_token = "ВАШ_ADMIN_TOKEN"
metrics_token = "ВАШ_METRICS_TOKEN"

rpc_secret сгенерируйте один раз и используйте одинаковым на всех узлах будущего кластера:

openssl rand -hex 32

docker-compose.yml:

services:
  garage:
    image: dxflrs/garage:v1.0.1
    container_name: garage
    restart: unless-stopped
    ports:
      - "3900:3900"
      - "3901:3901"
      - "3902:3902"
      - "3903:3903"
    volumes:
      - ./etc/garage.toml:/etc/garage.toml:ro
      - ./meta:/var/lib/garage/meta
      - ./data:/var/lib/garage/data

Версию образа dxflrs/garage перед запуском сверьте с тегами на Docker Hub или релизами на GitHub проекта — Garage обновляется чаще, чем этот текст, а мажорная версия влияет на формат конфига. Поднимаем:

docker compose up -d
docker compose logs -f garage

В логе при первом старте появится сгенерированный node_id — он понадобится на следующем шаге.

Инициализация кластера, бакеты и ключи доступа

Даже одиночному узлу нужен layout — без него Garage не начинает принимать данные. Все команды выполняются через garage CLI внутри контейнера:

docker compose exec garage garage status

Вывод покажет узел со статусом NO ROLE ASSIGNED и его ID (короткий хеш вида a1b2c3d4...). Назначаем ему зону и ёмкость, затем применяем layout:

docker compose exec garage garage layout assign -z dc1 -c 10G a1b2c3d4
docker compose exec garage garage layout show
docker compose exec garage garage layout apply --version 1

Версию (--version 1) Garage подскажет сам в выводе layout show — это защита от случайного применения layout, рассчитанного на другое состояние кластера. После применения garage status должен показать узел как healthy.

Дальше — бакет и ключ доступа:

docker compose exec garage garage bucket create my-app-storage
docker compose exec garage garage key create my-app-key
docker compose exec garage garage bucket allow \
  --read --write --owner my-app-storage --key my-app-key

Команда key create выведет Key ID и Secret Access Key — сохраните их сразу, повторно secret не показывается (можно только пересоздать ключ). Эти два значения и есть то, что вы пропишете в приложении вместо AWS-ключей, s3_region из конфига (garage в примере выше) — вместо AWS-региона.

Геораспределённый кластер на несколько локаций

Смысл Garage раскрывается, когда узлов больше одного и они физически разнесены. Сценарий: три VPS в разных локациях — например, в России, ЕС и США. На каждом сервере — тот же docker-compose.yml и тот же garage.toml, но с двумя отличиями: rpc_public_addr указывает на публичный IP именно этого сервера, а rpc_secret — одинаковый на всех трёх.

rpc_public_addr = "203.0.113.10:3901"

Порт 3901 должен быть открыт между узлами (в файрволе — разрешить входящие с IP остальных узлов кластера, не открывать всему интернету без необходимости). После старта всех трёх контейнеров узлы нужно свести в один кластер командой garage node connect, выполненной с любого из них в сторону остальных:

docker compose exec garage garage node connect <node_id_2>@203.0.113.20:3901
docker compose exec garage garage node connect <node_id_3>@203.0.113.30:3901

Дальше — layout с тремя зонами и replication_factor = 3 в конфиге каждого узла (после первого запуска фактор репликации без пересоздания кластера не поменять, так что закладывайте его сразу):

docker compose exec garage garage layout assign -z ru -c 10G <node_id_1>
docker compose exec garage garage layout assign -z eu -c 10G <node_id_2>
docker compose exec garage garage layout assign -z us -c 10G <node_id_3>
docker compose exec garage garage layout apply --version 1

С этого момента каждый записанный объект реплицируется в три зоны, и клиент может обращаться к S3 API любого из трёх узлов — данные будут те же. Для распределения клиентов по узлам обычно ставят перед кластером балансировщик или geo-DNS, отдающий ближайший узел; если это ваш случай, пригодится статья про гео-DNS и когда он нужен. Учтите и задержку между зонами: запись подтверждается, когда кворум реплик её принял, поэтому медленный канал между дата-центрами замедлит запись — для бэкапов и статики это не критично, но для синхронной записи с жёстким SLA латентность стоит измерить заранее на вашей паре локаций.

Проверка S3 API, мониторинг и бэкап метаданных

Проверить, что S3 API отвечает, можно любым S3-клиентом. Быстрее всего — через rclone, который умеет работать с произвольным S3-совместимым эндпоинтом:

[garage]
type = s3
provider = Other
access_key_id = ВАШ_KEY_ID
secret_access_key = ВАШ_SECRET
endpoint = http://ВАШ_СЕРВЕР:3900
region = garage
rclone lsd garage:
rclone copy ./test.txt garage:my-app-storage/

Если предпочитаете держать rclone тоже в контейнере рядом — есть готовый rclone в Docker Compose. Тем же способом подключаются aws-cli (aws --endpoint-url http://ВАШ_СЕРВЕР:3900 s3 ls) и SDK приложения — меняете только endpoint_url, region и ключи.

Мониторинг: admin API отдаёт метрики в формате Prometheus на /metrics порта 3903 (авторизация — заголовком с metrics_token из конфига). Основные метрики — число объектов, занятое место по узлам, состояние layout, задержки RPC между узлами; последнее особенно важно в геораспределённом кластере, где рост RPC-латентности — первый признак проблем с сетью между локациями.

Бэкап в Garage — двухуровневый. Сами данные объектов уже реплицированы кластером, поэтому отдельно бэкапить их обязательно только если репликация меньше 3 или вы не доверяете кластеру целиком (например, единая точка отказа в оплате всех серверов у одного провайдера). Метаданные (layout, бакеты, ключи) — то, что действительно больно потерять: держите свежую копию garage.toml и rpc_secret вне серверов кластера, а сам каталог meta можно снапшотить штатными средствами тома (LVM/ZFS snapshot) или синхронизировать в другой бакет через rclone sync. Общие принципы разворачивания Docker Compose на проде, включая типичные грабли с правами на volume и restart-политиками, разобраны в статье про частые ошибки Docker Compose на сервере.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Чем Garage принципиально отличается от MinIO, если оба говорят по S3?

Моделью отказоустойчивости: MinIO режет объект на части (erasure coding) и рассчитан на однородный кластер с быстрой сетью, Garage хранит целые копии и использует CRDT для метаданных, что делает его устойчивее к разрывам связи между разными дата-центрами и легче по ресурсам на узел.

Можно ли начать с одного узла, а потом добавить ещё два для геораспределения?

Да, но replication_factor в конфиге поменять на лету нельзя без пересоздания кластера — если план на 3 узла в разных локациях, закладывайте replication_factor = 3 сразу, даже если на старте физически один узел.

Нужен ли отдельный балансировщик перед кластером Garage?

Не обязательно — любой узел отвечает на S3-запросы с одинаковыми данными, клиент может стучаться в любой. Балансировщик или geo-DNS нужны, если хотите направлять клиентов на ближайший географически узел ради задержки.

Есть ли у Garage веб-интерфейс, как консоль в MinIO?

Встроенной консоли у Garage нет, только CLI и HTTP admin API. Сторонние веб-панели существуют, но не входят в официальную поставку и их зрелость стоит проверить перед продакшн-использованием.

Что будет, если один из трёх узлов в другой стране надолго отвалится?

Кластер продолжит принимать запросы, пока доступен кворум оставшихся узлов и зон, покрывающих replication_factor; данные, которые были только на упавшем узле, восстановятся репликацией после его возврата в кластер.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →