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) | ✅ Реализовано | Не было в плане, но сделано |
Ключевые уроки
- Объединение в монорепо + Docker радикально упрощает деплой и устраняет взаимодействие через внешние адреса — именно это предсказывал документ планирования
- Self-hosted runners — простое решение для CI/CD в закрытых сетях, не было в исходном плане но оказалось ключевым для реализации
- Отдельные контейнеры вместо supervisor/crontab — Docker way, проще управлять и масштабировать
- Обновление PHP (8 → 10) открыло доступ к современным инструментам мониторинга
- Документ планирования оказался точным — целевая архитектура реализована как задумано, а дополнительные этапы (performance, monitoring, PHP upgrade) стали бонусом