Docker — правила проекта Mart

Краткий контракт: где крутится Docker, что ставим локально, что — нет. Пошаговые команды и troubleshooting добавим позже.

compose.yml — только для server (/srv/mart/src). Локально не поднимаем.


Главное

  1. Docker на локальной машине не используем и не ставим.
  2. Docker есть только на удалённом сервере — хост server в ~/.ssh/config (доступ по SSH-ключу).
  3. Любая команда docker / docker compose выполняется на server, не на Mac/Windows разработчика.
  4. Весь dev-стек на server в Docker (frontend, backend, PostgreSQL, кэш) — не через локальный just front-dev / just back-dev как основной способ запуска.

Отсутствие Docker локально — норма, а не ошибка окружения.


Что где запускаем

КомпонентГде
Frontend (Next.js)Docker на server
Backend (nginx, PHP-FPM, memcached, Redis)Docker на server
PostgreSQLDocker на server

Локально: git, IDE, Node/pnpm (линт, typecheck), SSH — без Docker.
На server: весь runtime приложения в контейнерах.

just front-check / just back-check — по-прежнему локально, это проверки кода, не запуск стека.


Именование

У каждого сервиса в compose:

  • container_name с префиксом mart- — обязательно
    Примеры: mart-nginx, mart-php, mart-front, mart-pg, mart-memcached, mart-redis
  • Имя сервиса в compose.yml — тоже с префиксом mart- (единый стиль в командах и логах)

Без префикса mart- новые сервисы не добавляем.


Монтирование: только bind

  • Named volumes и секция volumes: в compose не используем.
  • Только bind mount: путь на server → путь в контейнере.
  • Три типа bind (см. структуру каталогов и пути в контейнере):
    • /srv/mart/src на server → /srv/www/mart/current в контейнере (код);
    • /srv/mart/data/<service> — персистентные данные сервиса;
    • /srv/mart/log/<service> — логи сервиса.

<service> — короткое имя сервиса (обычно без префикса mart-): pg, redis, nginx, php, front, memcached.

Примеры:

Bind на serverПуть в контейнереНазначение
/srv/mart/src/srv/www/mart/currentрепозиторий (код)
/srv/mart/data/pgкаталог данных PostgreSQLБД
/srv/mart/log/nginx/var/log/nginx (или аналог)access/error log

Исключений без явного решения в команде не делаем.


SSH

  • Единственная точка входа к Docker — ssh server.
  • Ключ и параметры хоста — в ~/.ssh/config, в репозиторий не коммитим.
  • Не настраиваем DOCKER_HOST на локальной машине — достаточно SSH.

Структура каталогов на server

Корень окружения Mart на server:

/srv/mart/
├── src/              ← этот репозиторий (git clone)
├── data/
│   ├── pg/           ← данные mart-pg
│   ├── redis/        ← данные mart-redis
│   └── …/            ← data/<service> для каждого сервиса с персистентом
└── log/
    ├── nginx/        ← логи mart-nginx
    ├── php/          ← логи mart-php
    └── …/            ← log/<service> для каждого сервиса с логами на диске
  • src — только код и конфиги из git; runtime-данные и логи сюда не пишем.
  • data/<service> — bind для данных (БД, Redis AOF/RDB и т.д.).
  • log/<service> — bind для файлов логов.

docker compose запускаем из /srv/mart/src (compose.yml в корне репозитория).


Пути: server и контейнер

На диске server и внутри контейнеров — разные пути. Якорь в контейнере — /srv/www/mart/current, но содержимое bind у каждого сервиса своё (см. compose.yml):

СервисBind на server (из /srv/mart/src)Что видит контейнер в /srv/www/mart/current
mart-nginxapps/backend/public/index.php, bundles/
mart-phpapps/backend/Symfony-проект (public/, src/, …)
mart-frontapps/frontend/Next.js-проект
mart-frontdocs//srv/www/docsMarkdown для UI /docs (resolveDocsRoot())
ГдеПутьДля чего
Server (host)/srv/mart/srcgit, rsync, docker compose
Логи/srv/mart/log/<service>bind в контейнер (например /var/log/nginx)
Данные/srv/mart/data/<service>Redis, PG и т.д.

Nginx root/srv/www/mart/current (public).
FastCGI SCRIPT_FILENAME/srv/www/mart/current/public/index.php (путь в mart-php, не в nginx).

  • Конфиги в .docker/ пишем с учётом таблицы выше.
  • Скрипты синхронизации работают с host-путём: /srv/mart/src.

Код на server

  • Репозиторий — /srv/mart/src.
  • Код должен совпадать с git (pull на server или bin/server/sync.sh / just bin-server-sync).
  • Docker-конфиги — .docker/server/, compose.yml в git; данные и логи — только в /srv/mart/data/* и /srv/mart/log/*.
  • Каталоги /srv/mart/data/* и /srv/mart/log/* на server создаём и бэкапим отдельно от git.

Типичный стек на server

Сервис (compose)container_nameРоль
mart-nginxmart-nginxHTTP/HTTPS, прокси к PHP и frontend
mart-phpmart-phpPHP-FPM, Symfony
mart-frontmart-frontNext.js dev или production-режим
mart-memcachedmart-memcachedКэш
mart-redismart-redisRedis
mart-pgmart-pgPostgreSQL → bind /srv/mart/data/pg

PHP подключается к БД по имени сервиса в compose (например, mart-pg), не к внешнему db-server.


Структура URL

Точка входа — http://mart.myazin.dev (nginx в mart-nginx, порт 80 на server).
Конфиг — .docker/server/nginx/conf/sites-enabled/mart.myazin.dev.conf.

root nginx: public/ смонтирован в /srv/www/mart/current (см. compose).

Браузер / API-клиент
       │
       ▼
  mart-nginx :80  (server_name mart.myazin.dev)
       │
       ├── ^~ /api        → @phpApi → mart-php:9001
       ├── ^~ /bundles/   → disk (статика Swagger UI для /api/docs)
       ├── /cache         → internal (Symfony HTTP cache)
       └── /              → mart-front:3000  (Next.js: /, /docs, /_next/, …)

Правило: /api — PHP; всё остальное — Next.js. Исключение — /bundles/ (ассеты API Platform, не UI).

Префикс ^~ /api обрабатывается до catch-all location /.

Таблица префиксов

URL (префикс)UpstreamНазначениеПримеры
/api, /api/…mart-php:9001REST API и OpenAPI/api, /api/campaigns, /api/docs
/bundles/nginx → diskСтатика API Platform (Swagger)/bundles/apiplatform/...
/ (всё остальное)mart-front:3000Next.js/, /docs/..., /_next/..., /demo/...

PHP вызывается только через @phpApiindex.php. Прямые запросы к *.php404.
Internal: /cache — только для внутренних redirect nginx/Symfony HTTP cache.

Frontend (Next.js)

Маршруты из apps/frontend/src/app/(app)/:

ПутьФайлПримечание
/(app)/page.tsxГлавная
/demo/...(app)/demo/...Демо UI-кита
/docs, /docs/...(app)/docs/...Документация проекта (Markdown из docs/)

Frontend обращается к API по /api/... на том же origin.

Backend (Symfony)

ПутьКонфигПримечание
/api/*api_platform.yaml, prefix /apiCORS на location
/api/docs, /api/docs.jsonAPI Platform (api_doc)Swagger UI / OpenAPI
/bundles/*assets в public/Без PHP

CORS и ошибки API

  • .docker/server/nginx/conf/snippets/cors.conf — заголовок Mart-Server, preflight OPTIONS на /api/.
  • Таймаут PHP → JSON 502 / 504 (@api_error_* в nginx).

Внутренние имена (Docker network)

СервисПортКто обращается
mart-php9001 FastCGImart-nginx
mart-front3000 HTTPmart-nginx
mart-pg5432mart-php
mart-redis, mart-memcachedmart-php

Снаружи server — только nginx :80.


Доступ с локальной машины

  • Порты слушают на server, не на localhost разработчика.
  • Браузер / отладка — SSH-туннель (ssh -L …). Детали — позже.

DO ✓

  • Управлять контейнерами только через ssh server '… docker compose …'.
  • Префикс mart- у всех сервисов и container_name.
  • Только bind для монтирования; данные и логи — в /srv/mart/data/<service> и /srv/mart/log/<service>, не в src/.
  • Линт и typecheck — локально (just front-check, just back-check).

DON'T ✗

  • Не ставить Docker Desktop / Engine на рабочую машину «для Mart».
  • Не использовать named volumes / anonymous volumes для данных проекта.
  • Не писать данные и логи сервисов в /srv/mart/src — только data/ и log/.
  • Не поднимать frontend/backend локально как основной dev-стек (если нужен разовый just front-dev — исключение, не модель проекта).
  • Не поднимать стек локально через docker compose (только на server).
  • Не считать отсутствие docker в bin/check-env проблемой.
  • Не хранить секреты и ключи SSH в репозитории.

Что добавим позже

  • Сервис mart-pg в compose.yml.
  • Команды: up/down/logs/exec, деплой кода на server.
  • SSH-туннели под типичные порты.
  • Troubleshooting и .env для контейнеров.