TECH 11 мин

От одного сервера до облака: как мы масштабируем 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

Сеть: безопасность по умолчанию

Вся инфраструктура живёт в приватной 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) — основной бэкенд и парламентские данные:

openreyestr-db (db-custom-1-4096) — Госреестр юридических лиц:

Оба инстанса имеют:

Qdrant на выделенной VM

Qdrant — векторная база для семантического поиска. Managed варианта от GCP нет, поэтому мы развернули её на отдельной VM:

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 файлах. Это работает, но:

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/мес

  1. Объединить Cloud SQL — openreyestr как отдельная база в main инстансе: -$55
  2. 1-year commitment на Cloud SQL: -$37
  3. 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:

Cloudflare остаётся

Мы не заменили Cloudflare на GCP Cloud Armor. Cloudflare остаётся первым слоем защиты:

Cloudflare DNS A-запись указывает на IP Google Cloud Load Balancer. Трафик: пользователь → Cloudflare edge → GCP LB → Cloud Run.

CI/CD: автоматический деплой

GitHub Actions workflow при merge в main:

  1. Build packages/shared (общие типы)
  2. Параллельно: build 4 Docker images → push в Artifact Registry
  3. Deploy каждого сервиса в Cloud Run
  4. gsutil rsync фронтенда в GCS

Rollback — одна команда: Cloud Run позволяет переключить трафик на предыдущую ревизию за секунды.

Что дальше

Эта архитектура — фундамент, на котором мы строим. Ближайшие шаги:

  1. Cloud Scheduler — автоматическое уменьшение min-instances ночью
  2. Cloud SQL Insights — мониторинг медленных запросов
  3. Prometheus + Grafana на Qdrant VM — кастомные метрики
  4. Workload Identity Federation — GitHub Actions без service account keys

Цель — инфраструктура, которая масштабируется вместе с продуктом, а не становится его ограничением.


Если вы строите юридический или любой другой SaaS на микросервисах — Cloud Run + Cloud SQL это отличный старт. Платите за то, что реально используете, а не за простаивающие серверы.