// DevOps
Как устроен деплой и панели управления: phpMyAdmin и RabbitMQ за nginx
Опубликовано 29.05.2026
Как устроен деплой и панели управления: phpMyAdmin и RabbitMQ за nginx
В этой заметке разберём практическую схему деплоя приложения через GitHub Actions и Docker Compose, а также подключение административных панелей phpMyAdmin и RabbitMQ через единый nginx-gateway.
Основная идея простая: наружу смотрит только nginx, а все внутренние сервисы — API, frontend, база данных, phpMyAdmin, RabbitMQ и воркеры — остаются во внутренней Docker-сети. При этом деплой полностью автоматизирован: после push в main CI собирает окружение, синхронизирует код на сервер, пересобирает контейнеры, запускает миграции и масштабирует воркеры.
Публичные домены, имена серверов и пути в статье анонимизированы. В примерах используются example.com, api.example.com, pma.example.com, rmq.example.com и условный путь /srv/project.
Общая схема
GitHub (push → main)
│
▼
GitHub Actions
┌─────────────────────────────┐
│ 1. Сборка .env │
│ 2. Генерация .htpasswd │
│ 3. rsync → сервер │
│ 4. docker compose build │
│ 5. docker compose up -d │
│ 6. Миграции │
│ 7. nginx -s reload │
│ 8. Масштабирование workers │
└─────────────────────────────┘
│
▼
Сервер приложения
┌──────────────────────────────────────────┐
│ gateway (nginx) ← единственный вход │
│ ├── example.com → frontend │
│ ├── api.example.com → api-php-fpm │
│ ├── pma.example.com → phpmyadmin │
│ └── rmq.example.com → rabbitmq:15672 │
└──────────────────────────────────────────┘
Здесь gateway — это контейнер с nginx, который принимает HTTP/HTTPS-трафик и проксирует запросы во внутреннюю сеть Docker. Снаружи у сервера открыты только порты 80 и 443. Панели phpMyAdmin и RabbitMQ не публикуют свои порты напрямую наружу.
Часть 1. Что происходит при push в main
Шаг 1. Сборка .env
Файл .env не хранится в Git. Он создаётся заново при каждом деплое прямо в CI. Это снижает риск случайной публикации секретов в репозитории и упрощает управление окружениями.
Все секреты живут в GitHub Actions в разделе:
Repository → Settings → Secrets and variables → Actions
Логика разделения такая:
vars.*— несекретные настройки: имена сервисов, домены, режимы работы, публичные параметры;secrets.*— пароли, токены, приватные ключи и любые значения, которые нельзя показывать в логах или хранить в коде.
Пример CI-шагa:
# deploy.yml
- name: Create .env
env:
MYSQL_USER: ${{ vars.MYSQL_USER }}
MYSQL_DATABASE: ${{ vars.MYSQL_DATABASE }}
MYSQL_PASSWORD: ${{ secrets.MYSQL_PASSWORD }}
RABBITMQ_USER: ${{ vars.RABBITMQ_USER }}
RABBITMQ_PASSWORD: ${{ secrets.RABBITMQ_PASSWORD }}
RABBITMQ_VHOST: ${{ vars.RABBITMQ_VHOST }}
run: |
echo "DB_URL=mysql://${MYSQL_USER}:${MYSQL_PASSWORD}@db:3306/${MYSQL_DATABASE}" >> .env
echo "MESSENGER_TRANSPORT_DSN=amqp://${RABBITMQ_USER}:${RABBITMQ_PASSWORD}@rabbitmq:5672/${RABBITMQ_VHOST}" >> .env
Важный момент: GitHub маскирует значения из secrets, но не стоит специально выводить их в лог. Даже если секреты скрываются, лучше строить пайплайн так, чтобы пароли и токены вообще не попадали в stdout.
Также нужно следить за спецсимволами в паролях. Если пароль содержит символы, значимые для URL, его нужно корректно кодировать, иначе DSN может стать невалидным. Это особенно актуально для строк подключения вида:
mysql://user:password@db:3306/database
amqp://user:password@rabbitmq:5672/vhost
Шаг 2. Генерация .htpasswd для панелей
На этом же этапе создаются файлы basic-auth для nginx. Они нужны, чтобы закрыть административные панели дополнительным браузерным логином и паролем.
Для phpMyAdmin используется отдельный логин:
# Для phpMyAdmin — отдельный логин
mkdir -p gateway/docker/production/nginx/auth
echo "${PMA_AUTH_USER}:$(openssl passwd -apr1 "${PMA_AUTH_PASSWORD}")" \
> gateway/docker/production/nginx/auth/.htpasswd
Для RabbitMQ используются те же реквизиты, что и у самого RabbitMQ:
# Для RabbitMQ — используются те же реквизиты, что и у самого RMQ
mkdir -p gateway/docker/production/nginx/auth
echo "${RABBITMQ_USER}:$(openssl passwd -apr1 "${RABBITMQ_PASSWORD}")" \
> gateway/docker/production/nginx/auth/.htpasswd_rmq
Команда:
openssl passwd -apr1 "password"
создаёт хэш в формате Apache MD5, он же apr1. Nginx умеет читать такие хэши в файлах, подключённых через директиву auth_basic_user_file.
Файлы попадают в каталог:
gateway/docker/production/nginx/auth/
После этого они синхронизируются на сервер вместе с кодом.
В Git эти файлы хранить не нужно. Каталог с auth-файлами или сами файлы должны быть добавлены в .gitignore:
gateway/docker/production/nginx/auth/.htpasswd
gateway/docker/production/nginx/auth/.htpasswd_rmq
Иначе можно случайно закоммитить хэши паролей. Хэш — это не пароль в открытом виде, но публиковать его всё равно нельзя: его можно пытаться подбирать офлайн.
Шаг 3. rsync на сервер
После подготовки .env и .htpasswd код синхронизируется на сервер.
Пример команды:
rsync -avz --delete \
--exclude='.git' \
--exclude='api/vendor' \
--exclude='frontend/node_modules' \
./ deploy@app-server:/srv/project/
Ключ --delete означает, что файлы, удалённые в рабочей копии CI, будут удалены и на сервере. Это полезно для чистой синхронизации, потому что сервер не превращается в склад старых файлов.
Но у --delete есть важное следствие: всё, что должно жить только на сервере, не должно попадать в директорию, которую полностью перетирает rsync. Например, runtime-данные, uploads, volume-данные базы и файлы, создаваемые приложением, лучше хранить вне каталога синхронизации или подключать через Docker volumes.
В этом варианте из синхронизации исключены:
--exclude='.git'
--exclude='api/vendor'
--exclude='frontend/node_modules'
Это логично: .git на production-сервере не нужен, PHP-зависимости и Node.js-зависимости обычно устанавливаются или собираются внутри образов, а не копируются как локальные каталоги разработчика.
Шаги 4–8. Деплой на сервере
После синхронизации CI заходит на сервер и выполняет команды Docker Compose.
Сначала собираются новые образы:
# Собираем новые образы. Старые контейнеры в этот момент ещё работают.
docker compose -f docker-compose-production.yml build
Этот шаг не переключает приложение сам по себе. Он только собирает новые версии образов. Старые контейнеры продолжают работать до команды up.
Дальше контейнеры обновляются:
# Переключаем контейнеры
docker compose -f docker-compose-production.yml up -d --remove-orphans
up -d запускает контейнеры в фоне. Если конфигурация сервиса или образ изменились, Compose пересоздаёт соответствующие контейнеры. Ключ --remove-orphans удаляет контейнеры от сервисов, которые раньше были в compose-файле, но теперь удалены из конфигурации.
Даунтайм здесь обычно минимальный, но не стоит называть такую схему полноценным zero-downtime деплоем. Если контейнер API пересоздаётся в единственном экземпляре, короткий разрыв возможен. Для настоящего zero-downtime нужны дополнительные механизмы: несколько реплик, healthcheck, корректное переключение трафика и аккуратная стратегия обновления.
После запуска контейнеров выполняются миграции:
# Ждём MySQL и гоняем миграции
docker compose -f docker-compose-production.yml run --rm api-php-cli \
php bin/console doctrine:migrations:migrate -n
Здесь используется отдельный CLI-контейнер приложения. Это правильный подход: миграции запускаются в том же окружении, что и приложение, с теми же переменными окружения и зависимостями.
Перед reload nginx лучше проверять конфигурацию:
# Проверяем конфигурацию nginx
docker compose -f docker-compose-production.yml exec gateway nginx -t
# Перечитываем конфиг nginx без полного перезапуска контейнера
docker compose -f docker-compose-production.yml exec gateway nginx -s reload
nginx -s reload перечитывает конфигурацию без остановки всего контейнера. Но если конфигурация невалидна, reload может привести к проблемам. Поэтому nginx -t перед reload — простая и полезная страховка.
В конце масштабируются воркеры:
# Масштабируем воркеры
docker compose -f docker-compose-production.yml up -d \
--scale api-workers=5 \
--scale log-workers=1 \
--no-recreate
Ключ --scale задаёт количество контейнеров для конкретного сервиса. Например, здесь поднимается пять экземпляров api-workers и один экземпляр log-workers.
Ключ --no-recreate говорит Compose не пересоздавать уже существующие контейнеры, если это не требуется. Для воркеров это удобно: можно поменять количество реплик без лишнего пересоздания того, что уже работает.
Часть 2. Как подключены phpMyAdmin и RabbitMQ
Сетевая изоляция
Все контейнеры находятся во внутренней Docker-сети, условно назовём её backend.
Снаружи открыты только порты 80 и 443, и оба слушает gateway — контейнер с nginx.
Internet → 443 → gateway (nginx) → internal Docker network
├── frontend
├── api-php-fpm
├── phpmyadmin:80
└── rabbitmq:15672
phpMyAdmin и RabbitMQ не имеют опубликованных наружу портов. Добраться до них можно только через gateway.
Это важный момент безопасности. Даже если кто-то знает внутреннее имя контейнера или стандартный порт RabbitMQ Management, напрямую из интернета он туда не попадёт.
Конфиг nginx для phpMyAdmin
Пример файла pma.conf:
server {
listen 443 ssl;
server_name pma.example.com;
# Basic auth — запрашивает логин/пароль перед любым запросом
auth_basic "Admin Area";
auth_basic_user_file /etc/nginx/auth/.htpasswd;
location / {
proxy_pass http://phpmyadmin;
}
}
Как это работает:
- Браузер идёт на
https://pma.example.com. - Nginx показывает браузерный диалог «введите логин/пароль».
- Nginx сверяет введённые данные с файлом
/etc/nginx/auth/.htpasswd. - Если данные не совпали — nginx отдаёт
401 Unauthorized. - Если данные совпали — nginx проксирует запрос в контейнер
phpmyadminна порт80.
Запись:
proxy_pass http://phpmyadmin;
работает за счёт Docker DNS. Имя phpmyadmin должно совпадать с именем сервиса или сетевым alias в Docker Compose, а сам nginx-контейнер должен находиться в той же Docker-сети.
Если nginx находится не в той же сети, имя контейнера не разрешится, и nginx выдаст ошибку вида:
host not found in upstream "phpmyadmin"
Конфиг nginx для RabbitMQ
Пример файла rmq.conf:
server {
listen 443 ssl;
server_name rmq.example.com;
auth_basic "Admin Area";
auth_basic_user_file /etc/nginx/auth/.htpasswd_rmq;
location / {
proxy_pass http://rabbitmq:15672;
}
}
Здесь логика такая же, как у phpMyAdmin, но upstream другой:
proxy_pass http://rabbitmq:15672;
Порт 15672 — это HTTP-интерфейс RabbitMQ Management. Он используется для веб-интерфейса и HTTP API управления RabbitMQ.
В описанной схеме используется management-вариант образа RabbitMQ:
rabbitmq:3.13.2-management
У такого образа management plugin уже включён, поэтому веб-интерфейс RabbitMQ доступен внутри Docker-сети на порту 15672.
Почему для RabbitMQ получается two-layer auth
У RabbitMQ Management есть собственная авторизация: пользователь вводит логин и пароль RabbitMQ уже в самой форме RabbitMQ Management UI.
Но перед этим стоит nginx basic auth.
Получается два слоя:
Браузер
│
▼
nginx basic auth
│
▼
RabbitMQ Management auth
│
▼
RabbitMQ Management UI
На практике пользователь проходит два логина:
- Сначала браузерный basic-auth диалог от nginx.
- Потом форму авторизации RabbitMQ Management.
Зачем это нужно:
- RabbitMQ Management UI не светится в интернете без предварительной авторизации nginx.
- Сканеры и случайные посетители не видят форму RabbitMQ напрямую.
- Даже если пароль RabbitMQ известен, нужно пройти ещё внешний слой basic auth.
Важно: nginx basic auth не заменяет авторизацию RabbitMQ. Это именно дополнительный слой перед management UI.
Часть 3. SSL-сертификаты
SSL-сертификаты Let’s Encrypt покрывают все публичные имена проекта:
example.com
www.example.com
api.example.com
rmq.example.com
pma.example.com
Рядом с основными контейнерами работает certbot. Он проверяет срок действия сертификата по расписанию и обновляет сертификаты автоматически.
Nginx читает сертификаты из общего volume, например:
/etc/letsencrypt/live/example.com/fullchain.pem
/etc/letsencrypt/live/example.com/privkey.pem
или из другого пути внутри контейнера, если volume примонтирован иначе.
Общая схема такая:
certbot → обновляет сертификаты → shared volume → nginx читает сертификаты
После успешного обновления сертификата nginx должен перечитать сертификаты. Для этого нужен reload nginx. В контейнерной схеме это обычно решается одним из вариантов:
- deploy-hook у certbot, который вызывает reload nginx;
- отдельный скрипт обслуживания;
- команда reload внутри CI/CD;
- периодический безопасный reload nginx после проверки конфигурации.
Минимальная проверка автоматического продления:
certbot renew --dry-run
Если certbot работает в контейнере, команда будет выглядеть с учётом Docker Compose, например:
docker compose -f docker-compose-production.yml run --rm certbot renew --dry-run
После обновления сертификата полезно проверять, какой сертификат реально отдаёт nginx:
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -dates -issuer -subject
Это помогает поймать ситуацию, когда certbot сертификат обновил, но nginx продолжает отдавать старый сертификат из памяти.
Часть 4. Экстренное ручное управление
Автоматический деплой закрывает обычный сценарий, но всегда нужен понятный аварийный путь: как посмотреть логи, перезапустить сервисы, выполнить миграции или сделать полный передеплой.
Полный передеплой
Если что-то сломалось, можно запустить отдельный workflow вручную:
GitHub → Actions → Full Redeploy → Run workflow
Такой workflow может называться redeploy.yml.
Его задача — не просто обновить изменившиеся контейнеры, а полностью пересобрать окружение:
# Остановить контейнеры
docker compose -f docker-compose-production.yml down
# Собрать образы с нуля
docker compose -f docker-compose-production.yml build --no-cache
# Поднять окружение заново
docker compose -f docker-compose-production.yml up -d --remove-orphans
Такой режим полезен, если есть подозрение на битый слой Docker image, конфликт старых контейнеров или некорректное состояние после неудачного деплоя.
Но это более жёсткая операция, чем обычный deploy: она может дать больший даунтайм, поэтому её лучше использовать именно как аварийный инструмент.
Посмотреть логи gateway
Чтобы посмотреть логи nginx-gateway:
docker compose -f docker-compose-production.yml logs -f gateway
Эта команда нужна, если:
- не открывается frontend;
- API отдаёт ошибку через nginx;
- не работает phpMyAdmin;
- не открывается RabbitMQ Management UI;
- есть подозрение на ошибку upstream;
- nginx не может найти контейнер по имени;
- не применился новый конфиг.
Типовые ошибки, которые можно увидеть в логах:
host not found in upstream
connect() failed
upstream timed out
SSL_do_handshake() failed
no user/password was provided for basic authentication
user "..." was not found in "/etc/nginx/auth/.htpasswd"
Посмотреть логи RabbitMQ
Для RabbitMQ:
docker compose -f docker-compose-production.yml logs -f rabbitmq
Эта команда помогает проверить:
- стартовал ли RabbitMQ;
- подключились ли приложения к брокеру;
- создался ли нужный vhost;
- есть ли ошибки авторизации;
- работают ли очереди;
- нет ли проблем с диском или памятью.
Зайти в MySQL вручную через приложение
Для простой проверки подключения к базе можно выполнить SQL-запрос через Symfony CLI-контейнер:
docker compose -f docker-compose-production.yml run --rm api-php-cli \
php bin/console doctrine:query:sql "SELECT 1"
Если команда возвращает результат, значит:
- CLI-контейнер приложения стартует;
- переменные окружения прочитаны;
- DSN до базы корректный;
- MySQL доступен из Docker-сети;
- Doctrine может выполнить запрос.
Для диагностики это часто удобнее, чем заходить напрямую в MySQL-контейнер, потому что проверяется именно путь приложения до базы.
Что важно проверить перед публикацией такой схемы в production
Ниже не новая архитектура, а чек-лист к уже описанной схеме. Он помогает не пропустить мелочи, из-за которых деплой обычно ломается в самый неудобный момент.
1. .env не должен попадать в Git
Проверьте .gitignore:
.env
.env.*
Если нужны шаблоны, лучше хранить только пример:
.env.example
В нём должны быть имена переменных без реальных секретов.
2. Auth-файлы не должны попадать в Git
Проверьте:
gateway/docker/production/nginx/auth/.htpasswd
gateway/docker/production/nginx/auth/.htpasswd_rmq
Можно исключить весь каталог:
gateway/docker/production/nginx/auth/
Но тогда нужно убедиться, что CI создаёт каталог перед записью файлов:
mkdir -p gateway/docker/production/nginx/auth
3. Nginx должен быть в той же Docker-сети, что phpMyAdmin и RabbitMQ
Если в конфиге nginx написано:
proxy_pass http://phpmyadmin;
proxy_pass http://rabbitmq:15672;
то контейнер gateway должен видеть сервисы phpmyadmin и rabbitmq по Docker DNS.
Проверить можно так:
docker compose -f docker-compose-production.yml exec gateway getent hosts phpmyadmin
docker compose -f docker-compose-production.yml exec gateway getent hosts rabbitmq
Если имена не резолвятся, проблема обычно в сетях Docker Compose: сервисы находятся в разных сетях или nginx не подключён к нужной сети.
4. Перед reload nginx нужно выполнять nginx -t
Безопасный порядок:
docker compose -f docker-compose-production.yml exec gateway nginx -t
docker compose -f docker-compose-production.yml exec gateway nginx -s reload
Если nginx -t падает, reload делать нельзя. Сначала нужно исправить конфигурацию.
5. Certbot нужно проверять dry-run’ом
Команда:
certbot renew --dry-run
или контейнерный вариант:
docker compose -f docker-compose-production.yml run --rm certbot renew --dry-run
должна успешно проходить после первичной настройки и после изменений в nginx, DNS, firewall или reverse proxy.
6. RabbitMQ Management лучше не публиковать напрямую
В этой схеме management UI доступен только через nginx и basic auth. Это лучше, чем открывать порт 15672 наружу через Docker ports.
То есть для production-сценария лучше избегать такого варианта:
ports:
- "15672:15672"
Если порт опубликован наружу, RabbitMQ Management UI будет доступен напрямую, в обход nginx basic auth.
Итог
Схема строится вокруг простой идеи: весь внешний трафик проходит через один nginx-gateway, а внутренние сервисы остаются закрытыми в Docker-сети.
При push в main GitHub Actions:
- Создаёт
.envизvarsиsecrets. - Генерирует
.htpasswdдля phpMyAdmin и RabbitMQ. - Синхронизирует код на сервер через
rsync. - Собирает Docker-образы.
- Обновляет контейнеры через Docker Compose.
- Запускает миграции.
- Перечитывает nginx.
- Масштабирует воркеры.
phpMyAdmin и RabbitMQ при этом не открываются напрямую в интернет. Доступ к ним идёт только через nginx:
pma.example.com → nginx basic auth → phpMyAdmin
rmq.example.com → nginx basic auth → RabbitMQ Management auth → RabbitMQ UI
Для production это нормальная и понятная схема: секреты не лежат в Git, административные панели закрыты дополнительным слоем авторизации, SSL обновляется автоматически, а аварийное управление остаётся доступным через отдельные команды и ручной workflow.
Главное — не забыть технические проверки: .gitignore для секретов, nginx -t перед reload, certbot renew --dry-run, отсутствие прямой публикации 15672 наружу и корректную Docker-сеть между gateway, phpmyadmin и rabbitmq.
// Reviews
Отзывы по теме
Опыт сотрудничества оставил максимально позитивное впечатление, в первую очередь профессионализмом и подходом к решению возникающих проблем.
Опыт сотрудничества оставил максимально позитивное впечатление, в первую очередь профессионализмом и подходом к решению возникающих проблем.
Jitsi meet: персональный zoom, настройка jitsi meet в docker и на VPS
11.11.2025 · ★ 5/5
Была задача наладить работу n8n, redis и базы данных. Заказывал раньше у другого исполнителя, постоянно все ломалось. Заказал у Михаила, на следующий же день все стало работать быстро, как часы!
Была задача наладить работу n8n, redis и базы данных. Заказывал раньше у другого исполнителя, постоянно все ломалось. Заказал у Михаила, на следующий же день все стало работать быстро, как часы!
N8n установка на ваш vps сервер. Настройка n8n, docker, ai, telegram
24.09.2025 · ★ 5/5
Спасибо за быструю и хорошую работу. Все сделали оперативно и так как нужно!
Спасибо за быструю и хорошую работу. Все сделали оперативно и так как нужно!
N8n установка на ваш vps сервер. Настройка n8n, docker, ai, telegram
06.09.2025 · ★ 5/5
Быстрое решение проблемы, всем рекомендую Михаила в качестве исполнителя! Пробовал собрать аналогичную конфигурацию самостоятельно и через советы нейросетей, в результате куча потраченных сил и средств (из-за простоя сервера). Так что мой совет в итоге - обращайтесь к профессионалам, это выйдет дешевле =) Спасибо Михаилу за профессионализм.
Быстрое решение проблемы, всем рекомендую Михаила в качестве исполнителя! Пробовал собрать аналогичную конфигурацию самостоятельно и через советы нейросетей, в результате куча потраченных сил и средств (из-за простоя …
N8n установка на ваш vps сервер. Настройка n8n, docker, ai, telegram
25.08.2025 · ★ 5/5
Михаил выполнил настройку очередного VPS. Быстро, профессионально обходя определенные ограничение хостинг провайдеров.
Михаил выполнил настройку очередного VPS. Быстро, профессионально обходя определенные ограничение хостинг провайдеров.
N8n установка на ваш vps сервер. Настройка n8n, docker, ai, telegram
12.08.2025 · ★ 5/5
Отличная работа, спасибо! Михаил профессионал своего дела, рекомендую!
Отличная работа, спасибо! Михаил профессионал своего дела, рекомендую!
N8n установка на ваш vps сервер. Настройка n8n, docker, ai, telegram
03.07.2025 · ★ 5/5
// Contact
Нужна помощь?
Свяжись со мной и я помогу решить проблему
// Related