# systemd

## Що це таке

Коли ядро Linux завершує власний старт, воно передає керування одному процесові
з ідентифікатором 1, і майже в усіх поширених дистрибутивах цим процесом є
systemd. Це менеджер системи та служб: він піднімає машину під час завантаження,
запускає служби й наглядає за ними, поки вона працює, а під час вимкнення
зупиняє все в правильному порядку. Проєкт пише свою назву малими літерами, без
варіантів на кшталт SystemD чи System D, а кінцева d — давня позначка демона в
Unix. Він належить до родини систем ініціалізації, поруч зі скриптами SysV init
та Upstart, які він замінив у більшості дистрибутивів, з OpenRC і runit, які
досі обирають деякі менші дистрибутиви, і з launchd, що виконує ту саму роботу в
macOS.

Усе, чим керує systemd, — це юніти, описані в коротких текстових файлах із
секціями та парами «ключ — значення». Посібник налічує 11 типів: service,
socket, target, device, mount, automount, timer, swap, path, slice і scope. Юніт
типу service запускає програму, а timer за розкладом запускає інший юніт. Socket
тримає порт, що слухає з'єднання, і запускає свою службу, коли надходить перше з
них, а target об'єднує юніти в стан, якого машина може досягти, наприклад
багатокористувацьку систему з мережею. Вимоги й порядок задають окремі
параметри, тож «цьому потрібне те» і «це стартує після того» — два різні рядки,
а менеджер збирає їх в одну транзакцію завдань запуску й зупинки, яку можна
перевірити ще до виконання.

Від скриптових систем ініціалізації, що були до нього, його відрізняють два
рішення. Кожна служба працює у власній контрольній групі ядра, тож менеджер знає
кожен процес, який служба будь-коли породила, і може зупинити їх усі, обмежити
їхню пам'ять і врахувати витрати процесора. А ще проєкт — це цілий набір
компонентів, а не лише менеджер: його власний сайт називає демон журналювання,
journal, і компоненти для входу користувачів, налаштування мережі, синхронізації
часу та розв'язання імен. Які з них увімкнути, дистрибутиви вирішують самі;
менеджер на PID 1 — та частина, яка є майже скрізь.

## Чому це важливо

На сервері з Linux питання рідко звучить так, чи запускати systemd: дистрибутив
уже зробив цей вибір. Справжнє рішення — чи використовувати його як супервізор і
планувальник, чи нашаровувати поверх нього інший менеджер процесів, політику
перезапуску контейнерного демона або cron.

Якщо використовувати його напряму, порядок запуску, політика перезапуску,
журнали, обмеження ресурсів і пісочниця опиняються в одному файлі на службу,
який читає процес, що вже працює й не потребує додаткової пам'яті. Юніт може
обмежити пам'ять через `MemoryMax=`, зробити системні каталоги доступними службі
лише для читання через `ProtectSystem=` і запускати її від тимчасового
користувача через `DynamicUser=`, а `systemd-analyze security` оцінює, наскільки
відкрита кожна служба, за шкалою від 0,0 до 10,0. Завдання, яке запускає таймер,
на відміну від рядка в crontab, пише в журнал, отримує ту саму пісочницю, що й
будь-яка служба, а `systemctl list-timers` показує його останній і наступний
запуск. Навичка ще й переноситься: інженер, який уміє читати юніт-файл, може
обслуговувати більшість серверів Linux незалежно від дистрибутива.

Ціна теж реальна. Юніти працюють лише в Linux, тож команда, яка постачає ще й на
macOS, пише для launchd файли property list, а та, що постачає на BSD, пише
rc-скрипти. У більшості контейнерів systemd немає взагалі: PID 1 там — сама
програма, а політика перезапуску належить тому, що запускає контейнер. Там, де
контейнери й звичайні служби ділять один хост, Quadlet від Podman скорочує цю
відстань: він перетворює опис контейнера на звичайний юніт служби, тож обидва
види навантаження стоять під одним супервізором.

## Як це виглядає на практиці

Власна платформа компанії спирається на systemd у трьох місцях.

Робочий хост із роботи
[вся компанія на одному хості з 512 МБ](/uk/portfolio/ran-the-whole-company-on-one-512mb-host-109/)
має одне ядро й близько 464 МБ придатної пам'яті, а на ньому працюють вебсервер
для семи доменів, git-forge, onion-служба Tor, сервери Gemini і Gopher,
зашифровані резервні копії та блокування спроб вторгнення. За такого розміру
кожен постійний процес мусить виправдати свою пам'ять, тож супервізором є
systemd без другого процесу-супервізора над ним, а контейнери працюють як юніти
Quadlet під тим самим менеджером, а не під контейнерним демоном зі своїм власним
наглядом. Запас, який лишає така схема, виміряли, коли вирішували, чи додати
onion-службу: за добу мінімум доступної пам'яті склав близько 310 МБ із 464.

[Зашифровані резервні копії з щомісячною перевіркою відновлення](/uk/portfolio/built-encrypted-backups-with-a-monthly-restore-drill-114/)
працюють за таймером systemd: дамп бази даних, якщо вона є, потім зашифрований
дедуплікований знімок, надісланий через SFTP у сховище в іншого провайдера,
потім очищення за правилами зберігання і перевірка цілісності. Сигнал dead man's
switch отримує пінг лише після успішного прогону. Ця робота зачепила й один із
гостріших кутів systemd, про який ідеться нижче: парольна фраза лежить у файлі,
який читають юніти, бо у значенні, переданому через середовище юніта, менеджер
обробляє екранування, і зворотна скісна риска непомітно змінила б парольну
фразу.

[Сторінка технологій](/uk/tech-stack/) проводить загальну межу: звичайні юніти
systemd як основний вибір, а Docker і Kubernetes — для навантажень, які на них
заслуговують.

## Де виникають проблеми

**Відредагований юніт працює зі старими налаштуваннями.** Менеджер тримає
юніт-файли в пам'яті, тож зміна на диску не діє, доки `systemctl daemon-reload`
не перебудує його уявлення про них, а `systemctl cat` може показати файл, який
відрізняється від того, чим користується запущений менеджер. Вносьте зміни через
`systemctl edit`, який сам перечитує конфігурацію, або перечитайте її вручну й
потім перезапустіть службу.

**Секрет доходить до служби без одного символу.** Значення в `Environment=`
проходять через власні правила лапок systemd: екранування в стилі C
обробляється, а буквальну зворотну скісну риску треба подвоювати. Документація
також попереджає, що змінні середовища видимі непривілейованим клієнтам через
D-Bus. Кладіть секрети у файл і передавайте їх через `LoadCredential=`: служба
отримає копію лише для читання, доступну тільки її користувачеві та root.

**Оновлення пакета тихо скасовує локальне виправлення.** Зміни у файлі
постачальника в `/usr/lib/systemd/system/` наступне оновлення пакета перезапише.
Натомість кладіть їх у drop-in-файл у `/etc/systemd/system/<unit>.d/`, який
накладається поверх оригіналу й переживає оновлення. Списки команд доповнюються,
а не замінюються, тож drop-in, що змінює команду запуску, спершу очищає її
порожнім присвоєнням:

```ini
# /etc/systemd/system/app.service.d/override.conf
[Service]
ExecStart=
ExecStart=/usr/local/bin/app --config /etc/app/app.toml
Restart=on-failure
```

**Нічне завдання пропускає ніч, коли сервер був вимкнений.** Календарний таймер,
чий час настав, поки машина стояла, просто чекає наступного слоту, бо
`Persistent=` типово має значення false. Задайте `Persistent=true` для резервних
копій та інших обов'язкових завдань, і пропущений запуск відбудеться, щойно
таймер знову стане активним.

**Служба, що впала, так і лишається лежати.** `Restart=` типово має значення
`no`, тож служба, яка аварійно завершилася, лишається мертвою, доки хтось це
помітить. Задайте `Restart=on-failure` для служб, які мають працювати постійно,
і пам'ятайте: коли юніт досягає ліміту запусків, менеджер відмовляється від
нього, а ручний запуск доведеться відкласти, доки мине інтервал або хтось
виконає `systemctl reset-failed`.

## Що почитати далі

- [systemd(1)](https://man7.org/linux/man-pages/man1/systemd.1.html) — сам
  менеджер: типи юнітів, послідовність завантаження і сигнали, на які він
  реагує.
- [systemd.unit(5)](https://man7.org/linux/man-pages/man5/systemd.unit.5.html)
  — юніт-файли, залежності, ліміти запусків і каталоги drop-in, що їх
  перевизначають.
- [systemd.service(5)](https://man7.org/linux/man-pages/man5/systemd.service.5.html)
  — типи служб, політика перезапуску і те, як скидаються списки команд.
- [systemd.exec(5)](https://man7.org/linux/man-pages/man5/systemd.exec.5.html)
  — середовище виконання, пісочниця і передавання облікових даних службам.
- [systemd.timer(5)](https://man7.org/linux/man-pages/man5/systemd.timer.5.html)
  — календарні та монотонні таймери, зокрема `Persistent=`.
- [systemctl(1)](https://man7.org/linux/man-pages/man1/systemctl.1.html)
  — командний рядок для запуску, редагування, перечитування й перегляду юнітів.

<https://engineer.company/uk/glossary/systemd/>
