systemd: от теории к продакшену — или как не превратить сервер в лотерею

Система инициализации — это не просто то, что запускается при старте ОС. Это основа того, как ваши сервисы будут жить в продакшене: перезапускаться, логироваться, взаимодействовать с другими процессами и реагировать на ошибки. В мире, где контейнеры и микросервисы уже стали нормой, а кластеры растут как на дрожжах, systemd перестал быть просто "ещё одной init-системой". Это инструмент, который либо упрощает жизнь DevOps-инженеров, либо превращает администрирование в рулетку.

Если вы когда-либо сталкивались с проблемами, когда сервис "сам собой перезапускался", логи терялись в /var/log/messages, а отладка занимала часы — эта статья для вас. Мы разберём не только что делает systemd, но и как это работает на практике, какие ловушки поджидают в продакшене, и как правильно конфигурировать его, чтобы избежать классических ошибок.


Проблема: почему init-системы — это не просто "запустить и забыть"

В старые добрые времена, когда Linux-серверы управлялись вручную через sysvinit или upstart, администрирование было проще — но и менее предсказуемо. Вот типичные сценарии, с которыми сталкиваются команды при переходе на systemd (или при его неправильной настройке):

  1. "Сервис упал, но systemd его не перезапустил" — Конфигурация Restart= неверно задана, и сервис остаётся мёртвым, пока кто-то не заметит.

  2. "Логи пропали — где они?" — Журналирование через journald не настроено, и важные события теряются в /dev/null.

  3. "Почему сервис завис на обновлении?" — Нет контроля над зависимостями между сервисами, и обновление одного пакета бьёт по всему кластеру.

  4. "Контейнеры и systemd — это враги?" — Попытка управлять контейнеризированными приложениями через systemd приводит к конфликтам с docker/podman или k8s.

  5. "Производительность падает — а systemd виноват?" — Неправильная настройка cgroups или systemd-networkd съедает ресурсы.

systemd решает эти проблемы, но только если его правильно использовать. А для этого нужно понимать не только синтаксис конфигов, но и внутреннюю логику работы.


Как systemd работает на самом деле: за кулисами

systemd — это не просто менеджер сервисов. Это экосистема, которая включает:

  • Систему инициализации (systemd как PID 1): управляет загрузкой, переключением пользователей, сессиями.
  • Менеджер сервисов (systemctl, .service-файлы): запуск, мониторинг, перезапуск приложений.
  • Журналирование (journald): централизованная система логирования с возможностью фильтрации и архивации.
  • Управление устройствами (udev): динамическое подключение/отключение hardware.
  • Сетевые сервисы (systemd-networkd): альтернатива NetworkManager или ifupdown.
  • Cgroups v2: управление ресурсами (CPU, память, диск) для процессов и сервисов.

Пример: как systemd запускает сервис

Когда вы пишете:

sudo systemctl start myapp.service

Происходит следующее:

  1. systemd читает файл /etc/systemd/system/myapp.service (или /usr/lib/systemd/system/).
  2. Проверяет зависимости (After=, Requires=).
  3. Запускает процесс с указанными параметрами (ExecStart=).
  4. Отслеживает его статус через PID-файл или SD_NOTIFY.
  5. Логирует все события в journald.

Если сервис падает, systemd:

  • Проверяет параметр Restart= (например, Restart=on-failure).
  • Если перезапуск разрешён, выполняет ExecStart снова.
  • Если нет — фиксирует ошибку в статусе (systemctl status myapp).

Практика: как правильно конфигурировать systemd

1. Базовый .service-файл: что обязательно указать

Вот минимально рабочий пример для сервиса на Go, который слушает на порту 8080:

[Unit]
Description=My Awesome Go App
After=network.target
Requires=network.target

[Service]
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/myapp -config=/etc/myapp/config.yaml
Restart=always
RestartSec=5s
TimeoutStopSec=30
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

Ключевые параметры:

  • Restart=always — перезапускать при любом крахе (кроме SIGKILL).
  • RestartSec=5s — пауза между перезапусками (чтобы не создать аварийную петлю).
  • TimeoutStopSec=30 — время ожидания graceful shutdown.
  • LimitNOFILE=65536 — лимит на открытые файлы (критично для сервисов с большим числом соединений).

2. Журналирование: как не потерять логи

По умолчанию journald хранит логи в /var/log/journal/. Чтобы настроить ротацию и фильтрацию:

[Service]
# ...
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapp

[Journal]
MaxFileSize=100M
MaxFiles=3

Как посмотреть логи:

# Последние 50 строк
journalctl -u myapp -n 50

# С фильтром по уровню (error, warn, info)
journalctl -u myapp --since "2024-01-01" -p err

# В реальном времени (как tail -f)
journalctl -u myapp -f

Ловушка: Если вы используете syslog (например, rsyslog), а не journald, логи могут дублироваться или теряться. Проверяйте SyslogIdentifier и настройки ForwardToSyslog=yes/no.


Типичные ошибки и как их избежать

Ошибка Причина Решение
Сервис не перезапускается после падения Restart=no или Restart=on-abnormal (не включает SIGSEGV) Установить Restart=always или Restart=on-failure
Логи не появляются в journalctl StandardOutput=inherit или SyslogIdentifier не задан Явно указать StandardOutput=journal и SyslogIdentifier
Зависание при обновлении Нет контроля над зависимостями (After=, BindsTo=) Прописать явные зависимости между сервисами
Утечка ресурсов (CPU/память) Не ограничены CPUQuota, MemoryLimit Добавить [Service]-секцию с CPUQuota=50% и MemoryLimit=1G
Конфликт с Docker/Podman Попытка управлять контейнеризированными процессами через systemd Использовать docker run --restart=always или podman generate systemd
Потеря состояния при перезагрузке Нет сохранения данных между перезапусками Использовать Type=notify + SD_NOTIFY или внешние хранилища (DB, файлы)
Ложные срабатывания перезапуска RestartSec слишком маленький, сервис не успевает завершиться Увеличить RestartSec до 10-30 секунд

Пример: полноценный workflow для продакшена

1. Развёртывание нового сервиса

# 1. Копируем конфиг в systemd
sudo cp /opt/myapp/myapp.service /etc/systemd/system/

# 2. Настраиваем пользователя (если нужно)
sudo useradd -r -s /bin/false myapp

# 3. Запускаем в тестовом режиме
sudo systemctl daemon-reload
sudo systemctl start myapp
sudo systemctl status myapp  # Проверяем логи и статус

# 4. Включаем автозапуск
sudo systemctl enable myapp

2. Обновление сервиса с нулевым даунтаймом

# 1. Применяем новые настройки
sudo systemctl edit myapp  # Для временных изменений
# Или редактируем файл напрямую

# 2. Перезапускаем с grace period
sudo systemctl reload-or-restart myapp

# 3. Проверяем логи на ошибки
journalctl -u myapp --since "5 minutes ago"

3. Отладка зависшего сервиса

# 1. Проверяем статус
systemctl status myapp

# 2. Смотрим core-dump (если есть)
journalctl -u myapp --show-stack

# 3. Принудительный перезапуск (если завис)
sudo systemctl kill --signal=SIGTERM myapp
sudo systemctl start myapp

# 4. Если не помогло — ручное вмешательство
ps aux | grep myapp  # Ищем PID
kill -9 <PID>        # Убиваем вручную

Вывод: systemd — это не волшебная палочка, но правильный инструмент

systemd не сделает ваш продакшен идеальным сам по себе. Но если вы понимаете его механизмы и правильно конфигурируете:

  1. Сервисы будут перезапускаться только когда нужно (без лишних рестартов).
  2. Логи не потеряются в море файлов и будут доступны для анализа.
  3. Зависимости между сервисами будут явно прописаны, что упрощает отладку.
  4. Ресурсы (CPU, память, диск) будут ограничены, предотвращая аварийные ситуации.

Практические рекомендации:

  • Всегда проверяйте systemctl status <service> после изменений.
  • Используйте journalctl как основной инструмент для мониторинга, а не /var/log/syslog.
  • Для контейнеров предпочитайте встроенные механизмы (docker run --restart=always), а не systemd.
  • В продакшене никогда не используйте Restart=always без анализа логики приложения — это может скрыть реальные проблемы.
  • Периодически проверяйте ротацию логов (journalctl --disk-usage) и очищайте старые записи.

Если вы дочитали до этого места — вы уже на шаг ближе к тому, чтобы ваши сервисы работали стабильно, а отладка занимала не часы, а минуты. А теперь — вперёд, настраивайте systemd правильно, и пусть ваш продакшен будет как швейцарские часы.