446 lines
32 KiB
Markdown
446 lines
32 KiB
Markdown
# arch-03. Docker Compose blueprint
|
||
|
||
> Термины (имена бакетов S3, идентификаторы) — в [`arch-00-glossary.md`](arch-00-glossary.md). Контракт Message Safety Service — в [`arch-02-api-contracts.md`](arch-02-api-contracts.md), раздел «api-backend ↔ message-safety». Переменные окружения и настройки — в [`arch-04-settings-and-content.md`](arch-04-settings-and-content.md).
|
||
|
||
## Назначение
|
||
|
||
Этот документ описывает целевой Docker Compose контур для первой production-like среды. Он не заменяет будущий `docker-compose.yml`, но задает требования, которым он должен соответствовать.
|
||
|
||
Требования к безопасности на уровне приложения и данных — в [`arch-01-system-architecture.md`](arch-01-system-architecture.md), раздел **«Принципы безопасности»**. Настоящий документ описывает только инфраструктурную реализацию этих принципов в compose/nginx: TLS, маршрутизация, сетевые границы, rate limits на edge. Значения переменных окружения — в [`arch-04-settings-and-content.md`](arch-04-settings-and-content.md). Дублировать прикладные требования (JWT, валидация, CORS в API, PII в логах и т.п.) здесь не нужно — они остаются в `arch-01`.
|
||
|
||
## Единый compose-контур (обязательно)
|
||
|
||
Это зафиксированное архитектурное требование, а не рекомендация.
|
||
|
||
### Принцип единого входа
|
||
|
||
- Весь backend-контур поднимается **одной командой** `docker compose up -d` из корня репозитория (`backend/`).
|
||
- Корневой `docker-compose.yml` — единственный источник правды для production-like среды. Отдельных compose-файлов для production-деплоя отдельных сервисов не должно быть.
|
||
- **Один `nginx`** поднимается из корневого `docker-compose.yml` и является единой публичной точкой входа с маршрутизацией на все сервисы:
|
||
- `/api/*`, `/realtime/*` → `api-backend`;
|
||
- `/auth/*` → `keycloak`;
|
||
- `/bitrix/*` (public: `/bitrix/handler`, `/bitrix/install`, `/bitrix/placement`, `/health/*` для `bitrix-local-app`) → `bitrix-local-app`;
|
||
- `/bitrix/sync/*` (public: webhook CRM sync для `bitrix-sync`) → `bitrix-sync`;
|
||
- web-сборка frontend или прокси на dev-сервер;
|
||
- `/internal/openlines/*`, `/internal/safety/*`, `/internal/sync/*` **не публикуются** наружу — доступны только из внутренней Docker-сети.
|
||
- Никакой другой `nginx` (ни в контейнере сервиса, ни на хосте) не терминирует внешний HTTPS для backend-контура. Site-конфиг `tohin.ru` на хосте, если используется, должен проксировать весь трафик на корневой `nginx` контейнера, а не на порты отдельных сервисов напрямую.
|
||
|
||
### Структура compose через `include`
|
||
|
||
Каждый сервис описывается в собственном `docker-compose.yml` внутри папки сервиса и подключается в корневой файл директивой `include`:
|
||
|
||
```text
|
||
backend/
|
||
docker-compose.yml # корневой: nginx + include сервисов + общие networks/volumes
|
||
.env
|
||
nginx/
|
||
docker-compose.yml # описание сервиса nginx (или секция в корневом)
|
||
nginx.conf
|
||
conf.d/
|
||
certs/
|
||
.gitkeep
|
||
api-backend/
|
||
docker-compose.yml # описание сервиса api-backend
|
||
message-safety/
|
||
docker-compose.yml # описание сервиса message-safety
|
||
bitrix-sync/
|
||
docker-compose.yml # описание сервиса bitrix-sync
|
||
bitrix-local-app/
|
||
docker-compose.yml # описание сервиса bitrix-local-app
|
||
keycloak/
|
||
docker-compose.yml # описание сервиса keycloak (или секция в корневом)
|
||
observability/
|
||
docker-compose.yml # otel-collector и т.п.
|
||
```
|
||
|
||
Корневой `backend/docker-compose.yml` (принципиальная схема):
|
||
|
||
```yaml
|
||
name: han-chat
|
||
|
||
include:
|
||
- nginx/docker-compose.yml
|
||
- api-backend/docker-compose.yml
|
||
- message-safety/docker-compose.yml
|
||
- bitrix-sync/docker-compose.yml
|
||
- bitrix-local-app/docker-compose.yml
|
||
- keycloak/docker-compose.yml
|
||
- redis/docker-compose.yml
|
||
- observability/docker-compose.yml
|
||
|
||
networks:
|
||
public:
|
||
backend:
|
||
observability:
|
||
|
||
volumes:
|
||
redis-data:
|
||
nginx-certs:
|
||
```
|
||
|
||
### Правила для сервисных compose-файлов
|
||
|
||
- Сервисный `docker-compose.yml` описывает **только** сервис(ы) своего модуля: образ, build context, `environment` (через `${VAR}` из корневого `.env`), порты (только внутренние, кроме случаев ниже), `depends_on`, healthcheck, подключение к сетям `public`/`backend`/`observability` (объявленным в корневом файле).
|
||
- Сервисный файл **не объявляет** сети и volumes верхнего уровня — они объявляются в корневом `docker-compose.yml`. Сервис только ссылается на них через `networks:` / `volumes:` (external-стиль не нужен, т.к. `include` объединяет файлы в один проект).
|
||
- Публикация портов наружу (`ports:`) разрешена **только** для `nginx` (80/443). Все остальные сервисы используют `expose:` для внутренних портов и общаются через Docker-сети.
|
||
- `bitrix-local-app` не публикует `8080` на хост (даже на `127.0.0.1`) — он доступен `api-backend` и `nginx` через сеть `backend`/`public`. Ранее применявшийся `127.0.0.1:8080:8080` считаем устаревшим; проверки через curl на `127.0.0.1:8080` заменяются на `docker compose exec bitrix-local-app` или прокси через `nginx`.
|
||
- Каждый сервисный compose-файл должен запускаться и в составе корневого контура, и автономно (`docker compose -f bitrix-local-app/docker-compose.yml up`) для локальной разработки сервиса — при условии, что переменные окружения заданы. Для автономного запуска сервис может объявлять заглушки сетей/volumes, но в составе корневого контура они переопределяются общими.
|
||
|
||
### Команды разработки
|
||
|
||
```text
|
||
docker compose up -d
|
||
docker compose logs -f nginx
|
||
docker compose logs -f api-backend
|
||
docker compose logs -f message-safety
|
||
docker compose logs -f bitrix-sync
|
||
docker compose logs -f bitrix-local-app
|
||
docker compose exec api-backend alembic upgrade head
|
||
docker compose exec api-backend pytest
|
||
docker compose exec api-backend ruff check .
|
||
docker compose exec api-backend ruff format .
|
||
```
|
||
|
||
## Сервисы
|
||
|
||
### nginx
|
||
|
||
Reverse proxy и единственная публичная точка входа в Docker Compose контур.
|
||
|
||
Требования:
|
||
|
||
- публикует наружу только `80` и `443` (см. политику HTTP ниже);
|
||
- принимает внешний HTTPS-трафик;
|
||
- выполняет TLS termination на reverse proxy; внутренний HTTP между контейнерами — только в закрытой Docker-сети `backend`;
|
||
- **политика HTTP/HTTPS по доменам** (каноническое правило — [`arch-01-system-architecture.md`](arch-01-system-architecture.md), «Принципы безопасности»):
|
||
- **веб-домен** (frontend, SPA, статика): `listen 80` допускается **только** для безусловного редиректа `301`/`308` на HTTPS; обработка бизнес-логики по HTTP запрещена;
|
||
- **API-домен** (если выделен отдельный host, напр. `api.example.ru`): **не** слушает порт `80`; только `listen 443 ssl`; HTTP-запросы к API-домену недоступны;
|
||
- **единый домен MVP** (напр. `tohin.ru` с путями `/api/*`, `/auth/*`, web): считается веб-доменом; порт `80` — только redirect на HTTPS для всего server block; после редиректа весь пользовательский трафик — HTTPS;
|
||
- **auth** на том же host, что API (`/auth/*`): следует политике host (redirect-only на :80 или HTTPS-only для выделенного API-host);
|
||
- **Bitrix callbacks** (`/bitrix/*`, `/bitrix/sync/*`): только HTTPS; порт `80` не обслуживает эти location — только redirect;
|
||
- маршрутизирует `/api/*` и `/realtime/*` в `api-backend`;
|
||
- маршрутизирует `/auth/*` в `keycloak` или проксирует отдельный auth-домен;
|
||
- маршрутизирует публичные `/bitrix/*` endpoint в `bitrix-local-app`;
|
||
- маршрутизирует `/bitrix/sync/*` webhook endpoint в `bitrix-sync`;
|
||
- закрывает `/internal/*` (в т.ч. `bitrix-local-app`, `message-safety`, `bitrix-sync` ops) от публичного доступа — только private network Docker/VPC;
|
||
- **не публикует** `message-safety` наружу;
|
||
- **production-like / production**: отдаёт **статическую сборку Expo web** из volume или каталога (`/usr/share/nginx/html` или аналог); `index.html` + assets, SPA fallback `try_files $uri /index.html`;
|
||
- **local dev** (опционально): при `FRONTEND_DEV_PROXY_ENABLED=true` проксирует `/` на Expo dev server (`EXPO_DEV_SERVER_URL`, напр. `http://host.docker.internal:8081`);
|
||
- передает upstream-сервисам `Host`, `X-Real-IP`, `X-Forwarded-For`, `X-Forwarded-Proto`, `X-Forwarded-Host`, `X-Request-ID`;
|
||
- задает разумные `proxy_connect_timeout`, `proxy_read_timeout`, `client_max_body_size`;
|
||
- применяет edge rate limits для auth, API и download endpoints;
|
||
- ограничивает частоту соединений и размер тела запроса;
|
||
- разрешает только TLS 1.2/1.3 и запрещает слабые шифры;
|
||
- добавляет HSTS и базовые security headers;
|
||
- скрывает заголовки, раскрывающие внутренние технологии;
|
||
- кэширует публичные endpoint настроек и контента;
|
||
- не проксирует наружу managed PostgreSQL, `redis`, `otel-collector` (БД вне compose, в VPC);
|
||
|
||
### api-backend
|
||
|
||
Python FastAPI backend.
|
||
|
||
Требования:
|
||
|
||
- запускается после доступности managed PostgreSQL, `keycloak`, `redis`;
|
||
- применяет настройки из `.env`;
|
||
- отдает `/health/live` и `/health/ready`;
|
||
- корректно работает за reverse proxy и доверяет proxy headers только от `nginx`;
|
||
- применяет API-level rate limits с состоянием в Redis;
|
||
- вызывает message safety pipeline для сообщений до отправки в Open Lines;
|
||
- вызывает `bitrix-local-app` для отправки сообщений в Open Lines;
|
||
- принимает forward нормализованных событий оператора от `bitrix-local-app`;
|
||
- поддерживает realtime endpoint для сообщений оператора;
|
||
- работает с Selectel S3 для файлов и документов;
|
||
- экспортирует traces/logs в `otel-collector`;
|
||
- не хранит состояние внутри контейнера.
|
||
|
||
### message-safety
|
||
|
||
Отдельный backend-сервис проверки входящих сообщений пользователя. HTTP-контракт — в [`arch-02-api-contracts.md`](arch-02-api-contracts.md), раздел «api-backend ↔ message-safety».
|
||
|
||
Требования:
|
||
|
||
- запускается после доступности managed PostgreSQL (схема `message_safety`), `redis`;
|
||
- **не публикуется** через `nginx` — доступен только из внутренней Docker-сети;
|
||
- отдаёт `/health/live` и `/health/ready` (ready проверяет PostgreSQL, Redis, workers, read-доступ к S3-quarantine);
|
||
- exposing endpoints: `POST /internal/safety/v1/messages/check`, `GET /internal/safety/v1/messages/tasks/{task_id}` (internal Docker network + `X-Service-Token` / `MESSAGE_SAFETY_SERVICE_TOKEN`);
|
||
- read-only доступ к S3-quarantine (отдельный access key без прав записи);
|
||
- использует отдельную схему `message_safety` в managed PostgreSQL и отдельный DB-user;
|
||
- использует Redis (отдельная DB, напр. `redis://redis:6379/2`) для verdict cache и rate limits;
|
||
- запускает async workers для file scan из S3-quarantine;
|
||
- экспортирует traces/logs в `otel-collector`;
|
||
- таймауты: POST check 5 s, GET task 2 s, file scan 60 s (см. [`arch-04-settings-and-content.md`](arch-04-settings-and-content.md), переменные `MESSAGE_SAFETY_*`).
|
||
|
||
### bitrix-sync
|
||
|
||
Python worker/service **двусторонней** синхронизации App DB ↔ Bitrix24 CRM.
|
||
|
||
Требования:
|
||
|
||
- запускается после готовности managed PostgreSQL, `redis`;
|
||
- читает задачи из `han_app.sync_queue` (заполняется триггерами App DB);
|
||
- имеет прямой доступ к `han_app` (`BITRIX_SYNC_APP_DATABASE_URL`) и схеме `bitrix_sync`;
|
||
- выполняет map/create Contact по телефону (интервал `BITRIX_SYNC_CONTACT_MAP_INTERVAL_SEC`, default 60);
|
||
- push обновлений Contact (интервал `BITRIX_SYNC_CONTACT_UPDATE_INTERVAL_SEC`, default 30);
|
||
- принимает webhook `POST /bitrix/sync/webhook/contact` от роботов Bitrix24;
|
||
- при записи в App DB от Bitrix использует GUC `han.sync_suppress=true`;
|
||
- поддерживает graceful shutdown и rate limiting Bitrix REST;
|
||
- не блокирует пользовательский API при ошибках Битрикс24;
|
||
- не участвует в OTP-flow, не создаёт `UserIdentity`/`ClientProfile`;
|
||
- **не участвует** в hot path чата Open Lines.
|
||
- `bitrix-sync` должен быть подключаем через .env (если отключили, то синхронизация с битрикс24 не проводится; если не отключили - проводится)
|
||
|
||
### bitrix-local-app
|
||
|
||
Локальное приложение Bitrix24 и custom connector `han_mobile_app`.
|
||
|
||
Требования:
|
||
|
||
- публикует наружу только `/bitrix/handler`, `/bitrix/install`, `/bitrix/placement`, `/health/live`, `/health/ready`;
|
||
- принимает `ONAPPINSTALL` и `ONIMCONNECTOR*` события от Bitrix24;
|
||
- регистрирует и активирует connector `han_mobile_app` для открытой линии 8;
|
||
- хранит OAuth-токены Bitrix24, inbox событий и `dialog_sessions` в managed PostgreSQL, схема `bitrix_local`;
|
||
- предоставляет internal API `POST /internal/openlines/v1/messages` и `GET /internal/openlines/v1/dialogs/{external_chat_id}` для api-backend;
|
||
- защищает internal API через `Authorization: Bearer {BITRIX_INTERNAL_API_TOKEN}`;
|
||
- forward-ит нормализованные события Open Lines в API, если задан `BITRIX_API_FORWARD_URL`;
|
||
- не хранит бизнес-данные приложения и не пишет напрямую в App DB.
|
||
|
||
### Managed PostgreSQL
|
||
|
||
**Во всех средах** (production, production-like, local dev) данные хранятся в **managed PostgreSQL** провайдера. Контейнер PostgreSQL в Docker Compose **не используется** — ни для production, ни для локальной разработки.
|
||
|
||
Прикладные данные, Keycloak, `bitrix-sync`, `bitrix-local-app` и `message-safety` подключаются к одной managed базе по URL из `.env` (`HAN_PG_HOST`, `HAN_PG_PORT`, `HAN_PG_DATABASE` и схемо-специфичные `*_DATABASE_URL`).
|
||
|
||
Требования:
|
||
|
||
- подключение только из приватной сети VPC (VM → managed PostgreSQL);
|
||
- одна managed база: схемы `han_app`, `bitrix_sync`, `message_safety`, `bitrix_local`, `keycloak`;
|
||
- отдельные DB-пользователи с доступом только к своей схеме; исключение: `bitrix_sync_user` дополнительно имеет ограниченный GRANT на `han_app` (`sync_queue`, `entity_external_mapping`, tracked columns профиля — детали схемы TBD в спецификации database);
|
||
- TLS к managed PostgreSQL обязателен;
|
||
- миграции Alembic выполняются отдельной командой при деплое;
|
||
- бэкапы и PITR — на стороне провайдера.
|
||
|
||
### keycloak
|
||
|
||
Identity provider.
|
||
|
||
Требования:
|
||
|
||
- отдельный realm для приложения;
|
||
- отдельный frontend client с PKCE;
|
||
- backend client для service-to-service сценариев;
|
||
- публичный issuer должен соответствовать HTTPS URL, видимому frontend-приложению;
|
||
- включены proxy settings для работы за `nginx`;
|
||
- импорт realm в local/dev;
|
||
- использует managed PostgreSQL, схема `keycloak` (см. раздел «Managed PostgreSQL» выше);
|
||
- healthcheck.
|
||
|
||
### redis
|
||
|
||
Очереди, кеш, rate limiting.
|
||
|
||
Требования:
|
||
|
||
- не использовать как единственное надежное хранилище бизнес-событий;
|
||
- хранить счетчики API-level rate limits;
|
||
- поддерживать TTL для лимитных ключей;
|
||
- sync_queue хранится в PostgreSQL (`han_app`), Redis может использоваться для wake-up/locking/queue optimization.
|
||
|
||
### otel-collector
|
||
|
||
Принимает telemetry от сервисов.
|
||
|
||
Требования:
|
||
|
||
- OTLP HTTP/gRPC receiver;
|
||
- экспорт traces/logs в stdout или платформенный collector;
|
||
- единые resource attributes: `service.name`, `deployment.environment`.
|
||
|
||
## Networks
|
||
|
||
Рекомендуемые сети:
|
||
|
||
- `public`: `nginx`, frontend dev access, внешний HTTPS entrypoint.
|
||
- `backend`: API, `message-safety`, `bitrix-sync`, `bitrix-local-app`, `redis` (managed PostgreSQL — вне compose, в VPC).
|
||
- `observability`: otel-collector.
|
||
|
||
Базы данных, Redis, Keycloak internal port и API internal port не должны публиковаться наружу. `message-safety` доступен только внутри сети `backend`. Основной пользовательский путь должен идти через `nginx` и HTTPS.
|
||
|
||
## Volumes
|
||
|
||
Минимальные volumes (production на одной VM):
|
||
|
||
- `redis-data` (опционально, если нужна персистентность);
|
||
- certbot / TLS volumes для `nginx`.
|
||
|
||
Данные PostgreSQL **не** хранятся в Docker volumes — только managed PostgreSQL вне compose.
|
||
|
||
## Переменные окружения
|
||
|
||
Корневой `backend/.env` читается всеми сервисами compose через `${VAR}` в сервисных `docker-compose.yml`. Канонический `.env.example`, service tokens, `app_settings` — в [`arch-04-settings-and-content.md`](arch-04-settings-and-content.md).
|
||
|
||
## HTTPS и TLS
|
||
|
||
Соответствует [`arch-01-system-architecture.md`](arch-01-system-architecture.md), «Принципы безопасности» (HTTPS, TLS, HSTS). Инфраструктурная реализация:
|
||
|
||
### Домены и HTTP
|
||
|
||
| Host | Порт 80 | Порт 443 | Примечание |
|
||
|---|---|---|---|
|
||
| Веб-домен (frontend) | только `301`/`308` → HTTPS | HTTPS, бизнес-логика | MVP: `tohin.ru` / `app.example.ru` |
|
||
| API-домен (если выделен) | **не слушает** | только HTTPS | Post-MVP: `api.example.ru` |
|
||
| Bitrix callbacks (`/bitrix/*`, `/bitrix/sync/*`) | не обслуживает API; только redirect на том же host | HTTPS | webhook и install URL |
|
||
|
||
Правила:
|
||
|
||
- все внешние пользовательские соединения — **HTTPS**;
|
||
- HTTP допускается **только** на веб-домене как вход для редиректа на HTTPS;
|
||
- для **выделенного API-домена** HTTP **не допускается** (нет listener на :80);
|
||
- при едином домене MVP redirect на :80 применяется ко всему host, включая `/api/*` и `/auth/*`, после редиректа — только HTTPS.
|
||
|
||
### TLS и заголовки
|
||
|
||
- cookies в web-клиенте: `Secure`, `HttpOnly`, корректный `SameSite`;
|
||
- OIDC redirect URI в Keycloak — HTTPS;
|
||
- `KEYCLOAK_PUBLIC_URL`, issuer и frontend auth discovery URL совпадают по схеме, host и path;
|
||
- backend формирует внешние ссылки с учётом `X-Forwarded-Proto=https`;
|
||
- HSTS включается в production-like среде **после** проверки доменов и сертификатов;
|
||
- TLS 1.0/1.1 запрещены; минимум TLS 1.2, предпочтительно TLS 1.3;
|
||
- слабые шифры запрещены на уровне `nginx`;
|
||
- `nginx` скрывает `Server`, `X-Powered-By` и аналогичные технологические заголовки;
|
||
- security headers: `Strict-Transport-Security`, `X-Content-Type-Options`, `Referrer-Policy`, `Content-Security-Policy` для web-приложения;
|
||
- секретный ключ сертификата не коммитится в репозиторий;
|
||
- использовать сертификаты доверенного CA; автоматизировать выпуск и продление (Let's Encrypt + reload `nginx`);
|
||
- закрыть прямой доступ к внутренним портам контейнеров извне.
|
||
|
||
## Nginx routing для Bitrix24 Local App
|
||
|
||
`nginx` должен поддерживать отдельные server/location rules для `bitrix-local-app`.
|
||
|
||
Рекомендуемая схема:
|
||
|
||
- **веб-домен** (MVP: `tohin.ru` или `app.example.ru`): `/api/*`, `/auth/*`, `/realtime/*`, web frontend; `:80` → redirect HTTPS; `:443` — TLS + маршрутизация;
|
||
- **выделенный API-домен** (post-MVP, опционально): отдельный `server { listen 443 ssl; ... }` **без** `listen 80`; только `/api/*`, `/realtime/*`;
|
||
- домен или path `/bitrix/*` → `bitrix-local-app`; `/bitrix/sync/*` → `bitrix-sync`;
|
||
- `GET/POST /bitrix/handler` и `GET/POST /bitrix/install` доступны публично для Bitrix24;
|
||
- `/bitrix/placement` доступен публично как заглушка UI настроек коннектора;
|
||
- `/health/live` и `/health/ready` для `bitrix-local-app` доступны только там, где это нужно для healthcheck и проверки Bitrix form URL;
|
||
- `/internal/openlines/v1/*` не публикуется наружу или защищается allowlist/private network плюс `Authorization: Bearer {BITRIX_INTERNAL_API_TOKEN}`;
|
||
- для `/bitrix/*` callbacks кэширование отключено;
|
||
- для `/bitrix/*` callbacks включены отдельные rate limits, но они не должны блокировать легитимные webhook-повторы Bitrix24.
|
||
|
||
## Nginx routing для bitrix-sync (CRM webhook)
|
||
|
||
`nginx` маршрутизирует публичные webhook CRM sync в `bitrix-sync`:
|
||
|
||
- `POST /bitrix/sync/webhook/contact` — исходящий webhook от роботов Bitrix24 при изменении Contact;
|
||
- проверка `BITRIX_SYNC_WEBHOOK_TOKEN` выполняется в `bitrix-sync`;
|
||
- кэширование отключено; rate limits не должны блокировать легитимные повторы Bitrix24;
|
||
- `/internal/sync/v1/*` не публикуется наружу (только internal network + `BITRIX_SYNC_SERVICE_TOKEN`).
|
||
|
||
## Rate limits и защита от abuse
|
||
|
||
Rate limits должны быть распределены по двум слоям.
|
||
|
||
`nginx`:
|
||
|
||
- ограничивает частоту запросов до попадания в API;
|
||
- держит отдельные зоны лимитов для `/auth`, `/api`, public endpoints, fallback polling и download endpoints;
|
||
- ограничивает `client_max_body_size`;
|
||
- ограничивает загрузку файлов лимитом 5 МБ; `client_max_body_size` должен быть чуть выше бизнес-лимита для учета overhead запроса;
|
||
- применяет `limit_req` для endpoint авторизации и fallback polling;
|
||
- для публичных endpoint использует лимит не выше 60 запросов в минуту с одного IP, если настройки не говорят иначе;
|
||
- возвращает `429` при превышении лимитов;
|
||
- не должен использоваться для сложных пользовательских правил, завязанных на `user_id`.
|
||
|
||
API:
|
||
|
||
- применяет лимиты после проверки JWT;
|
||
- считает лимиты по `user_id`, IP, route, dialog id и service client;
|
||
- хранит быстрые счетчики в Redis;
|
||
- пишет значимые превышения в audit/App DB;
|
||
- возвращает `Retry-After`, если клиент может повторить запрос позже.
|
||
|
||
Проверка сообщений на prompt injection и вредоносные действия не должна выполняться в `nginx`: это задача отдельного сервиса `message-safety`, вызываемого из `api-backend` (см. [`arch-02-api-contracts.md`](arch-02-api-contracts.md)).
|
||
|
||
## WAF
|
||
|
||
WAF можно подключить внешним слоем перед `nginx` без изменения бизнес-кода, если соблюдены требования:
|
||
|
||
- `nginx` и API корректно работают с цепочкой proxy headers и доверяют real IP только от доверенных прокси;
|
||
- CORS разрешает только доверенные домены;
|
||
- публичные endpoint имеют rate limits и кэширование даже без WAF;
|
||
- схема TLS termination согласована с тем, где завершается TLS: WAF/CDN, load balancer или `nginx`;
|
||
- WAF не должен подменять тело запросов и ответы API без явной необходимости.
|
||
|
||
WAF не заменяет обязательные лимиты, валидацию схем, авторизацию и аудит внутри приложения.
|
||
|
||
## Публичные endpoint
|
||
|
||
`GET /api/v1/public/app-config` и `GET /api/v1/public/content` являются публичными, поэтому для них обязательны:
|
||
|
||
- `limit_req` на уровне `nginx`, базово 60 запросов в минуту с одного IP;
|
||
- агрессивное кэширование на уровне `nginx` или CDN;
|
||
- заголовок `Cache-Control: public, max-age=3600`;
|
||
- строгая DTO-схема ответа на backend, без сериализации всех строк таблицы настроек;
|
||
- CORS только для доверенных доменов приложения;
|
||
- отсутствие секретов, внутренних URL, service tokens и приватных feature flags в ответе.
|
||
|
||
Подробнее — [`arch-04-settings-and-content.md`](arch-04-settings-and-content.md), разделы «Публичный config endpoint» и «Публичный content endpoint».
|
||
|
||
## Healthchecks
|
||
|
||
Минимальные проверки:
|
||
|
||
- `nginx`: на веб-домене — `301` с `:80` на HTTPS; на API-домене (если выделен) — `:80` не слушает; `:443` — HTTP 200/301 и успешная TLS handshake;
|
||
- `api-backend`: HTTP 200 от `/health/ready`;
|
||
- `message-safety`: HTTP 200 от `/health/ready` (проверяет PostgreSQL, Redis, workers, read S3-quarantine);
|
||
- `bitrix-sync`: процесс жив, подключение к App DB доступно;
|
||
- `bitrix-local-app`: HTTP 200 от `/health/live`, readiness показывает наличие OAuth-токенов после установки приложения;
|
||
- `keycloak`: health endpoint Keycloak; readiness — подключение к managed PostgreSQL;
|
||
- `redis`: `redis-cli ping`;
|
||
|
||
## Порядок запуска
|
||
|
||
1. `redis` (managed PostgreSQL должна быть доступна до старта зависимых сервисов).
|
||
2. `keycloak`.
|
||
3. `otel-collector`.
|
||
4. `message-safety`.
|
||
5. `api-backend`.
|
||
6. `bitrix-local-app`.
|
||
7. `bitrix-sync`.
|
||
8. `nginx`.
|
||
|
||
`depends_on` не заменяет проверку готовности. Сервисы должны уметь ждать зависимости или корректно завершаться с понятной ошибкой. `api-backend` должен ждать готовности `message-safety` (healthcheck), т.к. отправка сообщения синхронно зависит от `POST /internal/safety/v1/messages/check`.
|
||
|
||
## Развёртывание на одной VM
|
||
|
||
Production-контур на `tohin.ru`:
|
||
|
||
1. VM и managed PostgreSQL в одном VPC/кластере провайдера.
|
||
2. Managed PostgreSQL без публичного IP; security group разрешает подключение только с VM.
|
||
3. `docker compose up -d` на VM поднимает все сервисы кроме БД.
|
||
4. Сервисы подключаются к managed PostgreSQL по приватному FQDN/IP.
|
||
|
||
## Production-замечания
|
||
|
||
### Frontend (Expo web)
|
||
|
||
| Режим | Поведение nginx |
|
||
|---|---|
|
||
| production-like / production | Статика Expo web (`expo export` / EAS web build), `FRONTEND_DEV_PROXY_ENABLED=false` |
|
||
| local dev | Опционально proxy на Expo dev server, `FRONTEND_DEV_PROXY_ENABLED=true` |
|
||
|
||
Переменные — [`arch-04-settings-and-content.md`](arch-04-settings-and-content.md), блок «Frontend (nginx)».
|
||
|
||
Docker Compose на одной VM — production-контур первого этапа. Позже при росте нагрузки можно отдельно решить:
|
||
|
||
- вынос Redis в managed cache;
|
||
- managed object storage;
|
||
- secret manager;
|
||
- TLS, reverse proxy или managed ingress;
|
||
- backup и restore;
|
||
- централизованный мониторинг;
|
||
- горизонтальное масштабирование API и worker.
|