SystemD
Материал упоминает устаревшие версии и утилиты: старая версия Ubuntu, старая версия Debian, init.d (сейчас — systemd). Проверьте актуальность для вашей системы.
systemd — система инициализации и менеджер служб Linux. Это первый процесс в системе (PID 1): ядро запускает именно его, а он поднимает всё остальное — службы, точки монтирования, сокеты, таймеры.
Слово «инициализация» тут узкое: systemd управляет не только запуском демонов, но и монтированием файловых систем, сетевыми сокетами, устройствами, расписанием задач, журналом и учётными сессиями. Это и главная причина споров вокруг него.
Зачем его придумали
Как было до него: SysVinit
Традиционная инициализация (init из System V) работала так:
- скрипты лежали в
/etc/init.d/; - порядок запуска задавался символическими ссылками в
/etc/rc3.d/:S20nginx,S21postgresql, а остановка —K20nginx; - номера в именах (
S20,S21) и были «зависимостями» — «запусти меня после того, у кого номер меньше»; - всё это были shell-скрипты, и запускались они по очереди.
Отсюда — проблемы, которые чувствовал каждый:
- медленно: пока не завершится одна служба, следующая не начнётся, хотя они не зависят друг от друга;
- нет настоящих зависимостей: «после» вместо «после и потому что»; служба могла стартовать, когда нужная ещё не готова;
- нет перезапуска: упавший демон никто не поднимал;
- нет учёта процессов: служба, оставившая «детей», при остановке не убивала их;
- нет ограничений ресурсов: сказать «этой службе не больше 512 МБ» было негде;
- нет единого формата: каждый скрипт писался как хотел автор.
Попытка исправить это — Upstart (Ubuntu, 2006): события («появилось устройство», «поднялся сетевой интерфейс») вместо номеров. Идея хорошая, но реализация ограниченная, и её не приняли вне Ubuntu.
systemd
В 2010 году Леннарт Пёттеринг (Red Hat) представил systemd. Идеи, которые он принёс:
- параллельный запуск: службы стартуют одновременно, а порядок задаётся зависимостями, а не номерами;
- активация по требованию: служба поднимается, когда к её сокету пришло соединение, а не «на всякий случай при загрузке»;
- юнит-файлы — один декларативный формат вместо сотен скриптов;
- cgroups — ядро отслеживает все процессы службы, поэтому остановка действительно останавливает всё, а лимиты ресурсов становятся возможными;
- перезапуск при падении (
Restart=on-failure); - журнал (
journald) вместо разрозненных текстовых логов.
Приняли его быстро: Fedora 15 (2011), RHEL 7, Debian 8 (2015), Ubuntu 15.04, Arch, openSUSE. Сегодня systemd стоит практически везде — даже там, где его когда-то не хотели.
Чем недовольны
Честно говоря, критика обоснована: systemd занял области, которые раньше были отдельными программами (udev, journald, logind, networkd, resolved, timesyncd). Кому-то это «монолит», кому-то — «слишком много прав у PID 1». Плюс журнал в двоичном формате и объём конфигурации. Но взамен получены скорость, надёжность и единый способ управлять системой.
Как systemd запускается
- Ядро запускает
/sbin/init(в большинстве дистрибутивов это ссылка на systemd) — процесс получает PID 1. - systemd читает юниты и строит граф зависимостей.
- Достигается
default.target— обычно ссылка наgraphical.targetилиmulti-user.target. - Дальше работают пользовательские сессии, службы и всё остальное.
Цели загрузки (targets) выстроены в цепочку:
sysinit.target → basic.target → multi-user.target → graphical.target
│
rescue.target / emergency.target
sysinit.target— самое необходимое: монтирование, устройства;basic.target— базовые службы и сокеты;multi-user.target— обычная система без графики (серверный режим);graphical.target— то же плюс графический вход;rescue.target— однопользовательский режим (корень на запись, минимум служб);emergency.target— только оболочка (корень только для чтения).
Юниты: из чего состоит systemd
Юнит — это описание одного ресурса: что запустить, от чего зависит, что делать при сбое. Юнит-файл состоит из секций:
[Unit]
Description=Мой сервис
After=network.target
[Service]
ExecStart=/usr/local/bin/my-service
Restart=on-failure
[Install]
WantedBy=multi-user.target
[Unit] — общие свойства и зависимости, [Service] — что и как запускать (у других типов — своя секция), [Install] — в какую цель включается.
Типы юнитов: назначение и что было бы без них
service — службы
Самый частый тип: демоны и одноразовые задачи. Пример — nginx.service, ssh.service.
Что даёт: автоматический запуск при загрузке, остановку, перезапуск при падении, зависимости, запуск от нужного пользователя, лимиты.
Без него: демоны пришлось бы поднимать руками — скриптом, из rc.local или по крону. Пользователь получил бы: после перезагрузки сайт не поднялся (забыли запустить), упавший веб-сервер лежит до утра (никто не перезапустил), а остановка «вручную» оставляет висеть дочерние процессы.
socket — сокеты
Юнит, который держит сокет и запускает службу при первом подключении (активация по требованию). Классический пример — sshd.socket.
Что даёт: служба не занимает память, пока её не позвали; соединение не теряется, пока служба стартует; если служба упала — следующее подключение поднимет её заново.
Без него: службы пришлось бы запускать всегда при загрузке, «на всякий случай». На сервере с десятками служб это лишняя память и лишнее время загрузки; а упавшая служба так и осталась бы лежать — подключиться к ней уже нельзя.
target — цели (группы)
Не действие, а точка синхронизации: «система дошла до этого состояния». Юниты включаются в цели, а цель может зависеть от других целей.
Что даёт: понятные состояния системы (multi-user, graphical, rescue) и точку, к которой привязываются остальные юниты.
Без него: не было бы ни «обычного режима», ни «аварийного» — пришлось бы держать список служб для каждого сценария вручную и включать их по одной. Переключение «в аварийный режим и обратно» стало бы ручной работой с длинным списком.
device — устройства
Юниты устройств systemd получает от udev, когда ядро сообщает о появлении оборудования. Имя — по шине и адресу, например dev-sda1.device.
Что даёт: можно сказать «запусти это после появления диска» или «останови, если диск исчез» (BindsTo=).
Без него: система не умела бы реагировать на подключение железа: включённый внешний диск не смонтировался бы сам, а служба, которой нужен диск, стартовала бы до его появления — и падала.
mount и automount — монтирование
mount — точка монтирования (systemd сам создаёт такие юниты из /etc/fstab), automount — монтирование по требованию: раздел подключается, когда к нему впервые обратились.
Что даёт: зависимости и порядок для файловых систем, монтирование по обращению, автоматический перезапуск неудачного монтирования.
Без него: /etc/fstab всё равно монтировал бы разделы, но без порядка и зависимостей: служба могла стартовать раньше, чем смонтирован её каталог данных, и упасть. Пользователь видел бы «случайные» падения при загрузке, зависящие от скорости диска. А монтирование по обращению (например, сетевой папки) пришлось бы настраивать через autofs.
swap — подкачка
Активация файлов и разделов подкачки (тоже генерируется из /etc/fstab).
Что даёт: подкачка включается в нужный момент и учитывается в зависимостях.
Без него: swap пришлось бы включать вручную (swapon) — то есть после перезагрузки система осталась бы без подкачки, и при нехватке памяти процессы убивались бы ядром (OOM) вместо ухода в swap.
path — слежение за путями
Юнит, который следит за файлом или каталогом и запускает что-то при изменении: PathExists=, PathChanged=, DirectoryNotEmpty=.
Что даёт: реакция на событие сразу, а не по расписанию.
Без него: «обработай файл, как только он появился» пришлось бы делать опросом по крону: файл лежал бы до следующей минуты, а при частом опросе система бы зря работала.
timer — таймеры
Расписание для юнитов. Умеет не только календарное время (OnCalendar=), но и относительное: «через 5 минут после загрузки» (OnBootSec=).
Что даёт: расписание с зависимостями, лог в общий журнал, «после загрузки, а не в фиксированный час», запуск пропущенной задачи при включении машины.
Без него: остался бы cron. Он работает, но не умеет «через 5 минут после загрузки», не знает зависимостей (задача может стартовать, когда нужная служба ещё не поднялась), не пишет в журнал systemd, а на выключенной машине пропущенная задача просто не выполнится — тогда как Persistent=true у таймера догоняет её при следующем включении.
slice — срезы (cgroups)
Иерархия групп для ограничения ресурсов: system.slice, user.slice, а внутри — свои срезы.
Что даёт: «этой группе служб — не больше 2 ГБ памяти и 50 % CPU»; видно, кто сколько потребляет.
Без него: ограничивать можно было бы только отдельные процессы (ulimit), и то по одному: служба с десятком дочерних процессов осталась бы неуправляемой, и один «прожорливый» сервис мог бы утянуть всю машину.
scope — области
Группа процессов, запущенных извне systemd (например, сессия SSH или контейнер), но учитываемая в его дереве.
Что даёт: такие процессы тоже видны в cgroups и подчиняются общим правилам.
Без него: процессы, запущенные вручную, жили бы «сами по себе»: их нельзя было бы учесть в лимитах, а при остановке сессии они могли остаться висеть.
snapshot — снимок
Сохранение текущего состояния юнитов, чтобы потом вернуться к нему.
Что даёт: быстрый откат набора запущенных служб.
Без него: пришлось бы вручную записывать, что было запущено, и так же вручную возвращать. Используется редко — но без него такой возможности нет вовсе.
Практика
Посмотреть, что в системе:
# все активные юниты
systemctl list-units
# только службы / только таймеры
systemctl list-units --type=service
systemctl list-timers
# все установленные юниты (включая выключенные)
systemctl list-unit-files
# чем загружена система и что её тормозит
systemctl get-default
systemd-analyze blame
systemd-analyze critical-chain
# зависимости юнита
systemctl list-dependencies nginx.service
Управление:
systemctl status nginx
sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl restart nginx
sudo systemctl enable nginx # включать при загрузке
sudo systemctl disable nginx
sudo systemctl mask nginx # запретить запуск вообще
systemctl cat nginx # показать юнит-файл
systemctl show -p MemoryMax nginx
Свой сервис и таймер:
# /etc/systemd/system/my-backup.service
[Unit]
Description=Резервная копия
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
[Install]
WantedBy=multi-user.target
# /etc/systemd/system/my-backup.timer
[Unit]
Description=Резервная копия каждую ночь
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now my-backup.timer
systemctl list-timers
Правка существующего юнита — через drop-in, не изменяя файл пакета:
sudo systemctl edit nginx
это создаст /etc/systemd/system/nginx.service.d/override.conf; после правки — sudo systemctl daemon-reload.
Что запомнить
- systemd — первый процесс (PID 1) и менеджер всего, что поднимается после ядра.
- Его придумали, чтобы уйти от последовательных shell-скриптов SysVinit: параллельный запуск, зависимости, перезапуск, единый формат, журнал.
- Юнит — описание ресурса; типы:
service,socket,target,device,mount,automount,swap,path,timer,slice,scope,snapshot. - Отсутствие любого типа — это не «мелкое неудобство»: без
socketтеряется запуск по требованию, безtimer— задачи «после загрузки», безslice— лимиты ресурсов, безdevice— реакция на железо. systemd-analyze blameпоказывает, что именно тормозит загрузку.
Комментарии
Пока комментариев нет.