Table of Contents

Зачем объединять системные докеры для развертывания производства

Современная инфраструктура требует, чтобы контейнерные сервисы пережили неожиданные перезагрузки, сбои оборудования или обновления пакетов. В то время как Docker предоставляет политики перезапуска (]), эти политики работают только до тех пор, пока работает демон Docker. Systemd - система init, используемая Ubuntu, Debian, Fedora, CentOS и большинство современных дистрибутивов Linux - делает это дальше, управляя жизненным циклом самого демона Docker и может запускать контейнеры даже до того, как станет доступен сокет Docker. Преимущества использования системного включают:

  • Гарантированный заказ на запуск через директивы зависимостей (например, после network.target, после docker.service)
  • Унифицированная регистрация через , делая отладку простой
  • Тонкий контроль над ограничениями ресурсов (CPU, память, I/O) с использованием директив системных блоков
  • Автоматический перезапуск при отказе с настраиваемыми ограничениями задержки и разрыва
  • Поддержка активации сокетов и тайм-стартапа

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

Создание системного сервиса для одного контейнера Docker

Стандартный подход включает в себя написание файла сервисного блока, который вызывает команды Docker для запуска и остановки контейнера. Ниже мы шаг за шагом проходим процесс, начиная с базового примера, а затем охватывая общие производственные требования.

Шаг 1: Напишите файл блока обслуживания

Создайте файл с именем . Используйте следующий шаблон в качестве отправной точки:

[Unit]
Description=My Application Container
After=network-online.target docker.service
Wants=network-online.target
Requires=docker.service

[Service]
Restart=always
RestartSec=10
StartLimitBurst=3
ExecStartPre=-/usr/bin/docker kill myapp
ExecStartPre=-/usr/bin/docker rm myapp
ExecStart=/usr/bin/docker run --rm --name myapp \
 -e DB_HOST=10.0.1.50 \
 -e DB_PORT=5432 \
 -v /data/myapp:/app/data \
 -p 8080:8080 \
 myregistry/myapp:latest
ExecStop=/usr/bin/docker stop -t 10 myapp
ExecStopPost=-/usr/bin/docker rm myapp

[Install]
WantedBy=multi-user.target

Объяснение ключевых директив:

  • — гарантирует, что демон Докера работает перед запуском контейнера.
  • — если Docker остановлен, то и эта услуга прекращается.
  • — очищает любой оставшийся контейнер от предыдущего прогона (] префикс означает, что сбои здесь не смертельны).
  • — использует для автоматического удаления контейнера при его остановке.
  • — изящно останавливает контейнер тайм-аутом (10 секунд).
  • — перезапускает контейнер независимо от кода выхода.
  • — ждёт 10 секунд перед перезапуском.
  • — ограничивает перезапуск до 3 попыток за интервал (по умолчанию 10 секунд), чтобы избежать циклов перезапуска.

Шаг 2: Включите и запустите сервис

sudo systemctl daemon-reload
sudo systemctl enable myapp.service
sudo systemctl start myapp.service

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

Управление службой с помощью стандартных системных команд

После запуска сервиса вы контролируете его так же, как и любой другой системный сервис:

  • Начать:
  • Стоп:
  • Перезагрузить:
  • Статус:
  • Логи: [следовать за живыми журналами]

Расширенные шаблоны конфигурации

Развертывание продукции часто требует больше, чем простое . Ниже приведены общие улучшения, которые вы можете добавить в свои системные служебные файлы.

Переменные окружающей среды

Жесткое кодирование секретов или конфигурация в файле службы не рекомендуется.Вместо этого используйте отдельный файл окружения:

[Service]
EnvironmentFile=-/etc/myapp/env.conf
ExecStart=/usr/bin/docker run --rm --name myapp \
 --env-file /etc/myapp/env.conf \
 myregistry/myapp:latest

Префикс перед маршрутом означает, что служба запустится, даже если файл не существует (полезен при первоначальной настройке).

Сетевые и портовые связи

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

ExecStart=/usr/bin/docker run --rm --name web \
 --network=my-net \
 -p 443:443 \
 -v /etc/ssl/certs:/etc/ssl/certs:ro \
 myregistry/web:latest

Если вы используете пользовательскую сеть, убедитесь, что сеть существует до запуска службы. Вы можете добавить команду , чтобы создать ее:

ExecStartPre=/usr/bin/docker network create my-net

Межконтейнерные зависимости

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

[Unit]
Description=Web App Container
After=network-online.target docker.service mydb.service
BindsTo=mydb.service

связывает жизненный цикл веб-приложения с контейнером базы данных — если база данных останавливается, веб-приложение также останавливается.

Проверка здоровья и готовность

Проверки здоровья Docker могут быть интегрированы с системой для предотвращения преждевременной доступности услуг. Используйте со сценарием, который опросит конечную точку здоровья:

ExecStartPost=/usr/local/bin/wait-for-health.sh http://localhost:8080/health 30

Сценарий должен выходить из 0 только тогда, когда контейнер здоров. Если он не работает, система отмечает единицу как неисправную.

Ограничения ресурсов через систему

Вы можете ограничить процессор и память контейнера на уровне cgroup без собственных флагов ресурсов Docker.Это особенно полезно при запуске нескольких контейнеров на одном хосте:

[Service]
MemoryMax=512M
CPUQuota=50%

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

Управление несколькими контейнерами: Systemd vs. Docker Compose

Для небольшого количества контейнеров (например, 2-5) отдельные системные служебные файлы просты и поддерживаются. Однако, когда проект включает в себя множество взаимосвязанных сервисов, Docker Compose становится более удобным. Вы все еще можете использовать систематизированный для оркестровки всего стека Docker Compose, создав единый сервисный блок, который вызывает . Пример:

[Unit]
Description=My Application Stack
After=network-online.target docker.service
Requires=docker.service

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/myapp
ExecStart=/usr/local/bin/docker-compose up -d
ExecStop=/usr/local/bin/docker-compose down

[Install]
WantedBy=multi-user.target

Этот подход дает вам простоту композитного определения услуг в сочетании с управлением жизненным циклом системы. Обратите внимание, что используется, потому что выходит немедленно. сохраняет устройство в «активном» состоянии до тех пор, пока не будет назван.

Какой метод вы должны выбрать?

  • Индивидуальные системные сервисы — лучше всего подходят для устаревших приложений, сервисов со строгим заказом на запуск или когда вам нужны ограничения ресурсов на контейнер.
  • Docker Compose with systemd — идеально подходит для стеков микросервисов, где зависимости обрабатываются внутри Compose, и вы хотите, чтобы один блок управлял всей группой.

Устранение общих проблем

Даже при тщательной настройке вы можете столкнуться с проблемами. Ниже приведены частые подводные камни и их решения.

Сервис не работает с «Невозможно подключиться к демону Докера»

Обычно это означает, что сервис начинается до того, как сокет Docker будет готов. Убедитесь, что ваш блок содержит и . Также проверьте, включен ли демон Docker: .

Контейнеры перезапускаются в петле

Если контейнер выходит немедленно, система будет продолжать перезапускать его в соответствии с и . Проверьте журналы контейнеров с . Увеличьте (например, 30 секунд) и установите , чтобы предотвратить загруженный цикл.

Сервис не останавливается чисто

Неправильно сконфигурированный может оставить контейнер запущенным. Убедитесь, что использует правильное название контейнера. Используйте для принудительного удаления контейнера, если остановка не срабатывает.

Переменные окружающей среды не загружаются

Если вы используете , подтвердите, что файл существует и читается root. Избегайте проблем с цитированием - цитаты из системных полос из переменных значений. Для секретного впрыска рассмотрите возможность использования системных учетных данных или выделенного секретного менеджера.

Рассмотрение вопросов безопасности

Запуск контейнеров Docker через систему повышает некоторые показатели безопасности:

  • Всегда запускайте системный сервис как некорневой пользователь, если это возможно (используйте директивы и , но убедитесь, что пользователь имеет доступ к сокету Docker или запускается в режиме без корней).
  • Не используйте в системных единицах, если это не является абсолютно необходимым.
  • Используйте крепления для связывания только для чтения () всякий раз, когда контейнеру не нужно писать хосту.
  • В этом случае они будут иметь право на использование и , чтобы закалить устройство от побегов.
[Service]
ProtectSystem=strict
ReadWritePaths=/var/log/myapp
PrivateTmp=true
User=myappuser

Внешние ресурсы

Для дальнейшего чтения, обратитесь к этим официальным ссылкам:

Заключение

Интеграция с контейнерами Docker дает вам надежный автоматизированный механизм запуска, который легко интегрируется с остальной частью вашей системы Linux. Написав хорошо структурированные файлы сервисных блоков, вы можете контролировать порядок запуска, управлять зависимостями, устанавливать ограничения ресурсов и отслеживать журналы с помощью инструментов, которые ваша операционная команда уже знает. Независимо от того, выбираете ли вы отдельные службы для каждого контейнера или одного блока для оркестрации стека Compose, система обеспечивает надежность и предсказуемость, которые требуют производственные среды.

Начните с простого файла блока, тщательно протестируйте его, а затем нажмите на расширенные параметры, такие как файлы окружающей среды, проверки здоровья и закаливание безопасности. При таком подходе контейнеры Docker выживут после перезагрузки, сбоев и изменений конфигурации без ручного вмешательства, что позволит вашей команде сосредоточиться на создании приложений.