Przejdź do głównej zawartości

Wewnętrzny backend S3 (Garage)

Opcjonalny profil dla wdrożeń, które nie chcą prowizjonować zewnętrznego dostawcy S3 (AWS S3, Cloudflare R2, Backblaze B2, MinIO itp.). Uruchamia Garage jako jednowęzłowy kontener wewnątrz stacku Compose i serwuje buckety, których potrzebuje API.

  • Self-hosted bez płatnego dostawcy S3.
  • Środowiska air-gapped lub z ograniczeniami compliance, gdzie dane muszą zostać na tym samym hoście.
  • Instalacje próbne / staging bez zewnętrznych zależności.

Serwis garage w Compose uruchamia garage server --single-node --default-access-key:

  • --single-node automatycznie aplikuje jednowęzłowy layout klastra przy pierwszym starcie.
  • --default-access-key automatycznie tworzy access key S3 z GARAGE_DEFAULT_ACCESS_KEY / GARAGE_DEFAULT_SECRET_KEY (Compose mapuje je z S3_API_KEY / S3_API_SECRET w .env).

API tworzy swoje buckety przy pierwszym starcie — bez ręcznego bootstrap-u. Kiedy wywołuje CreateBucket przez S3 API, Garage przyznaje wywołującemu kluczowi własność bucketu, więc API od razu może czytać i pisać.

W .env:

Terminal window
COMPOSE_PROFILES=garage # lub backup,garage dla obu
S3_ENDPOINT=http://garage:3900

Bez garage w COMPOSE_PROFILES serwis jest zdefiniowany w docker-compose.yaml, ale się nie uruchamia.

Garage potrzebuje pary kluczy S3 (które wykorzystuje API) i wewnętrznego sekretu RPC:

Terminal window
echo "S3_API_KEY=GK$(openssl rand -hex 16)"
echo "S3_API_SECRET=$(openssl rand -hex 32)"
echo "GARAGE_RPC_SECRET=$(openssl rand -hex 32)"

Wklej trzy linie do .env. Prefiks GK to konwencja Garage, dzięki której klucz łatwo rozpoznać w logach — dowolny ciąg działa, ale zostaw go.

Terminal window
docker compose pull
docker compose up -d

To cała konfiguracja. Przy pierwszym starcie:

  1. Garage aplikuje jednowęzłowy layout klastra (nie trzeba ręcznie garage layout assign).

  2. Garage tworzy access key z GARAGE_DEFAULT_ACCESS_KEY / GARAGE_DEFAULT_SECRET_KEY.

  3. API uruchamia migracje, tworzy bucket projektu (sancho-env-${DEPLOY_ENV}), a następnie seeduje konto root — co tworzy bucket konta (sancho-env-${DEPLOY_ENV}-account-1).

Oba buckety są własnością wywołującego klucza, więc API może z nich od razu korzystać.

Terminal window
docker compose exec garage /garage status # node Up, layout zaaplikowany
docker compose exec garage /garage key list # auto-importowany klucz
docker compose exec garage /garage bucket list # 2 buckety, oba z kluczem allowed
# End-to-end: wgraj awatar z aplikacji webowej, potem
ls ./data/garage/data # pliki blob się pojawiają

Jeśli ustawisz dodatkowo COMPOSE_PROFILES=backup,garage, serwis postgres-backup pisze codzienne dumpy do Garage. Nie tworzy swojego bucketu samodzielnie, więc uruchom raz:

Terminal window
docker compose exec garage /garage bucket create "$BACKUP_S3_BUCKET"

Garage przyznaje domyślnemu kluczowi pełny dostęp przy tworzeniu bucketu, więc krok bucket allow nie jest potrzebny. Następnie docker compose exec postgres-backup /backup.sh wymusza natychmiastowy dump jako test poprawności.

Garage binduje 3900 (S3 API), 3901 (RPC) i 3903 (admin) wewnątrz sieci sancho (garage.toml nie konfiguruje [s3_web], więc port web 3902 nie jest bindowany). Żaden z nich nie jest wystawiony na hosta. API dociera do Garage przez http://garage:3900; do CLI ad-hoc używaj docker compose exec garage /garage ....

Ścieżka Zawartość
./data/garage/meta Metadane SQLite, stan bucketów/kluczy
./data/garage/data Bloby obiektów

Snapshotuj oba katalogi razem — rozdzielenie ich między snapshotami daje niespójny stan. Zatrzymaj stack (docker compose down) przed kopiami plikowymi, żeby uniknąć wyścigów na lockach.

Terminal window
docker compose down
sudo rm -rf ./data/garage
sudo tar -xzf garage-data-YYYY-MM-DD.tar.gz # rozpakowuje ./data/garage/{meta,data}
docker compose up -d

Access key, lista bucketów i allow-list przetrwają w snapshocie metadanych — żadnych dodatkowych kroków po restore.

Garage śledzi klucze po ID. Zmiana S3_API_KEY w .env sprawia, że stary klucz jest bezużyteczny po stronie API, ale dalej istnieje w Garage:

  1. Wygeneruj nową parę:

    Terminal window
    NEW_KEY="GK$(openssl rand -hex 16)"; NEW_SECRET="$(openssl rand -hex 32)"
  2. Zaimportuj jako nowy klucz w Garage:

    Terminal window
    docker compose exec garage /garage key import "$NEW_KEY" "$NEW_SECRET" -n sancho-new --yes
  3. Nadaj dostęp do każdego bucketu, który mógł obsłużyć stary klucz:

    Terminal window
    for b in $(docker compose exec garage /garage bucket list | awk '/^ /{print $1}'); do
    docker compose exec garage /garage bucket allow --read --write --owner "$b" --key "$NEW_KEY"
    done
  4. Zaktualizuj .env i zrestartuj api + postgres-backup:

    Terminal window
    sed -i -e "s|^S3_API_KEY=.*|S3_API_KEY=$NEW_KEY|" \
    -e "s|^S3_API_SECRET=.*|S3_API_SECRET=$NEW_SECRET|" .env
    docker compose up -d api postgres-backup
  5. (Opcjonalnie) usuń stary klucz po potwierdzeniu, że nowy działa:

    Terminal window
    docker compose exec garage /garage key delete <OLD_KEY_ID> --yes