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 це відмінний старт. Платите за те, що реально використовуєте, а не за простоюючі сервери.