Skip to main content

idea-store (idea.uz) — Что было и что стало

О проекте

idea-store — e-commerce платформа (интернет-магазин idea.uz). Стек: Laravel (PHP) бэкенд + Next.js (SSR) фронтенд, MySQL, Meilisearch (поиск), Redis (кэш/сессии/очереди), Voyager (CMS-панель).

Перед началом работ был составлен документ планирования — «idea.uz Модернизация инфраструктуры» (idea_uz_new_infra.md), в котором описывалась исходная архитектура, целевое состояние и планируемый CI/CD-пайплайн. Ниже — что из этого плана было реализовано, что пошло иначе и что добавилось сверх плана.


1. Серверная архитектура

Было (как описано в документе планирования)

Frontend и backend жили в двух отдельных репозиториях и были развёрнуты на разных bare-metal серверах с отдельными доменами (idea.uz и api.idea.uz). Взаимодействие между ними шло через публичный интернет — Next.js SSR обращался к Laravel API по внешнему домену, трафик уходил в интернет и возвращался обратно, даже если обе машины находились рядом. Как отмечено в документе: «это делало API-коммуникацию между частями одного приложения ненадёжной и зависимой от внешних факторов».

PHP версии 8.x. Деплой ручной. Очереди через supervisor, планировщик через crontab, кэш в файлах. БД на той же VM, что и бэкенд.

Стало

Реализовано в точности по целевой схеме из документа планирования:

  • Оба проекта объединены в один корпоративный монорепозиторий
  • Всё упаковано в Docker Compose — один docker-compose.prod.yml поднимает полный стек: Nginx (единая точка входа), Next.js, PHP-FPM, scheduler, queue workers (2 реплики), Redis, Meilisearch
  • Все сервисы общаются через внутреннюю Docker-сеть — никакого трафика через внешние адреса
  • 1 внешний IP вместо нескольких — как и планировалось, единственная точка входа через Nginx
  • БД вынесена на отдельный сервер по приватной сети — изолирована от интернета
  • PHP обновлён до версии 10
  • Кэш, сессии и очереди переведены на Redis
  • Supervisor заменён на отдельные Docker-контейнеры для scheduler и queue

Документ планирования указывал «время первичного развёртывания: минуты (make setup && make up)» — это реализовано, стек поднимается одной командой.


2. CI/CD

Было

В документе планирования этот раздел был помечен как «В планах» — описывался ручной деплой через SSH-скрипт с рисками downtime, отсутствием проверок и невозможностью отката.

Стало

Планируемый пайплайн из документа реализован полностью, причём с дополнениями которые не были описаны в исходном плане:

  • CI: при push автоматическая сборка Docker-образов (backend, frontend, nginx)
  • CD: деплой по кнопке через workflow_dispatch — выбор окружения (dev/production) и ветки
  • Атомарные шаги: build → deploy → migrate → healthcheck — соответствует схеме из документа (Build → Test → Registry → Deploy → Health-check → Rollback)

Что отличается от плана:

  • В документе планировалось «нет прямого доступа к серверу — CI работает через Docker-механизмы». В реальности используются self-hosted runners — один в офисе (dev, закрытая сеть без публичного IP), второй на прод-сервере. Runner находится прямо на целевой машине, так что SSH-доступ из CI действительно не нужен
  • В документе упоминались автотесты как обязательный этап — в текущей реализации этап тестирования есть в пайплайне, но полное покрытие автотестами пока в развитии
  • Near-zero downtime описанный в плане достигается за счёт docker compose up -d --remove-orphans — новые контейнеры поднимаются до остановки старых

3. PHP версия

Было

Laravel 8.x — ряд современных инструментов работал с ограничениями.

Стало

Обновление до Laravel 10 — дало возможность подключить AppSignal и другие современные пакеты.


4. Мониторинг и observability

В документе планирования мониторинг не был описан как отдельный этап.

Было

Никакого APM. Отладка — вручную через curl, strace, MySQL slow query log.

Стало

Подключён AppSignal — APM с трейсами, error tracking, метриками хоста (стало возможным после обновления до PHP 10).

Была попытка развернуть SigNoz (open-source на базе ClickHouse + OTel Collector) на локальном мониторинг-сервере — стек был поднят, дашборды импортированы, трейсы начали приходить. Однако из-за сложности задачи решение отложено на неопределённый срок. Остались на AppSignal.


5. Production-окружение

Было

  • Очереди через supervisor
  • Планировщик через crontab
  • Статика на bare-metal сервере

Стало

  • Очереди — отдельный Docker-контейнер, 2 реплики, автоперезапуск
  • Планировщик — отдельный Docker-контейнер
  • Статика через Docker volumes

Итоговая архитектура

┌──────────────────────────────────────────────────┐
│ Docker Compose │
│ │
│ ┌──────────┐ ┌───────────┐ ┌─────────┐ │
│ │ Nginx │───▶│ Frontend │ │ Redis │ │
│ │ :80/443 │ │ Next.js │ │ :6379 │ │
│ └────┬─────┘ │ :8080 │ └─────────┘ │
│ │ └───────────┘ │
│ ▼ │
│ ┌──────────┐ ┌───────────┐ ┌────────────┐ │
│ │ PHP-FPM │ │ Scheduler │ │ Queue (×2) │ │
│ │ :9000 │ │ │ │ │ │
│ └────┬─────┘ └───────────┘ └────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────┐ ┌─────────────┐ │
│ │ MySQL │ │ Meilisearch │ │
│ │(отд. VM) │ │ :7700 │ │
│ └──────────┘ └─────────────┘ │
└──────────────────────────────────────────────────┘

GitHub Actions CI/CD
CI: push → build Docker images
CD: кнопка → build → deploy → migrate → healthcheck
Runners: self-hosted (dev в офисе + prod в облаке)

Мониторинг: AppSignal (APM, error tracking, host metrics)

Соответствие документу планирования

Пункт планаСтатусКомментарий
Контейнеризация в Docker Compose✅ РеализованоВ точности по целевой схеме
Единая точка входа (1 IP)✅ РеализованоNginx как единый reverse proxy
Изоляция межсервисного трафика✅ РеализованоВнутренняя Docker-сеть
БД на отдельном сервере✅ РеализованоПриватная сеть между VM
CI/CD пайплайн✅ РеализованоС дополнениями (self-hosted runners, manual rollback)
Near-zero downtime деплой✅ РеализованоDocker Compose rolling update
Автотесты как обязательный этап⏳ В процессеЭтап есть, покрытие растёт
Объединение в монорепо✅ РеализованоНе было в плане, но сделано
Обновление PHP 8 → 10✅ РеализованоНе было в плане, но сделано
Оптимизация производительности✅ РеализованоНе было в плане, обнаружено при тестировании
APM/Мониторинг (AppSignal)✅ РеализованоНе было в плане, но сделано

Ключевые уроки

  1. Объединение в монорепо + Docker радикально упрощает деплой и устраняет взаимодействие через внешние адреса — именно это предсказывал документ планирования
  2. Self-hosted runners — простое решение для CI/CD в закрытых сетях, не было в исходном плане но оказалось ключевым для реализации
  3. Отдельные контейнеры вместо supervisor/crontab — Docker way, проще управлять и масштабировать
  4. Обновление PHP (8 → 10) открыло доступ к современным инструментам мониторинга
  5. Документ планирования оказался точным — целевая архитектура реализована как задумано, а дополнительные этапы (performance, monitoring, PHP upgrade) стали бонусом