Задача администратора в этом уроке - перестать воспринимать systemd как черный ящик, который сам как-то поднимает систему, и научиться им управлять. Вы должны уметь читать юниты, понимать почему сервис стартует именно в этот момент, добавлять и убирать зависимости, переопределять чужие настройки не ломая пакет, и быстро находить кто тормозит загрузку. Разберем типы юнитов, модель зависимостей, параллелизм, инструменты systemctl и systemd-analyze, а также drop-in переопределения. В конце коротко глянем на SysVinit и initramfs - на экзамене они есть, но в 2026 это уже наследие.

Как это работает
systemd - это PID 1, первый процесс после ядра, и одновременно менеджер сервисов. Его базовая единица - юнит. Юнит описывает один управляемый объект, а суффикс в имени задает тип: .service (демон или одноразовая задача), .target (группа-точка синхронизации, аналог уровней запуска), .mount (точка монтирования), .socket (сокет для активации по запросу), .timer (запуск по расписанию вместо cron), а еще .device, .path, .swap, .slice. Файлы юнитов лежат в трех местах с разным приоритетом: /usr/lib/systemd/system - от пакетов, /etc/systemd/system - правки администратора (главнее), /run/systemd/system - временные, генерируемые в рантайме.
Ключевая идея systemd - зависимости, а не жесткий линейный порядок. Есть две оси, и их постоянно путают. Первая ось - требование (нужен ли мне другой юнит вообще): Wants - желательно, но если тот не поднимется, я все равно запущусь; Requires - жестко, если зависимость упала, меня тоже остановят; Requisite - зависимость должна быть уже активна, иначе сразу провал без попытки запуска; BindsTo - еще строже, я умираю вместе с ней. Вторая ось - порядок (кто раньше во времени): After и Before. Это разные вещи. After=foo.service не означает, что foo вообще будет запущен - оно лишь говорит, что ЕСЛИ оба в плане, мой старт ждет завершения старта foo. Чтобы и затребовать, и упорядочить, нужны обе директивы: Wants и After.
Параллелизм отсюда и берется. systemd строит граф зависимостей и запускает все, что не связано отношениями порядка, одновременно. Поэтому загрузка быстрая - не ждем по цепочке, как в SysVinit. Точки синхронизации - это targets. Например multi-user.target говорит готова многопользовательская система без графики, graphical.target добавляет дисплей-менеджер. Цель загрузки по умолчанию задается симлинком default.target.
Сокет-активация и таймеры - современная замена легаси. Вместо постоянно висящего демона .socket-юнит держит порт открытым и поднимает сервис при первом подключении (так systemd вытеснил inetd/xinetd). Таймеры с OnCalendar заменяют cron и дают логи в journald, зависимости и точный контроль. Это и есть актуальный 2026-подход.
Команды и примеры
Базовое управление одинаково в Debian 13/Ubuntu 24.04 и RHEL 10/Fedora 41+ - systemd везде один:
Код: Выделить всё
systemctl status nginx.service # состояние, последние строки лога
systemctl start|stop|restart nginx # запуск/останов/перезапуск
systemctl reload nginx # перечитать конфиг без рестарта
systemctl enable --now nginx # автозапуск + сразу включить
systemctl disable nginx # убрать из автозапуска
systemctl mask nginx # запретить запуск намертво (симлинк в /dev/null)
systemctl list-units --type=service # что сейчас активно
systemctl list-unit-files # все юниты и их состояние enable/disable
systemctl daemon-reload # перечитать изменения в файлах юнитов
Код: Выделить всё
systemctl get-default # текущая цель по умолчанию
systemctl set-default multi-user.target
systemctl isolate multi-user.target # переключиться сейчас (как смена runlevel)
systemctl list-dependencies graphical.target
Код: Выделить всё
systemd-analyze # суммарно: kernel + initrd + userspace
systemd-analyze blame # сервисы по времени старта, медленные сверху
systemd-analyze critical-chain # КРИТИЧЕСКАЯ цепочка: что реально держит загрузку
systemd-analyze critical-chain nginx.service
Drop-in переопределения - правильный способ менять чужой юнит. НЕ редактируйте файл в /usr/lib - его перезатрет обновление пакета. Вместо этого:
Код: Выделить всё
systemctl edit nginx.service
Код: Выделить всё
[Service]
# обнулить старое значение, иначе оно сложится со старым
ExecStart=
ExecStart=/usr/sbin/nginx -c /etc/nginx/custom.conf
LimitNOFILE=65536
Код: Выделить всё
systemctl cat nginx.service # итоговый юнит со всеми drop-in
systemctl show nginx.service -p ExecStart -p LimitNOFILE
Код: Выделить всё
# backup.timer
[Unit]
Description=Ночной бэкап
[Timer]
OnCalendar=*-*-* 03:30:00
Persistent=true
[Install]
WantedBy=timers.target
Разница по семействам в основном не в systemd, а в пакетах и фаерволе вокруг: Debian/Ubuntu - apt install, RHEL/Fedora - dnf install (dnf5 в Fedora). Сетевые сервисы и фаервол сегодня тоже под systemd и nftables (iptables - легаси-обертка).
Частые грабли
- Перепутать оси: написали Requires без After и удивляетесь гонке. Requires тянет запуск, но НЕ упорядочивает - нужен еще After.
- After= без Wants/Requires не запускает зависимость. Многие думают, что After сам поднимет сервис - нет, он только про порядок.
- Забыли systemctl daemon-reload после правки .service-файла - systemd работает по старой версии в памяти.
- Редактировали юнит в /usr/lib/systemd/system - обновление пакета затерло правки. Используйте drop-in.
- В drop-in задали ExecStart, не обнулив старый. Для списочных директив надо сначала ExecStart= (пустой), потом новое значение.
- enable у таймера повесили на .service вместо .timer - расписание не работает.
- Доверяете blame вместо critical-chain - чините юнит, который и так шел параллельно.
- mask путают с disable: mask делает запуск физически невозможным, даже по зависимости, что иногда ломает соседние сервисы.
- Выполните systemd-analyze blame и systemd-analyze critical-chain, сравните: найдите юнит из blame, которого нет в критической цепочке, и объясните почему.
- Создайте простой юнит /etc/systemd/system/hello.service с Type=oneshot и ExecStart=/usr/bin/echo привет, сделайте daemon-reload и запустите его, проверьте journalctl -u hello.
- Через systemctl edit hello.service добавьте drop-in с RemainAfterExit=true, посмотрите итог через systemctl cat.
- Сделайте hello.timer с OnCalendar=*:0/5 (каждые 5 минут), включите его и убедитесь через systemctl list-timers.
- Переключите систему в multi-user.target через isolate, затем верните default-цель обратно.
- Замаскируйте ненужный сервис, проверьте, что start выдает ошибку, затем размаскируйте.
- В чем разница между Wants и Requires и как каждый из них ведет себя при падении зависимости?
- Почему директива After не гарантирует, что указанный юнит вообще будет запущен?
- Чем вывод systemd-analyze blame принципиально отличается от critical-chain и какой из них использовать для оптимизации загрузки?
- Где правильно разместить переопределение чужого юнита и почему нельзя править файл в /usr/lib/systemd/system?
- Зачем при изменении ExecStart в drop-in сначала писать ExecStart= с пустым значением?
- Чем .socket-активация и .timer-юниты заменяют легаси xinetd и cron, и какие преимущества дают?