Другое

Как настроить запуск Docker RabbitMQ до Nginx на хосте

Решение проблемы запуска Docker-контейнера RabbitMQ до сервиса Nemesida WAF в Nginx через systemd зависимости.

Как обеспечить запуск Docker-контейнера RabbitMQ до запуска сервиса Nginx на хосте?

Проблема:

  • Nginx работает как сервис на хосте
  • Nginx использует модуль Nemesida WAF, который подключается к RabbitMQ при запуске
  • После перезапуска машины Nginx запускается раньше, чем Docker-контейнер RabbitMQ
  • Это вызывает ошибки “Nemesida WAF: unable to connect to the RabbitMQ” в логах

Предпринятые действия:

  • Добавил “Before=nginx.service” в блок [Unit] файла rabbitmq-server.service
  • Это не сработало, так как контейнер Docker не видит nginx.service, установленный на хосте

Вопрос:
Как настроить согласованность запуска, чтобы Docker-контейнер RabbitMQ гарантированно стартовал до сервиса Nginx, установленного на хосте (не в Docker)?

Чтобы обеспечить запуск Docker-контейнера RabbitMQ до сервиса Nginx на хосте, нужно настроить зависимости на уровне systemd, чтобы Docker-демон запускался после RabbitMQ, а Nginx — после Docker-демона. Это можно сделать через создание пользовательского systemd сервиса для управления Docker-контейнером с правильными зависимостями или путем модификации существующих service-файлов.

Содержание

Проблема зависимости между сервисами

Основная проблема заключается в том, что Docker-контейнеры запускаются асинхронно относительно сервисов хоста. Когда система перезагружается, systemd запускает сервисы в соответствии с их приоритетами и зависимостями, но Docker-контейнеры управляются Docker-демоном, который имеет свою собственную логику запуска.

Nginx с модулем Nemesida WAF пытается подключиться к RabbitMQ во время своего запуска, но к этому моменту Docker-контейнер RabbitMQ еще не успел запуститься. Это приводит к ошибкам подключения.

Традиционный подход с добавлением Before=nginx.service в файл rabbitmq-server.service не работает, потому что:

  1. Контейнер RabbitMQ управляется Docker-демоном, а не напрямую systemd
  2. Service-файлы контейнеров не имеют прямого доступа к сервисам хоста
  3. Зависимости нужно настраивать на уровне сервисов, которые управляют Docker-контейнерами

Решение через пользовательский systemd сервис

Самый надежный способ — создать собственный systemd сервис для управления RabbitMQ контейнером с правильными зависимостями:

ini
# /etc/systemd/system/rabbitmq-docker.service
[Unit]
Description=RabbitMQ Docker Container
After=docker.service
Requires=docker.service
Before=nginx.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStartPre=/usr/bin/docker start rabbitmq
ExecStop=/usr/bin/docker stop rabbitmq

[Install]
WantedBy=multi-user.target

Затем нужно отключить автоматический запуск контейнера Docker и включить этот новый сервис:

bash
# Отключаем автоматический запуск контейнера
docker update --restart=no rabbitmq

# Включаем наш новый сервис
systemctl enable rabbitmq-docker.service

Этот подход создает явную зависимость:

  • After=docker.service — сервис запускается после Docker-демона
  • Before=nginx.service — сервис запускается до Nginx
  • Requires=docker.service — сервис требует наличия работающего Docker-демона

Использование Docker Compose с systemd интеграцией

Если вы используете Docker Compose, можно создать systemd сервис, который будет управлять сервисами Compose:

ini
# /etc/systemd/system/docker-compose-rabbitmq.service
[Unit]
Description=RabbitMQ Docker Compose Service
After=docker.service network.target
Requires=docker.service
Before=nginx.service

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/path/to/docker-compose
ExecStartPre=/usr/bin/docker-compose up -d rabbitmq
ExecStop=/usr/bin/docker-compose stop rabbitmq

[Install]
WantedBy=multi-user.target

Затем в вашем docker-compose.yml файле нужно правильно расставить зависимости:

yaml
version: '3.8'

services:
  rabbitmq:
    image: rabbitmq:3-management
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"]
      interval: 30s
      timeout: 10s
      retries: 3

  # Другие сервисы...

Настройка таймеров и задержек

Если предыдущие методы не подходят, можно использовать systemd таймеры для создания задержки:

ini
# /etc/systemd/system/rabbitmq-delay.service
[Unit]
Description=Delayed RabbitMQ Container Start
After=docker.service
Requires=docker.service

[Service]
Type=oneshot
ExecStart=/bin/sleep 10
ExecStart=/usr/bin/docker start rabbitmq

[Install]
WantedBy=multi-user.target

И настроить Nginx для запуска после этого сервиса:

ini
# В nginx.service добавляем
After=rabbitmq-delay.service

Этот метод менее надежный, так как использует фиксированную задержку, но может подойти в некоторых случаях.

Модификация Docker-демона

Более сложный подход — модифицировать systemd unit Docker-демона, чтобы он запускал контейнеры в нужном порядке:

bash
# Создаем override файл для docker.service
mkdir -p /etc/systemd/system/docker.service.d
cat > /etc/systemd/system/docker.service.d/override.conf << EOF
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
Restart=always
TimeoutStartSec=0

Затем нужно создать скрипт, который будет управлять порядком запуска контейнеров, и добавить его в вызов Docker-демона.

Альтернативные подходы

Использование готовых решений

Существуют готовые решения для управления Docker-контейнерами через systemd:

  • podman — более современная альтернатива Docker с нативной systemd интеграцией
  • systemd-nspawn — для контейнеризации на уровне systemd

Скриптовые решения

Создать скрипт-обертку, который будет проверять доступность RabbitMQ и только потом запускать Nginx:

bash
#!/bin/bash
# /usr/local/bin/wait-for-rabbitmq.sh

RABBITMQ_HOST="localhost"
RABBITMQ_PORT="5672"

until nc -zv "$RABBITMQ_HOST" "$RABBITMQ_PORT"; do
  echo "Waiting for RabbitMQ at $RABBITMQ_HOST:$RABBITMQ_PORT..."
  sleep 2
done

echo "RabbitMQ is ready"

Затем модифицировать systemd unit Nginx:

ini
# В nginx.service
ExecStartPre=/usr/local/bin/wait-for-rabbitmq.sh

Практическая реализация

Вот пошаговая инструкция для реализации решения через пользовательский systemd сервис:

  1. Создайте файл сервиса для RabbitMQ:
bash
sudo nano /etc/systemd/system/rabbitmq-docker.service
  1. Добавьте содержимое:
ini
[Unit]
Description=RabbitMQ Docker Container
After=docker.service network.target
Requires=docker.service
Before=nginx.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStartPre=/usr/bin/docker start rabbitmq
ExecStop=/usr/bin/docker stop rabbitmq
TimeoutStartSec=0

[Install]
WantedBy=multi-user.target
  1. Перезагрузите systemd:
bash
sudo systemctl daemon-reload
  1. Отключите автоматический запуск контейнера:
bash
docker update --restart=no rabbitmq
  1. Включите новый сервис:
bash
sudo systemctl enable rabbitmq-docker.service
  1. Перезапустите сервисы для проверки:
bash
sudo systemctl restart nginx

Этот подход гарантирует, что RabbitMQ контейнер будет запущен до Nginx, решая проблему с ошибками подключения Nemesida WAF.

Источники

  1. Official Docker Documentation - systemd integration
  2. systemd Unit Configuration - Dependencies
  3. Docker and systemd - Best Practices
  4. systemd Service Type - oneshot
  5. Docker Container Management

Заключение

  • Для решения проблемы запуска Docker-контейнера RabbitMQ до Nginx на хосте нужно создать пользовательский systemd сервис с правильными зависимостями
  • Наиболее надежный метод — использовать сервис типа oneshot с директивами After=docker.service и Before=nginx.service
  • Важно отключить автоматический запуск контейнера через Docker и управлять им через systemd
  • Альтернативные методы включают использование таймеров, скриптов ожидания или переход на системные контейнеры типа podman
  • Все решения нужно тестировать после перезагрузки системы для подтверждения правильной последовательности запуска

Рекомендуется начать с пользовательского systemd сервиса, так как он предоставляет наибольший контроль и предсказуемость в управлении зависимостями между контейнерами и сервисами хоста.

Авторы
Проверено модерацией
Модерация