feat(infra): déploie Garage par environnement, secrets par le .env, fumée S3 en CI

Reprend l'amorce de la PR 164 et l'intègre à la stack : service `garage` (dxflrs/garage v2.4.1,
`--single-node --default-bucket`) dans docker-compose.yml, garage.toml versionné sans secret,
ports sur 127.0.0.1 décalés par environnement dans provision-host.sh, garde des six clés
GARAGE_* dans le Makefile, cible Prometheus avec jeton, tests de fumée déplacés dans
tests/garage et joués par le job compose d'infra.yml contre le vrai conteneur, SSE-C compris.
Variables APP_S3_* et APP_READING_RETENTION_DAYS posées sur airflow-scheduler pour le DAG
`retention`. ADR 0019.

Closes #24
This commit is contained in:
Johan LEROY
2026-09-24 10:30:04 +02:00
parent 0a7a802b38
commit e53c7e441c
19 changed files with 372 additions and 105 deletions
+45
View File
@@ -82,6 +82,37 @@ services:
- "${MAILPIT_UI_PORT:-8025}:8025"
restart: unless-stopped
# Piège : image `FROM scratch`, sans shell : healthcheck en forme exec, et aucune garde shell sur
# les secrets. Un GARAGE_RPC_SECRET vide ou non hexadécimal fait échouer Garage lui-même, message
# explicite dans ses journaux ; `make services-up` et `make stack-up` vérifient le .env avant.
# Piège : GARAGE_SECRET_KEY ne se change pas sur un volume `garage_meta` déjà peuplé, Garage
# refuse alors de démarrer. Rotation par `garage key` ou par recréation du volume (ADR 0019).
garage:
image: dxflrs/garage:v2.4.1
command: ["/garage", "server", "--single-node", "--default-bucket"]
environment:
GARAGE_RPC_SECRET: ${GARAGE_RPC_SECRET:-}
GARAGE_ADMIN_TOKEN: ${GARAGE_ADMIN_TOKEN:-}
GARAGE_METRICS_TOKEN: ${GARAGE_METRICS_TOKEN:-}
GARAGE_DEFAULT_ACCESS_KEY: ${GARAGE_ACCESS_KEY:-}
GARAGE_DEFAULT_SECRET_KEY: ${GARAGE_SECRET_KEY:-}
GARAGE_DEFAULT_BUCKET: ${GARAGE_BUCKET:-enervision-archives}
volumes:
- ./infra/garage/garage.toml:/etc/garage.toml:ro
- garage_meta:/var/lib/garage/meta
- garage_data:/var/lib/garage/data
ports:
- "127.0.0.1:${GARAGE_S3_PORT:-3900}:3900"
- "127.0.0.1:${GARAGE_ADMIN_PORT:-3903}:3903"
healthcheck:
test: ["CMD", "/garage", "health", "-q"]
interval: 15s
timeout: 5s
retries: 6
start_period: 20s
mem_limit: 256m
restart: unless-stopped
backend:
build: ./apps/backend
depends_on:
@@ -178,6 +209,15 @@ services:
APP_MOCK_API_USERNAME: ${APP_MOCK_API_USERNAME:-}
APP_MOCK_API_PASSWORD: ${APP_MOCK_API_PASSWORD:-}
APP_MOCK_API_TIMEOUT_SECONDS: ${APP_MOCK_API_TIMEOUT_SECONDS:-10}
# Le DAG `retention` archive les chunks de `reading` sur le Garage du projet (ADR 0019),
# chiffrés par la clé SSE-C du .env (ADR 0020). Vides, `app.etl.reading_retention` refuse seul.
APP_S3_ENDPOINT_URL: http://garage:3900
APP_S3_REGION: garage
APP_S3_ACCESS_KEY: ${GARAGE_ACCESS_KEY:-}
APP_S3_SECRET_KEY: ${GARAGE_SECRET_KEY:-}
APP_S3_BUCKET: ${GARAGE_BUCKET:-enervision-archives}
APP_S3_SSE_KEY: ${GARAGE_SSE_KEY:-}
APP_READING_RETENTION_DAYS: ${READING_RETENTION_DAYS:-1095}
depends_on:
db:
condition: service_healthy
@@ -209,6 +249,7 @@ services:
- prometheus_data:/prometheus
secrets:
- metrics_token
- garage_metrics_token
ports:
- "127.0.0.1:${PROMETHEUS_PORT:-9090}:9090"
mem_limit: 512m
@@ -318,6 +359,8 @@ services:
volumes:
pgdata:
garage_meta:
garage_data:
airflow_logs:
airflow_ml_state:
prometheus_data:
@@ -328,3 +371,5 @@ volumes:
secrets:
metrics_token:
environment: APP_METRICS_TOKEN
garage_metrics_token:
environment: GARAGE_METRICS_TOKEN