33 KiB
Руководство по развёртыванию «Контракт-чек»
Целевая аудитория: DevOps / разработчик, поднимающий стек на одном VPS или локально. Предполагается: Docker + Docker Compose установлены, есть SSH-доступ к VPS. Полная архитектура:
ARCHITECTURE.md.
Содержание
- Требования
- Быстрый старт локально
- Конфигурация через
.env - Docker Compose — полный стек
- Первичная инициализация
- Проверка работоспособности
- Telegram-бот: первый запуск
- B2B API: первый API-ключ
- Резервное копирование
- Обновление (zero-downtime-ish)
- Масштабирование
- Troubleshooting
- Edge proxy (Nginx + certbot)
- Telegram-бот на отдельном сервере
1. Требования
| Компонент | Минимум | Рекомендуемое |
|---|---|---|
| CPU | 2 vCPU | 4 vCPU (worker-extract CPU-bound) |
| RAM | 2 GB | 4 GB |
| Disk | 20 GB SSD | 40 GB SSD (MinIO + WAL-archive) |
| OS | Ubuntu 22.04+ / Debian 12+ | Ubuntu 24.04 LTS |
| Docker | 24.0+ | 25.0+ |
| Docker Compose | v2 | v2 |
| Интернет | нужен для Ollama Cloud | — |
Важно: Ollama Cloud инферит в США. Если 152-ФЗ data-residency критичен — рассмотрите self-hosted Ollama (GPU) или GigaChat-адаптер (
core/llm/factory.py).
2. Быстрый старт локально
2.1 Клонирование и env
git clone <repo> contract-check
cd contract-check
cp .env.example .env
# Отредактируйте .env — минимум: OLLAMA_HOST, OLLAMA_API_KEY, BOT_TOKEN,
# TELEGRAM_BOT_TOKEN, JWT_SECRET
2.2 Инфраструктура (только Postgres + Redis + RabbitMQ + MinIO)
docker compose up -d
# Ждём healthy:
docker compose ps
# Ожидаемый результат: postgres, redis, rabbitmq, minio, minio-init — Up (healthy)
Порты на хосте (смещены, чтобы не конфликтовать):
- Postgres:
15432 - Redis:
17379 - RabbitMQ AMQP:
5672, Management UI:http://localhost:15672 - MinIO S3 API:
9000, Console:http://localhost:9001
2.3 Миграции
# Локально (нужен uv + dev-группа):
uv sync --group dev
uv run alembic upgrade head
# Или через временный api-контейнер:
docker compose --profile services run --rm api alembic upgrade head
2.4 Полный стек (api + workers + bot)
docker compose --profile services up -d --build
# Ждём healthy:
docker compose --profile services ps
3. Конфигурация через .env
Все секреты — в .env (не коммитить!). Ключевые переменные:
# --- LLM (обязательно) ---
OLLAMA_HOST=https://api.ollama.com
OLLAMA_API_KEY=sk-xxxxxxxx
OLLAMA_MODEL=qwen2.5:14b
OLLAMA_FALLBACK_MODEL=qwen2.5:7b
# --- Telegram + auth (обязательно) ---
BOT_TOKEN=123456789:ABCDEF... # для aiogram бота
TELEGRAM_BOT_TOKEN=$BOT_TOKEN # тот же токен; API использует для проверки Login Widget / Mini App
BOT_SERVICE_TOKEN=bot-prod-secret-xxx # см. §5.3
JWT_SECRET=$(openssl rand -hex 32) # HS256 secret для подписи JWT
# --- WebUI auth + admin panel (опционально, defaults ниже) ---
WEB_AUTH_ENABLED=true # /api/v1/auth/register,login,...
WEB_APP_BASE_URL=http://localhost:5173 # SPA — ссылки для сброса пароля
WEB_ADMIN_ENABLED=true # панель управления по /admin
ADMIN_REQUIRED_ROLE=admin # users.role, которое допущено в /admin
PASSWORD_MIN_LENGTH=8
POSTGRES_USER=contract_check
POSTGRES_PASSWORD=changeme-strong-password
POSTGRES_DB=contract_check
# --- RabbitMQ ---
RABBITMQ_USER=contract_check
RABBITMQ_PASS=changeme-strong-password
# --- MinIO ---
S3_ACCESS_KEY=contract_check
S3_SECRET_KEY=changeme-strong-password
S3_BUCKET=contract-check-docs
# --- Billing ---
REFUND_POLICY=all # или infra_only
PLANS_ENABLED=false # квоты подписок (миграция 0011); false = только кредиты
YOOKASSA_ENABLED=false # выключено → checkout/refund отвечают 503
PRICE_PER_DOC_KOPECKS=19900 # 199 ₽ за документ без подписки
# Полный список биллинг-env: см. .env.example и ARCHITECTURE.md §11
# --- Observability (опционально) ---
SENTRY_DSN=https://...@sentry.io/...
# OTLP/HTTP endpoint. With profile `observer` use the local collector.
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318
OPENOBSERVE_AUTH_TOKEN=cm9vdEBleGFtcGxlLmNvbTpDb21wbGV4cGFzcyMxMjM=
Полный список: см.
.env.exampleиARCHITECTURE.md §11. Включение платежей (ЮKassa) — §4a ниже.
4a. ЮKassa: включение платежей
-
Получите
shopIdи секретный ключ в личном кабинете ЮKassa (Настройки → API-ключи; для старта можно «тестовый» магазин). -
Пропишите в
.env:YOOKASSA_ENABLED=true YOOKASSA_SHOP_ID=812345 YOOKASSA_SECRET_KEY=live_Ax... # или test_... YOOKASSA_RETURN_BASE_URL=https://<ваш-домен>/pay/{invoice_id} PLANS_ENABLED=true # включить квоты подписок BILLING_RETURN_JWT_SECRET=$(openssl rand -hex 32) # или пусто = JWT_SECRET -
В личном кабинете ЮKassa настройте HTTP-уведомления (webhook) на
https://<ваш-домен>/api/v1/webhooks/yookassaдля событийpayment.succeeded,payment.canceled,refund.succeeded. API аутентифицирует уведомления по Basic auth (shopId:secretKey) и всегда перезапрашивает платёж через REST — сумме из push не доверяем. -
docker compose --profile services up -d --build— изменения подхватятся без правок compose (12-factor). -
Поднимите
worker-billing(входит в профильservices): продления подписок, expiry/past_due, сверка «зависших» pending-счетов старше 15 мин.
Degraded-режим (YOOKASSA_ENABLED=false, дефолт): каталог тарифов
GET /api/v1/billing/plans читается, мутации checkout/refund отвечают 503,
webhook-и игнорируются с warning-логом. Отключение платежей не требует
отката миграций.
Тюнинг каталога: тарифы сидируются миграцией 0011 (free/lite/pro/max).
Правьте таблицу plans напрямую (цены — в копейках):
make shell-db # UPDATE plans SET price_kopecks = 59000 WHERE code = 'lite';
4. Docker Compose — полный стек
4.1 Профили
| Профиль | Что поднимает |
|---|---|
| (default) | postgres, redis, rabbitmq, minio, minio-init |
services |
api, worker-extract, worker-prescreen, worker-analyze, worker-billing, worker-notify, bot |
obs |
loki, promtail, grafana, prometheus (logs + metrics; traces — позже) |
observer |
openobserve, otel-collector (logs + metrics + traces через collector) |
edge |
nginx, certbot |
# Инфра:
docker compose up -d
# + сервисы (сборка + запуск):
docker compose --profile services up -d --build
# + observability Grafana/Loki/Prometheus (логи + метрики):
docker compose --profile services --profile obs up -d --build
# + observability OpenObserve (логи + метрики + трейсы через otel-collector):
docker compose --profile services --profile observer up -d --build
# + edge (nginx reverse proxy + TLS; deploy/nginx готов):
docker compose --profile services --profile observer --profile edge up -d --build
4.2 depends_on и healthchecks
api ждёт postgres, rabbitmq, minio-init (healthy / completed).
bot ждёт только api (healthy) — не инфра напрямую, соблюдая hexagonal boundary.
Workers ждут postgres, rabbitmq, minio-init.
5. Первичная инициализация
5.1 Миграции
# Способ A: локально (если uv установлен)
uv run alembic upgrade head
# Способ B: через api-контейнер (если uv недоступен на хосте)
docker compose --profile services run --rm api alembic upgrade head
Проверка:
docker compose exec postgres psql -U contract_check -d contract_check -c "\dt"
# Должны быть: api_key_requests, api_keys, documents, invoices, jobs, reports, service_tokens, users
5.2 Seed-token для адаптеров (bot / web / cli)
Бот аутентифицируется в api через Authorization: Bearer <BOT_SERVICE_TOKEN>.
Этот токен нужно положить в service_tokens.
# Генерируем секрет
BOT_SERVICE_TOKEN=$(openssl rand -hex 32)
echo "BOT_SERVICE_TOKEN=$BOT_SERVICE_TOKEN" >> .env
# Записываем hash в БД через временный api-контейнер
docker compose --profile services run --rm api \
python -c "
import asyncio, os, sys
os.chdir('/app')
sys.path.insert(0, 'src')
from contract_check.core.config import get_settings
from contract_check.core.tokens import hash_token
from contract_check.core.db.session import create_session_factory
from sqlalchemy import text
async def seed():
factory = create_session_factory()
async with factory() as s:
h = hash_token('$BOT_SERVICE_TOKEN')
await s.execute(text('INSERT INTO service_tokens (name, token_hash, adapter) VALUES (:n, :h, :a) ON CONFLICT (name) DO UPDATE SET token_hash = EXCLUDED.token_hash'), {'n': 'bot-prod', 'h': h, 'a': 'bot'})
await s.commit()
print('seeded bot-prod')
asyncio.run(seed())
"
В production используйте
python -m contract_check.api seed-tokenCLI (если реализовано).
5.3 Перезапуск бота с новым токеном
docker compose --profile services restart bot
# Проверка логов:
docker compose --profile services logs -f bot
6. Проверка работоспособности
6.1 Health endpoints
curl http://localhost:8000/healthz # liveness — 200
curl http://localhost:8000/readyz # readiness — 200 (PG + Rabbit + MinIO)
curl http://localhost:8000/metrics # Prometheus exposition
6.2 RabbitMQ Management UI
Откройте http://localhost:15672 (guest/guest или credentials из .env).
Проверьте:
- Exchange
contracts.x(direct) - Queues:
extract.q,analyze.q(quorum),extract.retry.q,analyze.retry.q, DLQ - Connections/consumers от worker-ов
6.3 MinIO Console
http://localhost:9001 (root user = S3_ACCESS_KEY / S3_SECRET_KEY).
Бакет contract-check-docs должен появиться после minio-init.
6.4 End-to-end smoke-тест (через curl)
# 1. Сначала нужен JWT пользователя. В dev можно обменять telegram_id через
# service-token endpoint /auth/telegram/bot (в production этим занимается бот):
curl -X POST -H "Authorization: Bearer $BOT_SERVICE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"telegram_id": 123456}' \
http://localhost:8000/api/v1/auth/telegram/bot
# → {"access_token":"eyJ...", "user_id":"...", "telegram_id":123456, ...}
USER_JWT="eyJ..." # скопируйте access_token
# 2. Проверить /me
curl -H "Authorization: Bearer $USER_JWT" http://localhost:8000/api/v1/me
# → {"telegram_id":123456, "credits_left":0}
# 3. Загрузить документ (нужен credit — пока 0, но проверим путь)
curl -X POST -H "Authorization: Bearer $USER_JWT" \
-F "file=@contract.pdf" http://localhost:8000/api/v1/documents
# → 402 Payment Required (нормально — нет кредитов)
# 4. Добавить кредит вручную (dev):
docker compose exec postgres psql -U contract_check -d contract_check \
-c "UPDATE users SET credits_left = 10 WHERE telegram_id = 123456;"
7. Telegram-бот: первый запуск
7.1 Создание бота в BotFather
- Напишите
@BotFather→/newbot - Скопируйте токен (
BOT_TOKEN) в.env - Установите webhook (опционально, polling работает по умолчанию):
curl -F "url=https://your-domain.com/webhook" \ https://api.telegram.org/bot$BOT_TOKEN/setWebhook
7.2 Запуск
docker compose --profile services up -d bot
docker compose --profile services logs -f bot
Ожидаемый вывод при /start:
Привет! Я «Контракт-чек» — первичный скрининг рисков в договорах...
Осталось проверок: 0.
7.3 Добавление кредитов пользователю
# Найти telegram_id пользователя (из логов бота или таблицы users)
docker compose exec postgres psql -U contract_check -d contract_check \
-c "UPDATE users SET credits_left = credits_left + 5 WHERE telegram_id = <tg_id>;"
8. B2B API: первый API-ключ
8.1 Создание ключа (через user JWT)
Управление B2B-ключами теперь требует пользовательский JWT. Получите JWT через
/auth/telegram/bot (как в §6.4) и выполните:
curl -X POST -H "Authorization: Bearer $USER_JWT" \
-H "Content-Type: application/json" \
-d '{"name": "integration-test", "rate_limit_rps": 3, "monthly_quota": 100}' \
http://localhost:8000/api/v1/b2b/keys
# → {"api_key": "cc_abc123...", "id": "...", ...}
# Сохраните api_key — он показывается ТОЛЬКО ОДИН РАЗ
8.2 Анализ документа через B2B
API_KEY="cc_abc123..."
curl -X POST -H "X-API-Key: $API_KEY" \
-F "file=@contract.pdf" \
http://localhost:8000/api/v1/analyze
# → 202 {"document_id": "...", "correlation_id": "..."}
8.3 Проверка usage
curl -H "X-API-Key: $API_KEY" http://localhost:8000/api/v1/b2b/usage
9. Резервное копирование
9.1 Postgres
# Ручной backup
docker compose exec postgres pg_dump -U contract_check -d contract_check \
> backup_$(date +%Y%m%d_%H%M%S).sql
# Автоматический backup (cron на хосте)
# 0 2 * * * cd /opt/contract-check && docker compose exec -T postgres pg_dump -U contract_check -d contract_check | gzip > backups/pg_$(date +\%Y\%m\%d).sql.gz
9.2 WAL-архивы (PITR-ready)
Postgres настроен с wal_level=replica, archive_mode=on.
WAL-сегменты пишутся в volume pgwal (mount /walarchive).
Для полноценного PITR настройте pg_backrest или репликацию (см. ARCHITECTURE.md §10).
9.3 MinIO
MinIO хранит raw-документы и extracted .txt. ILM-правило истекает объекты
через DOC_RETENTION_DAYS (по умолчанию 7 дней). Отчёты живут в Postgres — они
выживут после истечения raw-документов. Backup MinIO не обязателен для бизнес-логики.
10. Обновление
10.1 Rolling update (без остановки всего)
# 1. Pull изменений
git pull origin main
# 2. Rebuild + recreate (Compose пересоздаёт только изменённые контейнеры)
docker compose --profile services up -d --build
# 3. Миграции (если есть новые)
docker compose --profile services run --rm api alembic upgrade head
# 4. Проверка:
curl http://localhost:8000/readyz
10.2 Graceful shutdown workers
Workers получают SIGTERM → завершают текущее сообщение → SIGKILL после grace period.
Compose stop_grace_period по умолчанию 10s; для долгих контрактов можно увеличить.
11. Масштабирование
11.1 На одном VPS (вертикальное)
- Увеличьте
MQ_PREFETCH_EXTRACTдо числа CPU - Увеличьте
MQ_PREFETCH_ANALYZEдо конкарренси Ollama (3 на Pro, 10 на Max) - Масштабируйте RAM под размер контрактов
11.2 Горизонтальное (несколько worker-ов)
# docker-compose.override.yml
services:
worker-extract:
deploy:
replicas: 2
worker-analyze:
deploy:
replicas: 2
docker compose --profile services up -d --scale worker-extract=2 --scale worker-analyze=2
Workers stateless — горизонтальное масштабирование бесплатно. Единственное ограничение: конкарренси Ollama Cloud (3 на Pro). Не масштабируйте
worker-analyzeвыше, чем позволяет квота провайдера — иначе получите 429.
11.3 Многонодовая HA
См. ARCHITECTURE.md §10. Путь к HA — только инфраструктурный (Compose → кластер
RabbitMQ 3 nodes + Patroni Postgres + distributed MinIO). Код core/ не меняется.
12. Troubleshooting
12.1 docker compose up зависает — сервисы не стартуют
Причина: depends_on: condition: service_healthy — кто-то не прошёл healthcheck.
# Диагностика:
docker compose ps
docker compose logs <service>
# Частые причины:
# - Postgres ещё не готов: подождите 15-20s после первого запуска
# - minio-init не выполнился: проверьте docker compose logs minio-init
# - RabbitMQ не отвечает: docker compose logs rabbitmq
12.2 POST /documents → 402 Payment Required
Пользователю не хватает credits_left. В dev добавьте вручную:
docker compose exec postgres psql -U contract_check -d contract_check \
-c "UPDATE users SET credits_left = credits_left + 1 WHERE telegram_id = <id>;"
12.3 Worker-extract падает с ExtractionError
Документ — скан/PDF без текстового слоя. Worker должен автоматически перейти к OCR.
Если и OCR падает — documents.status=failed, credit refunded (если refundable).
Проверьте:
docker compose --profile services logs worker-extract
# Или в Postgres:
docker compose exec postgres psql -U contract_check -d contract_check \
-c "SELECT id, status, stage, last_failure_class FROM documents ORDER BY created_at DESC LIMIT 5;"
12.4 Worker-analyze: постоянные 429 / llm_quota
- Превышена квота Ollama Cloud. Проверьте usage в dashboard ollama.com.
- Уменьшите
MQ_PREFETCH_ANALYZEили перейдите на Enterprise тариф. - Проверьте fallback-model (
OLLAMA_FALLBACK_MODEL) — он должен сработать на 429.
12.5 Bot: Unauthorized / 401
BOT_SERVICE_TOKENне совпадает сservice_tokens.token_hashв БД.- Бот не смог получить user JWT через
/api/v1/auth/telegram/bot(проверьте логи бота и api). TELEGRAM_BOT_TOKEN/JWT_SECRETне заданы в.envдля сервисаapi.- Пересоздайте токен через §5.2.
12.6 Бот не отвечает
# Проверка логов
docker compose --profile services logs -f bot
# Проверка polling:
# Бот использует polling по умолчанию (aiogram). Если webhook установлен —
# убедитесь, что Nginx проксирует /webhook к api:8000.
13. Edge proxy (Nginx + certbot)
13.1 Профиль edge
Профиль поднимает Nginx (443/80) и certbot-renewal sidecar.
# .env
NGINX_SERVER_NAME=contract-check.example.com
Важно: nginx зависит от
api(service_healthy), аapiнаходится в профилеservices— поэтому nginx/certbot всегда запускайте с обоими профилями:docker compose --profile services --profile edge .... Запуск только--profile edgeпадает сservice "nginx" depends on undefined service "api".
# Запуск edge вместе со стеком (после получения первого сертификата, см. §13.2)
docker compose --profile services --profile edge up -d
13.2 Первый запуск и получение сертификата
DNS A-запись должна указывать на IP сервера до запуска certbot.
NGINX_SERVER_NAME в .env должен совпадать с доменом.
Nginx не может стартовать без сертификата — рендеренный конфиг ссылается на
/etc/letsencrypt/live/<domain>/fullchain.pem, и nginx падает на старте, если
файла нет. Поэтому первый сертификат получается в --standalone режиме
(certbot сам слушает порт 80, nginx в этот момент не запущен), и только затем
nginx стартует. Всё это делает certbot-init.sh:
# 1. Запускаем основной стек (nginx пока НЕ стартуем)
docker compose --profile services up -d
# 2. Получаем первый сертификат (standalone, порт 80) и стартуем nginx + renew-sidecar
chmod +x deploy/nginx/certbot-init.sh
./deploy/nginx/certbot-init.sh contract-check.example.com admin@example.com
# 3. Проверяем
curl https://contract-check.example.com/healthz
При повторном запуске скрипт делает renew через webroot (nginx уже отдаёт
челленджи) и nginx -s reload.
По умолчанию nginx.conf проксирует:
/api/v1/*,/admin/*/healthz,/readyz/metrics(открыт наружу; закройте файрволом или уберите приватный скрейпинг)/webhook/*→/api/v1/webhooks//grafana/*→grafana:3000(когда поднят профильobs)
Grafana под путём /grafana:
# Открыть логи через основной домен (нужен профиль obs + edge)
docker compose --profile services --profile obs --profile edge up -d
# https://contract-check.example.com/grafana/d/contract-check-logs
Безопасность: Grafana под
/grafanaдоступна всем, у кого есть доступ к домену. На проде добавьте basic auth, IP whitelist или вынесите Grafana на отдельный поддомен с отдельным ingress/VPN.
13.3 Обновление сертификата
Certbot-контейнер уже запущен с cron-подобным циклом renew каждые 12 часов.
Проверка вручную:
docker compose --profile edge exec certbot certbot renew --dry-run
docker compose --profile edge exec nginx nginx -s reload
13.4 MinIO: файлы не видны / bucket не создан
# Ручной init (если minio-init не отработал)
docker compose run --rm minio-init
# Или проверьте через mc:
docker compose run --rm minio-init mc ls local/
13.5 Миграции: alembic не видит таблицы / revision conflict
# Проверка текущей HEAD:
docker compose --profile services run --rm api alembic current
# Принудительный upgrade (осторожно — только dev!):
docker compose --profile services run --rm api alembic stamp head
13.6 Полный сброс (dev only!)
# Удалить ВСЕ данные (тома + контейнеры):
docker compose --profile services down -v
docker compose down -v
# Затем пересоздать с нуля: §2 + §5
14. Telegram-бот на отдельном сервере
Бот изначально спроектирован как тонкий HTTP-адаптер: он не импортирует core.db/core.s3/core.mq/core.llm и не хранит состояние. Поэтому его можно вынести на отдельный хост без изменения кода — только настроив API_URL на центральный API.
Типичные причины вынести бота:
- Безопасность: на ботовом сервере нет секретов БД/MinIO/RabbitMQ/LLM.
- Сетевая изоляция: боту нужен только outbound HTTPS к Telegram и к API.
- Масштабирование: можно запустить несколько ботов за одним токеном (aiogram polling не допускает несколько активных polling-инстансов с одним токеном; для HA используйте webhook + балансировку, см. §14.4).
14.1 Сетевая связность
| Направление | Протокол | Порт | Комментарий |
|---|---|---|---|
| bot → Telegram | outbound HTTPS | 443 | Polling updates от api.telegram.org |
| bot → API | HTTPS (рекомендуется) | 443 / 8000 | API_URL без trailing slash, например https://contract-check.example.com |
| bot → Redis (опц.) | Redis | 6379 | Только внутри приватной сети/VPN; иначе оставьте REDIS_URL пустым |
На firewall ботового сервера открывайте только outbound 443 (и 22 для SSH). Входящих портов бот не требует.
14.2 Подготовка центрального API
Убедитесь, что API доступен ботовому серверу по домену/IP и что service_tokens содержит запись для бота (§5.2):
# На сервере с API — убедитесь, что токен засеян:
docker compose --profile services exec api python -m src.contract_check.api seed-token bot-prod bot
Если API закрыт файрволом по IP, разрешите входящие от IP ботового сервера на порт API/Nginx.
14.3 Развёртывание бота на отдельном сервере
-
Склонируйте репозиторий на ботовый сервер (нужны только
srv/bot/Dockerfile,docker-compose.bot.yml,pyproject.toml,uv.lock,src/,.env.bot.example). -
Создайте
.envиз.env.bot.exampleс минимальным набором переменных:cp .env.bot.example .env # отредактируйте .envENV=prod LOG_LEVEL=INFO LOG_FORMAT=json BOT_TOKEN=123456789:ABCDEF... # тот же токен из @BotFather BOT_SERVICE_TOKEN=bot-prod-secret-xxx # должен совпадать с service_tokens.name='bot-prod' API_URL=https://contract-check.example.com # публичный адрес центрального API, без trailing slash REDIS_URL= # пусто → in-memory rate limiter; достаточно для одного инстанса -
Запустите:
make bot-remote-up # или напрямую: docker compose -f docker-compose.bot.yml up -d --build -
Проверьте логи:
make bot-remote-logsОжидаемое сообщение:
bot_started username=<...> api_url=https://contract-check.example.com -
Проверьте healthcheck контейнера:
docker compose -f docker-compose.bot.yml ps docker compose -f docker-compose.bot.yml exec bot python -c \ "import urllib.request, os; print(urllib.request.urlopen(os.environ['API_URL'] + '/healthz').read())"
14.4 Webhook вместо polling (опционально, для HA)
По умолчанию бот использует polling. Два и более инстанса с одним токеном в polling-режиме будут конфликтовать (Telegram отдаёт updates только одному соединению). Для высокой доступности:
- Переведите бота на webhook: укажите публичный URL, за который отвечает Nginx, и проксируйте запросы на ботовый сервер.
- В боте реализуйте webhook-эндпоинт (не входит в текущую версию; требуется небольшая доработка
bot/__main__.pyи добавление HTTP-сервера, например aiohttp/uvicorn). - Используйте
docker-compose.bot.ymlс healthcheck иdeploy.replicas, но за балансировщиком, который направляет Telegram-запросы на одну активную реплику (либо на все, если webhook-эндпоинт идемпотентен и дедуплицирует updates поupdate_id).
Для большинства production-развёртываний polling на выделенном ботовом сервере достаточен.
14.5 Управление и обновление
# Пересобрать и перезапустить бота после git pull:
make bot-remote-up
# Остановить:
make bot-remote-down
# Посмотреть статус:
make bot-remote-ps
Чек-лист перед production
.envзаполнен,.env.exampleне содержит реальных секретовPOSTGRES_PASSWORD,RABBITMQ_PASS,S3_SECRET_KEY— strong random.envзаполнен (включаяTELEGRAM_BOT_TOKEN,JWT_SECRET)BOT_SERVICE_TOKENзасеян вservice_tokens- Миграции накатаны (
alembic upgrade head) docker compose --profile services psпоказывает все healthy/healthzи/readyzотвечают 200- Telegram-бот отвечает на
/start - Тестовый PDF проходит pipeline: upload → extract → analyze → report
- B2B-ключ создаётся через
/api/v1/b2b/keysс user JWT - Backup cron настроен
- Firewall: открыты только 443 (nginx), 22 (ssh), 15672 (RabbitMQ mgmt, restrict IP)
- Если бот на отдельном сервере:
API_URLуказывает на центральный API,BOT_SERVICE_TOKENзасеян, ботовый сервер имеет outbound HTTPS
Ссылки
- ARCHITECTURE.md — полная архитектура, схемы БД, RabbitMQ topology, конфиг
- TICKETS.md — текущие статусы задач
- README.md — структура, quick start, API overview