От одного сервера до облака: как мы масштабируем legal.org.ua на Google Cloud
Cloud Run с автоскейлингом до нуля. Cloud SQL с автобекапами. Qdrant на выделенной VM. Вся инфраструктура за $280-430/мес с возможностью масштабирования от 10 до 10 000 пользователей без изменений архитектуры.
От одного сервера до облака: как мы масштабируем legal.org.ua на Google Cloud
Как мы перенесли юридическую AI-платформу с Docker Compose на одном сервере до полноценной облачной инфраструктуры с автоматическим масштабированием.
Почему миграция стала необходимой
legal.org.ua — платформа для юристов с AI-анализом судебных решений, семантическим поиском по законодательству и реестрам. Под капотом — 3 микросервиса, PostgreSQL, Redis, Qdrant (векторная БД), MinIO и фронтенд на React.
Начальная инфраструктура — один VPS-сервер с Docker Compose. Это работало для MVP, но создавало риски:
| Проблема | Последствие |
|---|---|
| Один сервер | Падение сервера = полный downtime |
| Фиксированные ресурсы | Не масштабируется под нагрузку |
| Ручные деплои | SSH → git pull → docker compose up |
| Бекапы вручную | Риск потери данных |
Нам нужна инфраструктура, которая масштабируется автоматически, имеет автобекапы и стоит разумных денег для стартапа.
Выбор облака: почему Google Cloud
Мы рассматривали AWS, GCP и Hetzner Cloud. Выбрали GCP по нескольким причинам:
Cloud Run — главный аргумент. Это serverless контейнеры с оплатой за фактическое использование и возможностью масштабирования до нуля. Для юридической платформы с дневным трафиком (юристы работают с 9 до 18) это значит, что ночью и на выходных мы платим почти ничего.
Cloud SQL — managed PostgreSQL с автоматическими бекапами, point-in-time recovery и возможностью вертикального масштабирования в один клик.
Регион europe-west1 (Бельгия) — ближайший к Украине с лучшими ценами среди европейских регионов GCP.
Архитектура: гибридный подход
Ключевое решение — не всё в serverless. Мы разделили сервисы по природе:
Cloudflare (DNS + CDN + WAF)
│
Cloud Load Balancer (HTTPS)
┌──────────┼──────────┐
Cloud Run Cloud Run Cloud Run
(mcp_backend) (mcp_rada) (openreyestr)
└──────────┼──────────┘
┌───────┬───────┼───────┬────────┐
Cloud SQL Memorystore GCE VM GCS
(PG 15) (Redis 7) (Qdrant) (файлы)
Stateless сервисы → Cloud Run
Наши 4 бэкенд-сервиса не хранят состояние между запросами — идеальные кандидаты для Cloud Run:
| Сервис | Что делает | CPU | RAM | Авто-масштабирование |
|---|---|---|---|---|
mcp-backend |
Судебные решения, AI-чат, 36 инструментов | 2 vCPU | 4 GiB | 1 → 4 инстанса |
mcp-rada |
Депутаты, законопроекты, голосования | 1 vCPU | 1 GiB | 0 → 2 инстанса |
mcp-openreyestr |
Госреестр, бенефициары | 1 vCPU | 1 GiB | 0 → 2 инстанса |
document-service |
Обработка документов | 2 vCPU | 4 GiB | 0 → 3 инстанса |
Обратите внимание на min instances: главный бэкенд всегда имеет хотя бы 1 инстанс (cold start недопустим для AI-чата с SSE стримингом), а вспомогательные сервисы масштабируются до нуля когда никто не использует.
Stateful сервисы → Managed или VM
- PostgreSQL → Cloud SQL (managed, автобекапы, point-in-time recovery)
- Redis → Memorystore (managed, sub-millisecond latency)
- Qdrant → GCE VM (нет managed варианта, требуется persistent storage)
- MinIO → GCS (Google Cloud Storage с S3-совместимым API)
Сеть: безопасность по умолчанию
Вся инфраструктура живёт в приватной VPC-сети. Ни один сервис не имеет публичного IP, кроме Load Balancer.
VPC: secondlayer-vpc
├── services-subnet 10.0.0.0/20 (Cloud Run VPC Connector)
├── data-subnet 10.0.16.0/20 (Cloud SQL, Qdrant VM)
└── VPC Connector 10.8.0.0/28 (Cloud Run → приватная сеть)
Cloud NAT обеспечивает исходящий интернет для VM без публичного IP. IAP (Identity-Aware Proxy) — SSH доступ к VM через Google аутентификацию вместо открытого 22 порта.
Firewall правила простые: разрешён только внутренний трафик между подсетями, SSH через IAP и health checks от Google Load Balancer.
Cloud SQL: два инстанса
Мы сознательно разделили PostgreSQL на два инстанса:
secondlayer-main (db-custom-2-8192) — основной бэкенд и парламентские данные:
- База
secondlayer_prod: судебные решения, документы, AI-аналитика, пользователи - База
rada_prod: депутаты, законопроекты, голосования
openreyestr-db (db-custom-1-4096) — Госреестр юридических лиц:
- Преимпортированная база с миллионами записей
- Read-heavy нагрузка, редко записывается
- Отдельный инстанс предотвращает lock contention с основной базой
Оба инстанса имеют:
- Private IP only (не доступны из интернета)
- Автоматические бекапы каждую ночь в 3:00
- Point-in-time recovery
max_connections=500(достаточно для Cloud Run с connection pooling)
Qdrant на выделенной VM
Qdrant — векторная база для семантического поиска. Managed варианта от GCP нет, поэтому мы развернули её на отдельной VM:
- e2-standard-4 (4 vCPU, 16 GiB RAM) — достаточно для миллионов векторов
- 100 GB persistent disk (pd-balanced) — данные переживают удаление VM
- Docker container с
--restart=always
Persistent disk — ключевая деталь. Даже если VM упадёт или потребует upgrade, данные останутся на диске. Мы можем сменить тип VM за 5 минут без потери индексов.
GCS вместо MinIO: ноль изменений в коде
Одно из самых элегантных решений: Google Cloud Storage имеет S3-совместимый API. Наш код использует AWS S3 SDK для работы с MinIO. Для миграции достаточно изменить endpoint:
# Было (MinIO)
MINIO_ENDPOINT=minio-stage
MINIO_PORT=9000
# Стало (GCS)
MINIO_ENDPOINT=storage.googleapis.com
MINIO_PORT=443
MINIO_USE_SSL=true
Ни одной строки кода не изменено. Тот же upload pipeline, те же presigned URLs, та же логика.
Секреты: Secret Manager вместо .env файлов
На VPS секреты жили в .env файлах. Это работает, но:
- Файл может попасть в git
- Нет аудита кто когда получал доступ
- Ротация ключей = ручное обновление на сервере
GCP Secret Manager решает все три проблемы. Каждый секрет имеет версии, аудит доступа и интегрируется напрямую с Cloud Run через --set-secrets.
Мы создали 12 секретов: API ключи OpenAI, токены ZakonOnline, JWT secret, пароли баз данных и другие.
Стоимость: от $280 до $430/мес
Полная разбивка:
| Компонент | Спецификация | $/мес |
|---|---|---|
| Cloud Run (4 сервиса) | Автоскейлинг | $76 |
| Cloud SQL (2 инстанса) | PG 15, SSD, автобекапы | $150 |
| Memorystore Redis | 2 GiB, Basic | $50 |
| GCE VM (Qdrant) | e2-standard-4, 100 GB disk | $105 |
| GCS + CDN | ~50 GB файлов | $8 |
| Сеть (LB, NAT, VPC) | $33 | |
| Artifact Registry | Docker images | $3 |
| Итого | ~$430 |
Оптимизация до $280/мес
- Объединить Cloud SQL — openreyestr как отдельная база в main инстансе: -$55
- 1-year commitment на Cloud SQL: -$37
- Spot VM для Qdrant (если допустим restart): -$60
Стратегия масштабирования
Горизонтальное (автоматическое)
Cloud Run масштабируется автоматически по concurrency. Когда нагрузка растёт — добавляются инстансы. Когда падает — лишние выключаются.
08:00 mcp-backend: 1 инстанс (тихое утро)
10:00 mcp-backend: 2 инстанса (рабочий день)
14:00 mcp-backend: 4 инстанса (пик активности)
22:00 mcp-backend: 1 инстанс (вечер)
02:00 mcp-rada: 0 инстансов (никто не ищет депутатов ночью)
Вертикальное (ручное, по необходимости)
| Триггер | Действие |
|---|---|
| Cloud SQL CPU > 80% | Upgrade до db-custom-4-16384 |
| Redis > 85% RAM | Resize до 4 GiB |
| Qdrant VM > 80% RAM | Upgrade до e2-standard-8 |
Что меняется при росте
10 → 100 пользователей: текущая архитектура справляется без изменений.
100 → 1000 пользователей: добавляем Cloud SQL read replica ($95/мес), увеличиваем max instances Cloud Run до 8.
1000+ пользователей: миграция на GKE Autopilot для более гранулярного контроля, Qdrant cluster (3 ноды), Cloud SQL HA.
Фронтенд: GCS + Cloud CDN
React SPA (Vite build) — это статические файлы. Вместо Cloud Run контейнера мы хостим их на GCS с Cloud CDN:
- Стоимость: ~$1/мес (вместо ~$15 за Cloud Run контейнер)
- Latency: файлы раздаются с ближайшего edge к пользователю
- Cache hit ratio: >95% для JS/CSS бандлов
Cloudflare остаётся
Мы не заменили Cloudflare на GCP Cloud Armor. Cloudflare остаётся первым слоем защиты:
- Бесплатный WAF — защита от SQL injection, XSS
- DDoS protection — автоматическое поглощение атак
- Edge caching — статика раздаётся с Kyiv PoP
- Origin CA — SSL сертификат уже настроен
Cloudflare DNS A-запись указывает на IP Google Cloud Load Balancer. Трафик: пользователь → Cloudflare edge → GCP LB → Cloud Run.
CI/CD: автоматический деплой
GitHub Actions workflow при merge в main:
- Build
packages/shared(общие типы) - Параллельно: build 4 Docker images → push в Artifact Registry
- Deploy каждого сервиса в Cloud Run
gsutil rsyncфронтенда в GCS
Rollback — одна команда: Cloud Run позволяет переключить трафик на предыдущую ревизию за секунды.
Что дальше
Эта архитектура — фундамент, на котором мы строим. Ближайшие шаги:
- Cloud Scheduler — автоматическое уменьшение min-instances ночью
- Cloud SQL Insights — мониторинг медленных запросов
- Prometheus + Grafana на Qdrant VM — кастомные метрики
- Workload Identity Federation — GitHub Actions без service account keys
Цель — инфраструктура, которая масштабируется вместе с продуктом, а не становится его ограничением.
Если вы строите юридический или любой другой SaaS на микросервисах — Cloud Run + Cloud SQL это отличный старт. Платите за то, что реально используете, а не за простаивающие серверы.