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