axe/compose.prod.yaml
edadmin 4f254d9faa
Some checks are pending
backend / test (push) Waiting to run
backend / lint (push) Waiting to run
backend / build-api (push) Waiting to run
backend / deploy (push) Blocked by required conditions
ci: Gitea Actions instead of Drone (workflow, README, secrets)
2026-10-18 23:22:02 +03:00

161 lines
6.4 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Прод-стек: db, minio, api и web в ОДНОЙ сети; наружу отдаёт только
# web (порт 80). Postgres и MinIO портов наружу не пробрасывают вовсе —
# они видны только контейнерам приложения.
#
# Образы api и web заранее собраны CI (или вручную):
# docker build -t building-mgmt-api:latest . # в этом репо
# docker build -t building-mgmt-web:latest . # в репо web
#
# Запуск и обновление (env-файл живёт на сервере, в git не входит):
# docker compose -p building-mgmt \
# --env-file /opt/building-mgmt/.env \
# -f compose.prod.yaml up -d --wait
#
# Имя проекта (-p) закреплено: по нему считается имя сети. CI и
# ручной запуск обязаны давать одно и то же имя, иначе web не найдёт
# api и стек разъедется на два.
#
# ${VAR} берутся из env-файла (--env-file): секреты в YAML не
# дублируются. `:?` — фейл-фаст: без обязательной переменной compose
# упадёт при разборе файла, а не поднимет стек с пустым паролем.
services:
db:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER:?POSTGRES_USER is required}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}
POSTGRES_DB: ${POSTGRES_DB:?POSTGRES_DB is required}
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 5s
timeout: 5s
retries: 12
# Потолок памяти: без него при аномалии postgres сожрёт всю машину.
# Подправьте под ресурсы сервера.
mem_limit: 1g
networks:
- app
# Портов нет: наружу база не видна, только сеть app.
minio:
# Образ собирается из официального бинарника релиза 2024-07-26 —
# версии до ухода MinIO из опенсорса (см. deploy/minio/Dockerfile).
build:
context: deploy/minio
image: minio/minio:RELEASE.2024-07-26T20-48-21Z
restart: unless-stopped
command: server /data
environment:
# Ключи MinIO — это же S3-ключи приложения: одна пара секретов
# вместо двух.
MINIO_ROOT_USER: ${S3_ACCESS_KEY:?S3_ACCESS_KEY is required}
MINIO_ROOT_PASSWORD: ${S3_SECRET_KEY:?S3_SECRET_KEY is required}
volumes:
- miniodata:/data
healthcheck:
test: ["CMD-SHELL", "wget -q -O /dev/null http://localhost:9000/minio/health/live || exit 1"]
interval: 5s
timeout: 5s
retries: 12
mem_limit: 512m
networks:
- app
# Портов нет: S3 и консоль видна только сеть app. Разовый проброс
# консоли для отладки:
# docker compose -p building-mgmt --env-file /opt/building-mgmt/.env \
# -f compose.prod.yaml run --rm -p 9443:9001 minio server /data --console-address :9001
api:
image: building-mgmt-api:latest
# build: — на случай ручной сборки без CI: если образа нет,
# compose соберёт его из этого же репо; готовый образ не трогает.
build:
context: .
restart: unless-stopped
depends_on:
db:
condition: service_healthy
minio:
condition: service_healthy
environment:
# Ссылки на сервисы внутри сети подставляются сами — в env-файле
# их держать не нужно (там только секреты и внешние настройки).
- DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}?sslmode=disable
- S3_ENDPOINT=http://minio:9000
# path-style обязателен: поддоменные имена внутри сети не резолвятся.
- S3_USE_PATH_STYLE=true
# Дальше — передача значений из env-файла (--env-file): не
# заданная переменная просто не попадёт в контейнер, и упадут
# дефолты приложения (тот же JWT_SECRET/S3 фейл-фаст на старте).
- S3_REGION
- S3_BUCKET
- S3_ACCESS_KEY
- S3_SECRET_KEY
- JWT_SECRET
- APP_ENV
- COOKIE_SECURE
- TRUSTED_PROXIES
- CORS_ORIGINS
- LOG_LEVEL
- LOG_FORMAT
- GIN_MODE
- SWAGGER
- APP_BASE_URL
- PHOTO_MAX_BYTES
- PHOTO_MAX_PER_TICKET
- TICKET_RETENTION_DAYS
- DB_MAX_CONNS
- DB_MIN_CONNS
- DB_MAX_CONN_LIFETIME
- DB_MAX_CONN_IDLE_TIME
- SMTP_HOST
- SMTP_PORT
- SMTP_USER
- SMTP_PASSWORD
- SMTP_FROM
- SMTP_TLS
- DATA_ENCRYPTION_KEY
- TELEGRAM_BOT_TOKEN
- TELEGRAM_API_URL
- TELEGRAM_NOTIFY_AFTER
- TELEGRAM_TICK_EVERY
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:8080/ping || exit 1"]
interval: 30s
timeout: 3s
retries: 5
# Датасерверу 15 секунд на завершение текущих запросов (graceful
# shutdown внутри api рассчитан на 15s), потом SIGKILL.
stop_grace_period: 20s
mem_limit: 512m
networks:
- app
# Портов нет: API доступен только web-контейнеру.
web:
# Образ собирается в репо веба: docker build -t building-mgmt-web:latest .
image: building-mgmt-web:latest
restart: unless-stopped
depends_on:
api:
condition: service_healthy
ports:
# Единственный порт наружу. TLS терминалирует перед этим nginx
# (балансировщик / Caddy / Traefik), HSTS веб отдаёт сам.
- "80:80"
mem_limit: 256m
networks:
- app
networks:
app:
driver: bridge
volumes:
pgdata:
miniodata: