161 lines
6.4 KiB
YAML
161 lines
6.4 KiB
YAML
# Прод-стек: db, minio, api и web в ОДНОЙ сети; наружу отдаёт только
|
||
# web (порт 80). Postgres и MinIO портов наружу не пробрасывают вовсе —
|
||
# они видны только контейнерам приложения.
|
||
#
|
||
# Образы api и web заранее собраны Drone (или вручную):
|
||
# 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) закреплено: по нему считается имя сети. Drone и
|
||
# ручной запуск обязаны давать одно и то же имя, иначе 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: — на случай ручной сборки без Drone: если образа нет,
|
||
# 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:
|