Устройство systemd: юниты, targets и управление сервисами¶
systemd — система инициализации (PID 1) практически в каждом mainstream-дистрибутиве Linux сегодня (Ubuntu, Debian, семейство RHEL/Fedora/CentOS, SUSE, Arch). Она запускает сервисы, управляет зависимостями между ними и первой попадает под подозрение — справедливо или нет — когда «сервер не поднялся после перезагрузки».
Как это устроено¶
systemd организует всё в юниты. Самые распространённые типы юнитов:
| Тип юнита | Назначение |
|---|---|
.service | Долгоживущий демон или разовая команда |
.target | Именованная точка синхронизации / группа юнитов (замена старых SysV runlevels) |
.socket | Сервисы с активацией по сокету — systemd слушает сокет и запускает сервис при первом подключении |
.timer | Замена cron от самого systemd |
.mount / .automount | Монтирование файловых систем |
Юниты — обычные текстовые файлы, обычно в /etc/systemd/system/ (локальные переопределения/ собственные юниты) или /lib/systemd/system/ (юниты, установленные пакетами дистрибутива).
Targets заменяют runlevels¶
$ systemctl list-units --type=target --no-pager
UNIT LOAD ACTIVE SUB DESCRIPTION
basic.target loaded active active Basic System
getty.target loaded active active Login Prompts
graphical.target loaded active active Graphical Interface
local-fs.target loaded active active Local File Systems
...
graphical.target и multi-user.target — современные аналоги старых SysV runlevel 5 и 3. systemctl get-default показывает, какой из них загружается по умолчанию; systemctl set-default multi-user.target переключает сервер на загрузку без графического интерфейса.
Просмотр состояния сервиса¶
$ systemctl status systemd-journald --no-pager -l
● systemd-journald.service - Journal Service
Loaded: loaded (/lib/systemd/system/systemd-journald.service; static)
Active: active (running) since Thu 2026-10-15 06:31:47 EEST; 5 days ago
Main PID: 3446425 (systemd-journal)
Status: "Processing requests..."
Tasks: 1 (limit: 19046)
Memory: 40.8M
CPU: 1min 19.843s
Как читать: Loaded показывает, был ли файл юнита успешно разобран и где он лежит; Active — реальное состояние во время работы (active (running), active (exited) для одноразовых юнитов, failed, inactive); Main PID — это то, за чем реально следит systemd — если этот процесс умрёт, а в юните указана директива Restart=, systemd перезапустит его автоматически.
Минимальный service-юнит¶
# /etc/systemd/system/myapp.service
[Unit]
Description=My Application
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/myapp
Restart=on-failure
RestartSec=5
User=myapp
[Install]
WantedBy=multi-user.target
After=network.target— это зависимость только по порядку. Она не гарантирует, что сеть уже реально пригодна для использования (DHCP может ещё согласовываться) — для этого нуженAfter=network-online.targetиWants=network-online.target, что на дистрибутивах с корректной реализацией является более сильной гарантией.Restart=on-failure— перезапуск только при ненулевом коде выхода или сигнале, не при чистомexit 0(Restart=alwaysперезапускает безусловно, даже после чистогоexit 0; явная остановка черезsystemctl stopавтоматически не перезапускается).WantedBy=multi-user.target— именно на это воздействуетsystemctl enable: создаётся симлинк, чтобы юнит запускался автоматически при достиженииmulti-user.targetна загрузке.
После создания или правки файла юнита systemd не видит его автоматически:
daemon-reload перечитывает файлы юнитов с диска; enable --now одновременно включает автозапуск на будущее и запускает сервис немедленно.
Чтение логов через journalctl¶
journalctl -u myapp.service --since "10 min ago"
journalctl -u myapp.service -f # следить за логом, как tail -f
journalctl -p err -b # только ошибки, текущая загрузка
Различия Ubuntu/Debian и RHEL-подобных систем¶
Механика systemd идентична во всех дистрибутивах — это один и тот же upstream-проект. Различия — в окружающих инструментах:
| Ubuntu/Debian | RHEL-подобные | |
|---|---|---|
| Юнит файрвола | ufw.service (обёртка над nftables) | firewalld.service |
| Настройка сети | netplan генерирует конфиг для systemd-networkd/NetworkManager | NetworkManager по умолчанию |
| Target по умолчанию | graphical.target на desktop-установках, multi-user.target на server | то же самое |
Практическое задание¶
- Создайте юнит
myapp.serviceиз примера выше, но укажите вExecStartбезопасный для тестирования/usr/bin/sleep infinityвместо реального приложения. Сначала удалите строкуUser=myapp(или создайте такую учётную запись командойsudo useradd -r -s /usr/sbin/nologin myapp), иначе юнит упадёт сstatus=217/USER. - Включите и запустите его, убедитесь, что он работает, и посмотрите его
Main PID. - Убейте процесс напрямую командой
kill -9 <PID>и понаблюдайте в другом терминале заjournalctl -u myapp -f, как systemd обнаруживает сбой и перезапускает сервис (благодаряRestart=on-failure).
Как проверить, что всё сделано правильно¶
systemctl status myappдолжен показатьActive: active (running)с новым PID послеkill -9, в течениеRestartSec(5 секунд в примере).journalctl -u myappдолжен показать строку о завершении процесса по сигналу, за которой следует новая запись «Started».
Типичные ошибки¶
- Правка файла юнита без последующего
daemon-reload. systemd продолжает использовать старую, закэшированную версию юнита, пока вы не перечитаете конфигурацию. - Использование
Restart=alwaysбез разумногоRestartSec. По умолчанию это 100 мс, поэтому падающий в цикле сервис перезапускается десять раз в секунду, нагружая CPU и забивая журнал, пока не сработает лимит частоты запусков systemd (по умолчанию 5 запусков за 10 секунд) и юнит не перейдёт вfailed. Всегда задавайтеRestartSec=явно и настройтеStartLimitBurst=/StartLimitIntervalSec=, чтобы прекратить попытки после N сбоев за промежуток времени. - Предположение, что
enableзапускает сервис.enableтолько настраивает запуск сервиса при следующей загрузке — он не запускает его сейчас. Используйтеenable --nowили отдельныйstart.
⚠ Опасные команды¶
systemctl set-default <target>на удалённом сервере: если задать неверный или GUI-ориентированный target на безголовой машине, после перезагрузки можно остаться без доступного приглашения входа — проверьте, что target существует и уместен (multi-user.target, неgraphical.target, для сервера), прежде чем применять.systemctl maskюнита, от которого зависит другой юнит, может породить запутанные каскадные сбои — маскировка (в отличие отdisable) делает запуск юнита невозможным даже как зависимости, пока явно не выполнитьunmask.
Связанные статьи¶
Контрольные вопросы¶
- В чём практическая разница между
Restart=on-failureиRestart=always? - Почему правка файла
.serviceне вступает в силу сразу, и какая команда это исправляет? - Что гарантирует
After=network.target, а что — нет, в отличие отAfter=network-online.target? - Как узнать, почему сервис не запустился, используя только
systemctlиjournalctl?