1. RabbitMQ в Compose: сервис, не религия
Общий принцип уже понятен: RabbitMQ в нашем стенде — ещё один сервис, а не новая архитектурная религия. Теперь нужен самый приземлённый слой задачи: добавить broker в compose.yaml так, чтобы было понятно, как он называется, какие порты кому нужны и как быстро проверить, что он вообще жив.
Если говорить совсем по-человечески, RabbitMQ в нашем стенде — это «почтовое отделение». Приложение приносит туда письма (сообщения), и оттуда же их забирает другой код (получатель). Но прежде чем мы начнём приносить письма, надо сначала… построить само отделение и повесить вывеску с адресом.
В Compose «вывеска» — это имя сервиса, например rabbitmq. Это имя автоматически становится DNS-именем внутри Compose-сети. То есть, когда контейнер нашего приложения будет пытаться подключиться к брокеру, он будет говорить не «иди на localhost», а «иди на rabbitmq». И это не магия: Compose действительно поднимает сеть, и в ней работает встроенный DNS.
При этом есть ещё один важный взгляд: у RabbitMQ есть «вход для приложений» и «вход для людей». Один порт нужен нашему Spring Boot сервису, чтобы подключиться по протоколу AMQP. Другой — нам с вами, чтобы открыть веб-интерфейс и убедиться, что брокер запустился, не умер и вообще не притворяется.
2. Образ RabbitMQ и параметры .env
Когда начинающий разработчик пишет Compose впервые, у него есть естественное желание сделать «быстро и навсегда»: прямо в compose.yaml указать image: rabbitmq:latest, а логин/пароль поставить guest/guest. Это работает ровно до момента, когда оно перестаёт работать (а оно перестаёт — обязательно). Поэтому мы идём по курсовому канону: образ фиксируем явно, а изменяемые параметры держим в .env / .env.example, чтобы YAML оставался читаемым и предсказуемым.
В курсе baseline RabbitMQ — линия 4.2.x, а для удобства мы используем management-вариант образа, чтобы веб-интерфейс был доступен сразу, без ручного включения плагинов. В реальном проекте это тоже частая практика для локального стенда: management UI — это очень полезный «фонарик», особенно когда вы только учитесь.
Пример .env.example для нашего репозитория можно держать таким (значения — учебные, не «production security»):
# Пример учебных переменных окружения для Compose (их читает docker compose при подстановке ${...})
RABBITMQ_IMAGE=rabbitmq:4.2-management
# Учебный пользователь, которого RabbitMQ создаст при первом старте контейнера
RABBITMQ_USER=catalog
# Учебный пароль (в проде так, конечно, не делаем)
RABBITMQ_PASSWORD=catalog
Эти RABBITMQ_USER и RABBITMQ_PASSWORD пригодятся дважды. Сначала ими стартует сам RabbitMQ-контейнер, а потом те же значения приложение получит как SPRING_RABBITMQ_USERNAME и SPRING_RABBITMQ_PASSWORD. Если на этом месте значения разъедутся, вы получите очень обидный сценарий: broker жив, UI открыт, а Spring Boot честно получает access refused.
Здесь есть маленькая, но важная деталь: мы не используем guest как пользователя. У RabbitMQ есть настройка по умолчанию, из-за которой guest часто разрешён только для loopback-доступа (то есть «только с localhost самого брокера»). А в Compose наше приложение — это другой контейнер, оно подключается «как будто по сети», и RabbitMQ может честно сказать: «Извините, guest только для местных». Итог — странные ошибки авторизации, которые выглядят как «я всё правильно написал». Поэтому для локального стека проще сразу задать своего пользователя.
Мы сейчас не углубляемся в безопасность (это будет позже и в другом масштабе), но даже на учебном уровне полезно привыкать к дисциплине: параметры окружения должны быть видимыми и контролируемыми, а не спрятанными по углам YAML и кода.
3. Сервис rabbitmq в compose.yaml
Самое приятное в Compose — его предсказуемость: если вы уже добавляли PostgreSQL и Redis, то RabbitMQ добавляется тем же способом. Мы описываем новый service, задаём image, публикуем нужные порты и объявляем переменные окружения для начальной настройки пользователя/пароля. Всё. Никаких шаманских бубнов, только YAML и лёгкая самоирония.
Минимальный блок сервиса RabbitMQ может выглядеть так:
services:
rabbitmq:
image: ${RABBITMQ_IMAGE} # Образ берём из .env, чтобы не хардкодить версию в YAML
environment:
RABBITMQ_DEFAULT_USER: ${RABBITMQ_USER} # Начальный пользователь в RabbitMQ
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASSWORD} # Начальный пароль в RabbitMQ
Это «сердце» конфигурации: Compose теперь знает, что в стенде есть сервис с именем rabbitmq. Но если оставить только это, мы не сможем удобно открыть UI из браузера на host-машине. Поэтому добавим публикацию портов. В учебном стенде обычно публикуют оба порта — и AMQP, и management UI, чтобы было проще дебажить и иногда подключаться к брокеру с host-машины утилитами.
services:
rabbitmq:
image: ${RABBITMQ_IMAGE} # Management-вариант образа нужен, чтобы UI был доступен сразу
ports:
- "5672:5672" # AMQP для приложений: host:container
- "15672:15672" # Management UI для человека: host:container
environment:
RABBITMQ_DEFAULT_USER: ${RABBITMQ_USER} # Эти креды будут работать и для AMQP, и для UI
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASSWORD} # Удобно: один источник правды — .env
Заметьте: мы пока не добавляли volumes для RabbitMQ. Для целей курса это нормально: RabbitMQ здесь — инфраструктурная зависимость, которую мы можем считать disposable. Нам важнее быстро поднимать/сносить стенд и проверять wiring, чем «бережно хранить очередь между перезапусками» (это отдельная вселенная, и мы туда сегодня не идём).
4. Порты RabbitMQ: 5672 и 15672 — это разные двери
На практике половина «RabbitMQ не работает» у новичков — это не проблема RabbitMQ, а проблема перепутанных портов. Поэтому остановимся на этом чуть подробнее, потому что это сэкономит вам много времени и немного нервных клеток (а нервные клетки, как известно, хоть и восстанавливаются, но не так быстро, как контейнеры).
RabbitMQ обычно интересен нам двумя интерфейсами. Первый — AMQP (порт 5672). Это то, куда подключается приложение. Второй — management UI (порт 15672). Это то, куда подключаетесь вы через браузер.
Чтобы совсем не запутаться, полезно держать вот такую таблицу в голове:
| Что мы делаем | Кто подключается | Куда подключается | Порт |
|---|---|---|---|
| Приложение отправляет/получает сообщения | Spring Boot (контейнер app) | RabbitMQ (контейнер rabbitmq) | 5672 |
| Мы проверяем, что брокер жив, и смотрим состояние | Мы в браузере на host-машине | Management UI | 15672 |
Ещё один важный момент: публикация портов (ports:) в Compose — это про доступ с host-машины. Для связи контейнер ↔ контейнер внутри Compose-сети публикация порта вообще не нужна. Если бы мы хотели максимально «чистый» стенд, то могли бы публиковать только 15672, потому что UI нужен извне, а 5672 можно оставить только внутри сети.
Но в учебном проекте обычно публикуют оба порта: это делает диагностику проще. Например, если у вас есть какая-то локальная утилита, которая умеет подключаться к RabbitMQ, вы сможете использовать localhost:5672. Главное — не перепутать, что localhost:15672 — это не AMQP, а «веб-админка для людей».
Чтобы закрепить, можно представить так: порт 5672 — это «служебный вход для сотрудников» (приложений), а 15672 — это «окошко с табличкой “Справочная”» (для человека). Если вы попытаетесь отправить письмо через справочную, вас вежливо посмотрят как на человека, который пришёл в банк с огурцом вместо паспорта.
5. Service name в Compose и container_name
Compose тем и хорош, что он позволяет говорить на уровне моделей: «в этом окружении есть сервис rabbitmq». И ровно это имя становится «адресом» внутри сети. Поэтому, когда наш контейнер app будет конфигурироваться на RabbitMQ, host будет не localhost и не IP-адрес, а просто rabbitmq.
С точки зрения Spring Boot это выглядит как параметры подключения, которые приходят через env vars. Даже до включения messaging полезно держать правильную форму в голове:
services:
app:
environment:
SPRING_RABBITMQ_HOST: rabbitmq # Внутри Compose-сети обращаемся по имени сервиса
SPRING_RABBITMQ_PORT: 5672 # AMQP-порт (не путать с 15672)
SPRING_RABBITMQ_USERNAME: ${RABBITMQ_USER} # Берём те же креды, что задали самому RabbitMQ
SPRING_RABBITMQ_PASSWORD: ${RABBITMQ_PASSWORD} # Иначе broker и приложение легко "разъедутся"
Здесь важно не увязнуть в двух похожих «мирах» переменных. SPRING_RABBITMQ_HOST — это env var, которую видит контейнер приложения. А ${RABBITMQ_IMAGE} — это переменная подстановки Compose, которая «срабатывает» ещё до запуска контейнеров, когда Compose читает YAML. Эти механизмы похожи, но живут в разных слоях. Мы это уже обсуждали на дне про .env, поэтому сейчас просто держим в голове: Compose подставляет одно, контейнер получает другое.
Отдельно скажу про container_name. Многие примеры в интернете любят писать:
container_name: rabbitmq
и кажется, что так «надежнее». В реальности это часто превращается в ловушку. Вы начинаете зависеть от именования контейнера, ломаете возможность легко масштабировать сервис, а ещё это просто не нужно, потому что Compose и так даёт вам имя сервиса как DNS-алиас. Для учебного проекта (и для большинства реальных dev-стендов) имя сервиса — это базовая и правильная опора.
Можно даже нарисовать простую схему, чтобы визуально закрепить, кто куда ходит:
flowchart TD Host["Host (ваш ноутбук)"] -->|браузер| UI["rabbitmq:15672 (management UI)"] App["app контейнер"] -->|AMQP| Broker["rabbitmq контейнер:5672"] Host -->|docker compose logs| Broker
Смысл схемы: приложение общается с брокером по внутренней сети Compose, а мы общаемся с UI через опубликованный порт.
6. Management UI: проверка запуска
Management UI RabbitMQ — штука очень человеческая. Docker и Compose любят консоль и логи, но мы с вами не обязаны страдать 24/7: иногда приятно открыть страницу и увидеть: «Node running», «Ports открыты», «Ошибок нет». Особенно в учебном контексте это помогает убрать тревожность уровня «а контейнер точно поднялся?».
После docker compose up (или up -d) вы сможете открыть UI в браузере по адресу:
http://localhost:15672
Логин/пароль будут теми, которые вы задали через RABBITMQ_DEFAULT_USER и RABBITMQ_DEFAULT_PASS (то есть через .env). Это важно: management UI — часть того же RabbitMQ, это не отдельный сервис. Если вы забыли пользователя/пароль, «магической кнопки “войти как админ”» там нет, и это правильно.
Что можно проверить в UI на минимальном уровне, не уходя в эксплуатационный RabbitMQ-трек:
- На главной странице (обычно вкладка Overview) видно, что брокер запущен и жив.
- Можно зайти в раздел Connections и позже увидеть, подключилось ли приложение.
- Можно зайти в раздел Queues and Streams и позже увидеть очередь, когда мы её объявим в приложении.
Здесь очень важно не сделать логическую ошибку: «UI открылся → значит приложение подключилось». UI всего лишь говорит: «RabbitMQ запустился и веб-порт доступен». Приложение может при этом смотреть вообще не туда (например, на localhost внутри контейнера), и UI вас никак не спасёт. Поэтому management UI — это быстрый индикатор «брокер жив», но не финальная гарантия «вся интеграция работает».
7. Команды запуска и проверки
Когда окружение становится multi-container, хочется иметь короткий «ритуал проверки», который не превращается в час расследования. И здесь Docker CLI снова становится вашим другом. Главное — не пытаться выучить весь справочник команд наизусть: лучше освоить несколько, но понимать, что именно они отвечают.
Запуск брокера можно делать как частью полного стека, так и отдельно. Например, если вы хотите сначала убедиться, что RabbitMQ вообще поднимается, можно поднять только его:
docker compose up -d rabbitmq # Поднимаем только сервис rabbitmq в фоне
docker compose ps # Проверяем статус контейнеров/сервисов
Если всё хорошо, вы увидите, что сервис rabbitmq в состоянии Up (или похожем). Дальше почти всегда стоит сразу посмотреть логи, потому что именно там живут первые сигналы «всё ок / всё не ок»:
docker compose logs -f rabbitmq # Следим за логами именно RabbitMQ (удобно при старте)
Как выглядят «нормальные логи»? Они обычно шумные, но вы хотя бы увидите, что брокер стартует, поднимает плагины и слушает порты. Если хочется прям короткий «пинг» внутри контейнера, у RabbitMQ в образе обычно есть утилиты диагностики. Например:
docker compose exec rabbitmq rabbitmq-diagnostics -q ping
# Если команда завершилась без ошибок — брокер внутри контейнера "жив"
Тут даже не так важно, что именно делает rabbitmq-diagnostics. Важно, что у вас появляется простой бинарный сигнал: команда завершилась успешно — значит брокер отвечает.
И ещё один практический момент: если management UI не открывается, не спешите переписывать YAML. Сначала проверьте очевидное: сервис поднят, порты опубликованы, и порт на host-машине не занят кем-то другим. Конфликт портов — абсолютно реальная штука, особенно если у вас уже стоит RabbitMQ локально или вы когда-то поднимали его раньше.
8. Типичные ошибки RabbitMQ в Compose
Ошибка №1: SPRING_RABBITMQ_HOST=localhost внутри Compose.
Это та самая классика, которая преследует людей, пока они не примут новую реальность: localhost внутри контейнера — это сам контейнер, а не «мой ноутбук» и не «другой сервис». Если приложение указывает localhost, оно пытается найти RabbitMQ внутри себя, не находит и падает с ошибками подключения. В Compose правильный host — это имя сервиса, то есть rabbitmq.
Ошибка №2: перепутан порт 5672 и порт 15672.
Порт 15672 — это веб-интерфейс. Если вы укажете его как порт брокера для приложения, Spring Boot будет пытаться говорить по AMQP туда, где ждут HTTP. Получится примерно как попытка заказать пиццу по номеру пожарной части: вам ответят, но разговор будет странный. Для приложения нужен 5672.
Ошибка №3: использование пользователя guest/guest и внезапные запреты на подключение.
Во многих статьях guest/guest выглядит как идеальный учебный вариант. Но RabbitMQ нередко ограничивает пользователя guest доступом только с loopback. В Docker Compose приложение подключается не с loopback брокера, а «из сети», и вы получаете ошибки авторизации, которые кажутся необъяснимыми. Проще сразу завести отдельного пользователя через RABBITMQ_DEFAULT_USER/PASS.
Ошибка №4: published ports есть, но management UI не открывается.
Тут обычно виноват не RabbitMQ, а банальный конфликт портов на host-машине. Если порт 15672 занят, Compose либо не поднимет сервис, либо поднимет, но без ожидаемой публикации. В такой ситуации гораздо полезнее посмотреть docker compose ps и логи, чем «перебирать заклинания» в YAML.
Ошибка №5: ожидание, что «UI открылся — значит интеграция готова».
Management UI — это проверка жизнеспособности брокера, но не доказательство того, что приложение подключилось, создало очередь и реально отправляет/получает сообщения. Если держать в голове эту границу, вы перестанете тратить время на «ну UI же работает, почему Spring падает?» и начнёте проверять конфигурацию приложения и активные профили.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ