С чего начать Первые шаги трек · 15 ступеней
Linux Labs

systemd

7 мин Ubuntu 24.04 · systemd 255

Init-система и менеджер служб (PID 1): юниты, цели, зависимости, журналы.

Шпаргалка

8 команд Версия для печати
  • systemctl status nginx состояние
  • systemctl start nginx запустить сейчас
  • systemctl stop nginx остановить сейчас
  • systemctl enable nginx включить автозапуск
  • systemctl reload nginx перечитать конфиг без обрыва
  • systemctl daemon-reload после правки юнит-файлов
  • journalctl -u nginx -f логи службы в реальном времени
  • systemd-analyze blame тайминги загрузки

Обзор

systemd — первый процесс системы (PID 1) и менеджер служб большинства современных дистрибутивов. Он запускает и контролирует юниты, разрешает зависимости между ними и параллелит загрузку.

Типы юнитов:

Тип Расширение Что описывает
service .service служба/демон (nginx, sshd)
socket .socket сокет активации
timer .timer расписание (замена cron)
mount .mount точку монтирования
target .target группу юнитов (аналог runlevel)

Управление — через systemctl, журналы — через journalctl.

Зависимости и порядок

Строки [Unit] отвечают на два вопроса: что запустить вместе и в каком порядке.

Строка Что делает
Wants=x при запуске юнита запустить и x; падение x юниту не мешает
Requires=x то же, и если x остановят — остановится и юнит; с After=x юнит не стартует, когда x не запустился
After=x если оба запускаются, этот стартует после того, как x закончил запуск
Before=x то же в другую сторону

After= и Before= задают только порядок: если x никто не запускает, After=x его не запустит. Поэтому зависимость пишут парой — Wants=x и After=x. Wants= без After= запускает оба юнита одновременно, и служба стартует раньше, чем готово то, что ей нужно. Requires= без After= тоже не ждёт: служба уже работает, когда зависимость упала. Юнит из Wants=, которого нет, systemd пропускает молча, из Requires= — отказывается запускать службу. Проверить юнит после правки — systemd-analyze, цепочку ожидания при загрузке — systemd-analyze.

Ключи

Юниты ищутся в (по возрастанию приоритета):

  • /lib/systemd/system/ — пакетные (не редактировать);
  • /etc/systemd/system/ — локальные переопределения (редактировать здесь).

Структура .service:

ini
[Unit]
Description=My App
After=network-online.target

[Service]
ExecStart=/usr/bin/myapp
Restart=on-failure
User=myapp

[Install]
WantedBy=multi-user.target

Drop-in: дополнение юнита

Юнит из пакета правят через drop-in: обновление пакета перезапишет сам файл юнита, а drop-in останется. Drop-in — файл .conf в каталоге /etc/systemd/system/<юнит>.d/. systemd читает юнит, затем drop-in'ы по алфавиту, и их строки дополняют или заменяют строки юнита.

bash
sudo systemctl edit nginx       # редактор; сохранит override.conf
sudo mkdir -p /etc/systemd/system/demo.service.d       # или руками:
printf '[Service]\nMemoryMax=256M\n' \
  | sudo tee /etc/systemd/system/demo.service.d/memory.conf
sudo systemctl daemon-reload    # перечитать юниты после ручных правок
systemctl cat demo              # юнит и все его drop-in'ы
systemctl show demo -p DropInPaths

systemctl edit сам делает daemon-reload. После ручной правки без daemon-reload systemd работает со старой версией юнита. Ключ вроде ExecStart= в drop-in'е добавляет вторую команду; чтобы заменить первую, перед новой строкой ставят пустую ExecStart=.

Ключ в чужой секции systemd пропускает: LimitNOFILE= под [Unit] не действует, служба получает лимит по умолчанию. Предупреждение видно в журнале после daemon-reload и в systemd-analyze verify — код у него всё равно 0:

bash
systemd-analyze verify /etc/systemd/system/demo.service
text
/etc/systemd/system/demo.service:3: Unknown key name 'LimitNOFILE' in
  section 'Unit', ignoring.

Активация сокетом

Юнит .socket держит порт вместо службы: systemd слушает сам и запускает одноимённую службу при первом соединении. Учебная пара — demo-sock.socket и demo-sock.service:

ini
# /etc/systemd/system/demo-sock.socket
[Socket]
ListenStream=8085

[Install]
WantedBy=sockets.target

# /etc/systemd/system/demo-sock.service
[Unit]
Requires=demo-sock.socket
After=demo-sock.socket

[Service]
ExecStart=/usr/bin/python3 /usr/local/bin/demo-sock.py

ListenStream=8085 — только порт, без адреса: сокет слушает все адреса, IPv4 и IPv6. До первого запроса порт держит процесс systemd, служба inactive:

text
student@ubuntu:~$ sudo ss -tlnp | grep 8085
LISTEN 0  4096  *:8085  *:*  users:(("systemd",pid=1,fd=71))
student@ubuntu:~$ systemctl list-sockets | grep -E 'LISTEN|demo'
LISTEN     UNIT              ACTIVATES
[::]:8085  demo-sock.socket  demo-sock.service
student@ubuntu:~$ curl -s http://127.0.0.1:8085/
hello
student@ubuntu:~$ sudo ss -tlnp | grep 8085
LISTEN 0  4096  *:8085  *:*
  users:(("python3",pid=2769,fd=3),("systemd",pid=1,fd=71))

Адрес меняют drop-in'ом. ListenStream= — список: пустая строка сбрасывает адреса из юнита, без неё новый адрес добавится к старому.

bash
sudo mkdir -p /etc/systemd/system/demo-sock.socket.d
printf '[Socket]\nListenStream=\nListenStream=127.0.0.1:8085\n' \
  | sudo tee /etc/systemd/system/demo-sock.socket.d/local.conf
sudo systemctl daemon-reload
sudo systemctl restart demo-sock.socket
sudo ss -tlnp | grep 8085           # 127.0.0.1:8085

restart сокета перезапускает и службу с Requires= на него: MainPID службы сменился (2769 → 2838). Остановить одну службу мало — следующее соединение запустит её снова, а systemctl stop предупредит:

text
Stopping 'demo-sock.service', but its triggering units are still active:
demo-sock.socket

Закрыть порт совсем — sudo systemctl disable --now demo-sock.socket.

Рецепты

1

Цели (targets)

Цель — группа юнитов, до которой systemd доводит загрузку: multi-user.target у сервера, rescue.target и emergency.target для починки. Подробно — boot-targets.

bash
systemctl get-default              # текущая цель по умолчанию
sudo systemctl set-default multi-user.target
systemctl isolate rescue.target    # переключиться сейчас

Неполадки

3

Служба не стартует

  1. systemctl status <unit> — код выхода и последние строки журнала.
  2. journalctl -xeu <unit> — подробности с пояснениями.
  3. После ручной правки юнита всегда sudo systemctl daemon-reload.

«Unit not found»

Проверь имя и путь: systemctl cat <unit>. Если правил файл — daemon-reload.

Долгая загрузка

systemd-analyze critical-chain — кто держал загрузку; порядок разбора — systemd-analyze.

Термины

3
юнит (unit)
Единица управления systemd (служба, сокет, таймер, mount). Описывается unit-файлом. Ещё встречается в: systemd-analyze: что тормозит загрузку и что не так в юните, Цели загрузки: default, multi-user, rescue и emergency, systemctl, Ограничения службы: User=, ProtectSystem, NoNewPrivileges и ещё 30
drop-in (файл дополнения юнита) drop-in (unit override file)
Файл .conf в каталоге /etc/systemd/system/<юнит>.d/: его строки дополняют или заменяют строки юнита, а сам юнит остаётся как есть. После правки — systemctl daemon-reload. Ещё встречается в: Ограничения службы: User=, ProtectSystem, NoNewPrivileges, OOM killer: кто и почему завершил процесс, ulimit и лимиты процесса: открытые файлы, процессы, core, systemctl и ещё 4
порядок запуска юнитов unit ordering
Строки After= и Before= юнита: кто кого ждёт, если оба запускаются. Сами они юнит не запускают — для этого Wants= или Requires=, поэтому их пишут парой.

См. также

  • systemctl — управление службами systemd.
  • systemd-analyze — время загрузки, цепочка зависимостей, проверка юнита.
  • boot-targets — цели загрузки, rescue и emergency.
  • journalctl — Чтение журнала systemd (journald) — фильтры по службе, времени, приоритету.
  • nginx — Веб-сервер и обратный прокси. Конфиг проверяется nginx -t; reload без даунтайма.
  • Службы под управлением systemd — урок Теории по теме
Загрузка…