Files
robotex/docs/Deployment.md
T
protokeyandClaude Opus 5.5 a319b81d8b build: лимиты ресурсов, прокси и нагрузочный тест; docs: API
- docker-compose: cpus/mem_limit через API_CPUS/API_MEM_LIMIT, прокси gost в профиле loadtest
- tests/loadtest.py: параллельные запросы к /v1/solve + пики docker stats
- docs/API.md: справочник по запросу, действиям, ответу и сессиям
- docs/Deployment.md: результаты замеров и правило подбора BROWSER_SLOTS

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 13:55:36 +04:00

5.0 KiB
Raw Blame History

Развёртывание в Docker

Быстрый старт

docker compose up -d --build
docker compose logs -f api

API доступно на http://localhost:8000 (POST /v1/solve).

Без compose:

docker build -t robotex-api .
docker run -d --name robotex -p 8000:8000 --shm-size=2g -v robotex-extra:/app/extra robotex-api

Что внутри образа

Компонент Зачем
python:3.12-slim-bookworm базовый образ, Python той же версии, что и в .venv
requirements.txt ставится из PyPI (папка wheels/ в образ не попадает — она под Ubuntu Noble и неполная)
Google Chrome stable (patchright install --with-deps chrome) сессия запускается с real_chrome=True → channel chrome
Xvfb (xvfb-run) браузер работает в headful-режиме (headless=False), нужен виртуальный дисплей 1920x1080x24
tini (ENTRYPOINT, PID 1) пробрасывает сигналы и подбирает осиротевшие процессы Chrome/Xvfb — без него копятся зомби-процессы
пользователь app (uid 1000) Chrome не запускается от root без --no-sandbox

Рабочая директория — /app/src, uvicorn запускается как uvicorn main:app (импорты в проекте вида from api import ...).

Данные и volume

/app/extra вынесен в volume extra:

  • extra/user_data_dir/<session_id> — профили браузера (cookies, local storage, fingerprint) по session_id;
  • extra/geoip_cache.json — кэш геолокации прокси.

Профили со временем накапливаются — чистить вручную:

docker compose exec api sh -c 'rm -rf /app/extra/user_data_dir/*'

Настройки compose

  • shm_size: 2gb — Chrome активно использует /dev/shm, дефолтных 64 МБ не хватает (вкладки падают с «Aw, Snap»).
  • API_PORT — порт на хосте (по умолчанию 8000): API_PORT=8001 docker compose up -d.
  • API_CPUS / API_MEM_LIMIT — потолок ресурсов контейнера (по умолчанию 4 CPU / 8 ГБ, swap выключен). При переборе памяти OOM-killer убивает процесс Chrome внутри контейнера, хост не страдает.
  • restart: unless-stopped.
  • .env подключается, если существует (шаблон — env.example).

Нагрузка

Замер: freelancehunt.com/projects за Cloudflare Turnstile, captcha_type: cloudflare, ожидание списка проектов, каждый запрос — новый профиль. Прокси — gost из профиля loadtest. Хост: 16 ядер, 31 ГБ.

Лимит Параллельно Успешно Медиана, с Пик CPU Пик RAM
4 CPU 1 1/1 5 3,5 ядра 0,5 ГБ
4 CPU 5 3/5 68 4,1 2,8 ГБ
4 CPU 10 4/10 229 4,2 6,0 ГБ
4 CPU 15 3/15 261 4,2 6,1 ГБ
12 CPU 3 3/3 8 7,2 1,4 ГБ
12 CPU 5 4/5 8 12,1 2,4 ГБ
12 CPU 10 9/10 23 12,3 4,7 ГБ

Выводы:

  • Узкое место — CPU. Один Chrome на прохождении Turnstile съедает 2–3,5 ядра. Когда процессора не хватает, время растёт в десятки раз, а Cloudflare перестаёт пропускать (на 4 CPU и 10 параллельных — 6 из 10 застряли на челлендже).
  • Память — около 0,5–0,6 ГБ на браузер.
  • Правило подбора: BROWSER_SLOTS ≈ число ядер, API_MEM_LIMIT ≈ 0,6 ГБ × BROWSER_SLOTS + 1 ГБ. Пример: сервер 4 ядра / 8 ГБ → 3–4 слота; сейчас BROWSER_SLOTS = 10 зашит в src/main.py и рассчитан на ~10–12 ядер.
  • Сверх BROWSER_SLOTS запросы сразу получают No slots available, не ставятся в очередь.

Повторить замер:

API_PORT=8001 docker compose --profile loadtest up -d
python tests/loadtest.py --api http://127.0.0.1:8001 --levels 1 5 10

Параметры скрипта: --url, --selector, --captcha-type, --proxy, --levels. Каждый запрос создаёт новый профиль в extra/user_data_dir — после прогона их стоит почистить.

Reverse proxy

В проде контейнер ставится за Caddy (TLS). Пример Caddyfile:

api.example.com {
	reverse_proxy localhost:8000
}

Таймауты прокси должны быть не меньше timeout запроса (по умолчанию 120 с).