# Прод-стек: 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: