Урок по треку Linux 7 мин

systemd и журналы за 7 минут

Юниты и их типы, systemctl, рестарт сервисов, таймеры вместо cron, journalctl и ротация логов.

systemd это PID 1 почти во всех современных дистрибутивах: Ubuntu, Debian, RHEL и их клонах. Он запускает сервисы, следит за ними, перезапускает упавшие, собирает логи и заменяет cron таймерами.

Юниты

Всё, чем управляет systemd, это юниты. Тип виден по расширению:

  • .service: процесс или демон.
  • .socket: сокет, при обращении к которому стартует сервис.
  • .timer: расписание, запускает связанный .service.
  • .target: группа юнитов, точка синхронизации (аналог runlevel): multi-user.target, graphical.target.
  • .mount, .path, .slice и другие.

Где лежат файлы: /usr/lib/systemd/system (или /lib/systemd/system) из пакетов, /etc/systemd/system для своих и для переопределений. Файлы в /etc важнее.

Свой сервис

# /etc/systemd/system/api.service
[Unit]
Description=Orders API
After=network-online.target postgresql.service
Wants=network-online.target

[Service]
Type=simple
User=api
WorkingDirectory=/srv/api
EnvironmentFile=/etc/api/env
ExecStart=/usr/bin/node /srv/api/server.js
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload        # перечитать файлы юнитов
sudo systemctl enable --now api     # автозапуск + старт сейчас
systemctl status api                # состояние и последние строки лога
sudo systemctl restart api
sudo systemctl reload nginx         # перечитать конфиг без остановки (если поддерживается)
systemctl list-units --failed

enable создаёт симлинк в multi-user.target.wants/, то есть «запускать при загрузке». start запускает прямо сейчас. Это разные действия: часто сервис запущен, но не включён и не поднимется после ребута.

Зависимости и порядок

  • Wants=/Requires= говорят, что запустить вместе. Requires жёстче: при остановке или провале зависимости юнит тоже остановится.
  • After=/Before= говорят, в каком порядке. Без After юниты из Wants стартуют параллельно.

Классическая ошибка: написать Requires=postgresql.service без After= и удивляться, что приложение стартует раньше базы.

Типы сервиса

  • simple (по умолчанию): главный процесс это то, что в ExecStart, считается запущенным сразу.
  • exec: то же, но запущенным считается после успешного exec.
  • forking: классический демон, который форкается и выходит; нужен PIDFile.
  • oneshot: команда, которая выполняется и завершается (миграции, настройка).
  • notify: процесс сам сообщает о готовности через sd_notify.

Демон, который сам уходит в фон, под Type=simple systemd посчитает упавшим. Лучше запускать приложение на переднем плане.

Рестарты и лимиты

Restart=on-failure или always, RestartSec= пауза. Если сервис падает слишком часто (по умолчанию больше 5 раз за 10 секунд, StartLimitBurst и StartLimitIntervalSec), systemd перестаёт пытаться: статус start-limit-hit. Сброс: systemctl reset-failed api.

Ресурсы и безопасность прямо в юните: MemoryMax=, CPUQuota=, LimitNOFILE=, NoNewPrivileges=yes, ProtectSystem=strict, PrivateTmp=yes. Каждый сервис живёт в своей cgroup, поэтому systemd видит и останавливает все его процессы.

Переопределение без правки пакета

sudo systemctl edit nginx

Создаёт /etc/systemd/system/nginx.service.d/override.conf. Файл пакета не трогаем: обновление его не затрёт. Чтобы заменить, а не дополнить ExecStart, сначала пишут пустой ExecStart=.

Таймеры вместо cron

# backup.timer
[Timer]
OnCalendar=*-*-* 03:30:00
Persistent=true

[Install]
WantedBy=timers.target

Persistent=true запустит пропущенный запуск после включения машины. Плюсы перед cron: логи в журнале, зависимости, лимиты ресурсов, systemctl list-timers показывает следующий запуск.

journalctl

journalctl -u api                 # логи сервиса
journalctl -u api -f              # следить
journalctl -u api --since "1 hour ago"
journalctl -u api -b              # с последней загрузки
journalctl -p err -b              # только ошибки и хуже
journalctl -k                     # сообщения ядра (как dmesg)
journalctl --disk-usage
sudo journalctl --vacuum-size=500M

Всё, что сервис пишет в stdout и stderr, попадает в журнал с меткой юнита, поэтому писать логи в файлы приложению под systemd не обязательно. Журнал хранится постоянно, если существует /var/log/journal или стоит Storage=persistent в journald.conf. Иначе он в /run и пропадает после перезагрузки.

logrotate

Для файловых логов (/var/log/nginx/*.log) есть logrotate: ротация по размеру или времени, сжатие, хранение N копий. После ротации процесс должен открыть новый файл: postrotate с сигналом (nginx: USR1) или copytruncate, который копирует и обнуляет файл, но может потерять строки.

Частые ошибки

  • Забыли daemon-reload после правки юнита.
  • start без enable: после ребута сервис не поднялся.
  • Requires без After.
  • Правка файла в /lib/systemd/system вместо systemctl edit.
  • Сервис форкается в фон под Type=simple.

Что спросят на собесе

  • Чем enable отличается от start.
  • Как написать юнит для своего приложения с автоперезапуском.
  • Разница Wants, Requires и After.
  • Как посмотреть логи сервиса за последний час.
  • Почему таймеры systemd удобнее cron.

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.