Files
robotex/docker-compose.yml
T
s0k0landClaude Sonnet 5 0a4da80ca9 feat(docker): многоступенчатая сборка с Nuitka и офлайн-режим
api и worker компилируются Nuitka (--mode=standalone) в builder-стадии; в финальном
образе — только скомпилированный бинарник и системные библиотеки, без исходников,
pip и build-инструментов. api/runtime — debian:bookworm-slim без Python вообще;
worker/runtime — python:3.12-slim-bookworm, но Python там нужен только на шаг
установки Chrome. db.cli объединён с сервером в один бинарник (entry_api.bin dbcli ...).

Заодно убрано 51 неиспользуемый пакет из requirements.txt (camoufox, PySide6, moz*-
стек и др., подтверждено графом зависимостей) — он давал больше веса, чем сама
компиляция. make build offline=1 ставит python-зависимости из wheels/ без PyPI.

make test-browser адаптирован под новую сборку: pytest не может подключиться к
internals скомпилированного бинарника, поэтому гоняется против отдельной стадии
test (Chrome из runtime + исходники как обычный интерпретируемый Python).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-11-06 13:52:28 +03:00

112 lines
3.6 KiB
YAML

# Локальный запуск и тесты: docker compose up -d --build
# Шаблон для релизов — docker-compose.prod.yml (make release).
x-backend-env: &backend-env
RABBITMQ_URL: amqp://robotex:${RABBITMQ_PASSWORD:-robotex}@rabbitmq:5672/
REDIS_URL: redis://redis:6379/0
DATABASE_URL: postgresql+asyncpg://robotex:${POSTGRES_PASSWORD:-robotex}@postgres:5432/robotex
services:
api:
build:
context: .
dockerfile: Dockerfile.api
args:
OFFLINE: ${OFFLINE:-0}
image: robotex-api:latest
restart: unless-stopped
ports:
- "${API_PORT:-8000}:8000"
env_file:
- path: .env
required: false
environment: *backend-env
depends_on:
rabbitmq: {condition: service_healthy}
redis: {condition: service_healthy}
postgres: {condition: service_healthy}
worker:
build:
context: .
target: runtime
args:
OFFLINE: ${OFFLINE:-0}
image: robotex-worker:latest
restart: unless-stopped
env_file:
- path: .env
required: false
environment: *backend-env
volumes:
- extra:/app/extra
# Chrome активно использует /dev/shm, дефолтных 64MB не хватает
shm_size: "2gb"
# Потолок ресурсов: при переборе OOM-killer прибьёт процесс Chrome внутри контейнера,
# а не соседние сервисы на хосте. Подбирать под сервер вместе с BROWSER_SLOTS.
cpus: ${WORKER_CPUS:-4}
mem_limit: ${WORKER_MEM_LIMIT:-8g}
memswap_limit: ${WORKER_MEM_LIMIT:-8g}
# при остановке воркер дожидается текущих задач и закрывает окна
stop_grace_period: 60s
depends_on:
rabbitmq: {condition: service_healthy}
redis: {condition: service_healthy}
postgres: {condition: service_healthy}
rabbitmq:
image: rabbitmq:4.1-alpine
restart: unless-stopped
environment:
RABBITMQ_DEFAULT_USER: robotex
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASSWORD:-robotex}
# задача выполняется до 3 ч, а по умолчанию RabbitMQ рвёт канал, если сообщение не подтверждено 30 мин
RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS: -rabbit consumer_timeout 14400000
volumes:
- rabbitmq:/var/lib/rabbitmq
healthcheck:
test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"]
interval: 10s
timeout: 10s
retries: 10
# клиенты и API-ключи, журнал окон и запросов
postgres:
image: postgres:17-alpine
restart: unless-stopped
environment:
POSTGRES_USER: robotex
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-robotex}
POSTGRES_DB: robotex
volumes:
- postgres:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready", "-U", "robotex", "-d", "robotex"]
interval: 5s
timeout: 5s
retries: 10
redis:
image: redis:8.2-alpine
restart: unless-stopped
volumes:
- redis:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 5s
retries: 10
# HTTP-прокси для локальных тестов (exit-IP = IP хоста): docker compose --profile loadtest up -d
# из контейнера worker доступен как http://proxy:8080
proxy:
image: gogost/gost:3.1.0
profiles: ["loadtest"]
restart: unless-stopped
command: ["-L", "http://:8080"]
volumes:
extra:
rabbitmq:
redis:
postgres: