systemd Builder: как Linux перешёл от init-скриптов к unit-файлам

systemd часто обсуждают как спор о вкусе: с одной стороны ставят быструю и современную загрузку, с другой вспоминают Unix-философию и слишком большой проект. Такое противопоставление скрывает главную причину перехода. Linux-дистрибутивам требовался менеджер, который мог не только вызвать стартовый скрипт, но и понимать связи между сервисами, определять момент их готовности, отслеживать все принадлежащие им процессы и одинаково управлять ими после загрузки. systemd предложил для этого единую модель unit-файлов и постепенно стал стандартным менеджером сервисов во многих дистрибутивах, хотя альтернативы никуда не исчезли.
Что было до systemd
На Unix-подобной системе ядро первым запускает процесс с PID 1, а тот поднимает остальное пользовательское пространство и принимает осиротевшие процессы. В классической схеме System V init состояние машины выражалось через runlevel, а переход к нему вызывал набор сценариев из каталогов rc. Каждый сценарий знал, как запустить, остановить или перезапустить конкретный демон, поэтому эта модель была прозрачной для администратора и десятилетиями хорошо выполняла свою задачу.
Слабость находилась не в самом факте использования shell-скриптов, а в ограниченности общего контракта. Скрипт мог запустить процесс в фоне и завершиться раньше, чем сервис действительно становился готов принимать запросы. Для дальнейшего управления демоны часто создавали PID-файлы, выполняли двойной fork и самостоятельно сбрасывали привилегии, причём разные проекты реализовывали эти действия по-разному. Менеджер запуска видел результат скрипта, но не всегда имел надёжное представление о полном жизненном цикле сервиса и всех созданных им дочерних процессах.
Неверно и утверждение, что SysV-подобная загрузка обязательно запускала всё строго последовательно. Например, Debian использовал insserv для вычисления порядка по LSB-заголовкам сценариев, а в мае 2010 года включил параллельный запуск независимых init-скриптов по умолчанию. Эта надстройка ускоряла загрузку и учитывала явные зависимости, но сами сведения оставались распределены между императивным сценарием, его заголовком, PID-файлом и поведением демона. Проблема состояла скорее в неоднородности этих механизмов, чем в полном отсутствии зависимостей или параллелизма.
К тому времени существовали и другие попытки переосмыслить управление сервисами. Solaris SMF хранил зависимости и мог перезапускать отказавшие службы, launchd в macOS использовал активацию по событиям и сокетам, а Ubuntu развивала событийный Upstart. systemd появился не в пустом поле и заимствовал уже проверенные идеи, но связал их с возможностями ядра Linux в собственной модели.
Проект, который начинался с PID 1
30 апреля 2010 года Леннарт Пёттеринг опубликовал статью «Rethinking PID 1» и представил экспериментальный init-проект. Основную кодовую базу на тот момент написал он сам, а детали архитектуры обсуждались с Кеем Зиверсом и другими инженерами. Целью было не просто заменить каталог shell-скриптов другим форматом конфигурации, а сделать менеджер сервисов участником их полного жизненного цикла.
Основной единицей стала не команда запуска, а unit. Unit может описывать сервис, сокет, точку монтирования, устройство, таймер, target или другой объект, которым управляет systemd. Когда запрашивается запуск target или сервиса, менеджер собирает транзакцию из связанных units, проверяет её на противоречия и выполняет операции в допустимом порядке. Независимые части графа при этом могут запускаться одновременно.
В этой модели важно различать два вида связей. Wants= и Requires= определяют, какие units нужно добавить в транзакцию, тогда как Before= и After= задают только порядок уже запланированных операций. Официальная документация systemd.unit подчёркивает, что требование и порядок ортогональны: одна запись After=network.target не запускает сеть сама по себе. Такое разделение делает граф точнее, но требует понимать семантику директив, иначе декларативный файл лишь скрывает ошибочную зависимость за аккуратным синтаксисом.
Другой важной идеей стала активация по сокету. Менеджер может заранее создать слушающий сокет, начать запуск зависящих сервисов и передать файловый дескриптор нужному демону, когда тот будет готов. Клиенту не приходится ждать отдельного шага «сервис полностью запущен», а запросы могут временно находиться в очереди сокета. Этот приём расширил возможности параллельной загрузки, но его польза не сводится к рекордам времени старта: он также задаёт явную точку активации и позволяет запускать некоторые службы по требованию.

Зачем systemd использует cgroups
Традиционный демон мог выполнить двойной fork, сменить PID и породить дополнительные процессы, из-за чего PID-файл описывал только выбранный главный процесс. systemd помещает процессы каждого сервиса в отдельную Linux control group и использует данные ядра, чтобы видеть всю группу независимо от того, сколько раз программа разветвилась. Благодаря этому остановка unit может охватывать все принадлежащие ему процессы, а к группе можно применять учёт и ограничения ресурсов.
cgroups при этом не имеют отношения к устранению зомби-процессов. Зомби появляется, когда дочерний процесс завершился, а родитель ещё не вызвал wait() и не забрал его статус. Если родитель завершается, такой потомок переходит к init или ближайшему процессу-субреперу, который должен выполнить wait(); это обычная обязанность PID 1, описанная в руководстве wait(2). cgroups решают другую задачу: сохраняют принадлежность живых процессов к сервису и дают менеджеру устойчивую границу управления ими.
Где заканчивается init и начинается набор systemd
Название systemd одновременно относится к менеджеру системы и сервисов, работающему как PID 1, и к более широкому проекту. Сам проект описывает себя как набор базовых компонентов Linux-системы: в него входят journald для журнала, logind для сеансов, networkd для сети, resolved для разрешения имён, timesyncd для синхронизации времени, а также инструменты для временных файлов, учётных записей и других системных задач. Эти компоненты не являются одним огромным процессом внутри PID 1 и во многих случаях собираются, включаются или заменяются отдельно.
Именно расширение набора стало одной из причин долгого спора. Единые соглашения упрощают интеграцию дистрибутива, диагностику и управление, особенно когда компоненты обмениваются состоянием через общие интерфейсы. Обратная сторона состоит в росте общей сложности, привязке многих возможностей к Linux и риске того, что теоретически отдельные задачи на практике становятся тесно связаны с одной экосистемой. Поэтому фраза «systemd нарушает Unix-философию» обозначает позицию, но плохо объясняет конкретный компромисс между интеграцией и взаимозаменяемостью.
journald, timers и resolved тоже следует описывать без абсолютов. Журнал systemd хранит структурированные записи и позволяет фильтровать их по unit, загрузке и другим полям, однако пересылка в традиционный syslog остаётся возможной. Timer units могут заменить часть заданий cron, но cron продолжает существовать. systemd-resolved входит в проект, но конкретный дистрибутив сам решает, использовать ли его как системный DNS-компонент.
Решение Debian и появление Devuan
11 февраля 2014 года технический комитет Debian постановил, что systemd станет init-системой по умолчанию для Linux-архитектур в Debian Jessie. Обсуждение затрагивало не только скорость загрузки, но и готовность реализации, интеграцию с рабочими окружениями, сопровождение пакетов и возможность поддерживать несколько init-систем одновременно. Выбор значения по умолчанию не означал немедленного запрета всех альтернатив, однако усилил опасения, что зависимости пакетов со временем сделают такую альтернативу всё дороже поддерживать.
В конце ноября 2014 года был запущен проект Devuan, который сохранил совместимость с Debian, но сделал отказ от systemd одной из своих основных целей. Сейчас Devuan использует sysvinit по умолчанию и предлагает другие init-системы, включая OpenRC и runit. Его существование показывает, что спор не завершился чьей-либо окончательной победой: большинство крупных дистрибутивов выбрало глубокую интеграцию systemd, а часть сообщества продолжила развивать системы с иными границами и приоритетами.
Что умеет systemd Builder
systemd Builder создаёт базовый .service unit для обычного процесса с Type=simple. В форме можно указать имя и описание сервиса, ExecStart, рабочий каталог, пользователя, группу, политику и задержку перезапуска, а также строку переменных окружения. Получившийся файл содержит After=network.target, основные секции [Unit], [Service], [Install] и подключение к multi-user.target.
Инструмент не строит произвольный граф зависимостей, не создаёт timer units и не переключает тип сервиса на forking, notify или oneshot. Его задача уже: подготовить понятную основу для приложения, которое остаётся на переднем плане и может управляться как простой сервис. Рядом выводятся команды для сохранения файла, daemon-reload, включения и немедленного запуска через enable --now, а также проверки состояния; unit можно скопировать или скачать.
Для начала укажите абсолютную команду вроде /usr/bin/node /var/www/app/index.js, рабочий каталог и непривилегированного пользователя, затем выберите Restart=on-failure. После сохранения файла проверьте права на каталог и переменные окружения, выполните sudo systemctl daemon-reload, запустите сервис и изучите результат через systemctl status. Для production-конфигурации основу обычно дополняют ограничениями доступа, условиями запуска и более точными зависимостями под конкретное приложение.
Похожие инструменты
Частые вопросы
Кто создал systemd?
Первую версию systemd в 2010 году представил Леннарт Пёттеринг. В исходном описании проекта он писал, что основная кодовая база была его работой, а архитектура подробно разрабатывалась вместе с Кеем Зиверсом при участии других инженеров.
Почему systemd вызывает споры?
Споры связаны не только с init-процессом, но и с масштабом всего проекта. В репозитории systemd развиваются менеджер сервисов, журнал, управление сеансами, сетью, DNS, временем и другие компоненты. Сторонники ценят общие интерфейсы и тесную интеграцию, а критики опасаются сложности, Linux-специфичных зависимостей и сокращения выбора взаимозаменяемых компонентов.
Что такое SysV init?
Так обычно называют семейство схем загрузки, основанных на Unix System V: процесс init переключает runlevel и вызывает сценарии из каталогов rc. Реализации могли учитывать зависимости и запускать независимые сценарии параллельно, но управление сервисом по-прежнему опиралось на императивные shell-скрипты и внешние соглашения вроде PID-файлов.
Что такое unit-файл systemd?
Unit-файл представляет собой декларативный INI-подобный файл, который описывает объект для менеджера systemd. Для сервиса секция [Unit] задаёт описание и связи, [Service] содержит команду и правила управления процессом, а [Install] указывает, к какому target подключать unit при включении.
Все ли Linux-дистрибутивы используют systemd?
systemd используется по умолчанию во многих распространённых дистрибутивах, включая Debian, Ubuntu, Fedora и RHEL, но не является обязательной частью Linux. Alpine использует OpenRC, Void Linux использует runit, а Devuan сохраняет sysvinit по умолчанию и предлагает другие init-системы.
Автор
Alex RelFullstack Engineer
В коммерческой разработке с 2011 года — больше 15 лет. Автор двух SaaS-продуктов: TeleGo.io и LingvoHabit.com. Статьи здесь выросли из реальной клиентской и продуктовой работы.



