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

systemd-analyze: что тормозит загрузку и что не так в юните

10 мин Ubuntu 24.04 · systemd 255

Разбирает загрузку по времени и цепочке зависимостей юнитов и проверяет юнит-файл до запуска службы.

Шпаргалка

8 команд Версия для печати
  • systemd-analyze сколько шла загрузка
  • systemd-analyze blame | head юниты по времени запуска
  • systemd-analyze critical-chain цепочка до цели загрузки
  • systemd-analyze critical-chain nginx.service чего ждал юнит
  • systemd-analyze verify ./app.service проверить файл юнита
  • systemd-analyze verify app.service проверить установленный юнит
  • systemd-analyze plot > boot.svg диаграмма загрузки
  • systemd-analyze security app оценка защиты службы

Обзор

systemd записывает, когда какой юнит начал и закончил запуск. systemd-analyze читает эти времена и показывает их тремя способами: итог загрузки, список юнитов и цепочку зависимостей. Та же команда проверяет юнит-файл раньше, чем его прочитает systemd.

Без подкоманды — итог загрузки учебной машины:

text
student@ubuntu:~$ systemd-analyze
Startup finished in 2.902s (kernel) + 6.382s (userspace) = 9.285s
multi-user.target reached after 6.356s in userspace.

kernel — ядро и initramfs до запуска systemd, userspace — работа systemd до конца загрузки. Загрузчик systemd-boot на UEFI передаёт ещё firmware и loader, GRUB этих времён не передаёт. Вторая строка называет цель загрузки и момент, когда systemd её достиг.

Кто сколько запускался — blame

text
student@ubuntu:~$ systemd-analyze blame | head -6
2.368s apparmor.service
2.208s labs-ssh-hostkeys.service
1.151s dev-vda4.device
 892ms containerd.service
 835ms rsyslog.service
 671ms ldconfig.service

Время в строке — сколько юнит запускался: от команды «запустить» до состояния active. Юниты стартуют параллельно, поэтому сумма времён больше всей загрузки, а первая строка списка могла никого не задержать. Кто держал загрузку, показывает critical-chain.

Время зависит от типа службы. У Type=oneshot в него входит вся работа ExecStart=. Служба Type=simple считается запущенной сразу, как systemd создал её процесс, — даже если программа потом минуту готовится принимать запросы.

В списке и службы, запущенные после загрузки. Тестовая служба с ExecStart=/bin/sleep 4 и Type=oneshot встала первой строкой:

text
4.007s lab-slow.service

Критическая цепочка

text
student@ubuntu:~$ systemd-analyze critical-chain
multi-user.target @6.356s
└─containerd.service @4.211s +892ms
  └─dbus.service @4.060s +131ms
    └─basic.target @4.045s
      └─sockets.target @4.044s
        └─lxd-installer.socket @4.030s +11ms
          └─sysinit.target @3.989s
            └─apparmor.service @1.618s +2.368s
              └─local-fs.target @1.603s

Две строки перед деревом объясняют значки: после @ — когда юнит стал активным (секунды от начала userspace), после + — сколько он запускался. Читай снизу вверх: каждый юнит ждал того, кто под ним. apparmor.service запускался 2.368 с, и sysinit.target дождался его только к 3.989 с — всё, что выше, стартовало после. Юниты, запущенные после того, как цель достигнута, в её цепочку не попадают.

Цепочка идёт по After=: из юнитов, после которых запускается этот, выбран тот, кто освободился последним. С именем юнита — чего ждал он сам; у первой строки только +:

text
student@ubuntu:~$ systemd-analyze critical-chain lab-slow.service
lab-slow.service +4.007s
└─nginx.service @4.114s +207ms
  └─basic.target @4.045s
    └─sockets.target @4.044s

lab-slow.service запускался 4 с, а до этого ждал nginx.service — у него After=nginx.service.

Проверка юнита — verify

systemd-analyze verify загружает юнит так же, как systemd при daemon-reload, и печатает, что ему не понравилось. Файл дают путём — его можно проверить ещё до копирования в /etc/systemd/system; установленный юнит — именем. Что печатает verify на ошибки в юните:

Что в юните Что печатает verify Код
Afer= вместо After= Unknown key name 'Afer' in section 'Unit', ignoring. 0
RestartSec=fast Failed to parse sec value, ignoring: fast 0
Type=simpel Failed to parse service type, ignoring: simpel 0
Wants= на юнит, которого нет ничего 0
WantedBy=multi-user без .target ничего 0
нет программы из ExecStart= Command /usr/local/bin/demo-app is not executable: No such file or directory 1
нет ExecStart= Service has no ExecStart=, ExecStop=, or SuccessAction=. Refusing. 1
Requires= на юнит, которого нет Failed to create app.service/start: Unit postgres.service not found. 1
два юнита с After= друг на друга Found ordering cycle on …, Transaction order is cyclic 1

Код 0 значит только, что юнит загрузится. Строку с опечаткой и неверное значение systemd пропускает, и служба работает с умолчаниями: без After=, с типом simple. Чистый юнит — это пустой вывод.

Ключи

13

Подкоманды и ключи — в таблице ниже; полный список — systemd-analyze --help и man systemd-analyze. Времена загрузки и verify работают от обычного пользователя, без sudo.

Ключ Что делает Пример
time Итог загрузки — ядро, userspace и когда достигнута цель; то же без подкоманды. systemd-analyze
blame Юниты по времени запуска, самые долгие сверху; кто кого ждал, не показывает. systemd-analyze blame | head
critical-chain [UNIT] Цепочка юнитов по After=, которая держала цель загрузки или указанный юнит. systemd-analyze critical-chain nginx.service
verify FILE|UNIT Загрузить юнит, как это делает systemd, и напечатать ошибки; пустой вывод — чистый юнит. systemd-analyze verify /etc/systemd/system/app.service
plot Диаграмма загрузки в SVG — каждый юнит полосой на шкале времени. systemd-analyze plot > boot.svg
security [UNIT] Оценка ограничений службы от 0 до 10 — подробно в статье systemd-hardening. systemd-analyze security nginx
calendar EXPR Проверить выражение OnCalendar= таймера и показать ближайший запуск. systemd-analyze calendar 'Mon..Fri 03:00'
--no-pager Печатать сразу в терминал, без less. systemd-analyze blame --no-pager
cat-config PATH Показать конфиг вместе со всеми файлами .d в порядке применения. systemd-analyze cat-config sysctl.d
timespan SPAN Разобрать запись промежутка времени (90s, 1h 30min) так, как её поймёт systemd. systemd-analyze timespan 90s
unit-paths Каталоги, в которых systemd ищет юниты, по порядку. systemd-analyze unit-paths
--fuzz SPAN В critical-chain показать и юниты, закончившие запуск в пределах этого времени от выбранного. systemd-analyze critical-chain --fuzz 1s
--user Смотреть менеджер служб пользователя вместо системного. systemd-analyze --user blame

Ничего не найдено. Попробуй запрос короче.

Рецепты

3

Найти, что задерживает загрузку

  1. systemd-analyze — сколько шла загрузка и до какой цели.
  2. systemd-analyze critical-chain — юнит с самым большим + в цепочке.
  3. systemd-analyze critical-chain <юнит> — чего ждал он сам.
  4. systemctl cat <юнит> — его After=, Wants= и Requires=. Частый случай — служба ждёт сеть, см. boot-targets.
  5. После правки — verify, sudo systemctl daemon-reload и перезагрузка: времена этой загрузки уже записаны и не изменятся.

Проверить юнит до запуска

bash
systemd-analyze verify ./app.service      # файл до установки
sudo cp app.service /etc/systemd/system/
sudo systemctl daemon-reload
systemd-analyze verify app.service        # установленный, по имени
systemd-analyze verify app.service 2>&1 | grep . || echo clean

Последняя строка годится для скрипта: сообщения verify идут в stderr, и пустой вывод — признак чистого юнита. Только по коду этого не понять.

Поднялась ли служба при загрузке сама

text
student@ubuntu:~$ systemctl show nginx \
> -p ActiveEnterTimestampMonotonic,NRestarts
NRestarts=0
ActiveEnterTimestampMonotonic=7224455

ActiveEnterTimestampMonotonic — микросекунды от старта ядра до момента, когда служба стала active: 7.2 с, внутри загрузки (9.285 с по systemd-analyze). Службу, запущенную руками после входа, выдаёт большое число. NRestarts — сколько раз systemd перезапускал её после падения.

Неполадки

5

Bootup is not yet finished

text
Bootup is not yet finished
  (org.freedesktop.systemd1.Manager.FinishTimestampMonotonic=0).
Please try again later.
Hint: Use 'systemctl list-jobs' to see active jobs

Загрузка ещё идёт: какое-то задание не закончилось — служба с долгим стартом или ожиданием сети. systemctl list-jobs покажет, какое; systemctl is-system-running в это время пишет starting. Задание закончится само или по TimeoutStartSec= — 90 с по умолчанию; у Type=oneshot предела по умолчанию нет, и такая служба держит загрузку, пока не завершится.

Убрал первую строку blame, а загрузка не ускорилась

blame показывает, сколько запускался каждый юнит, но не кто кого ждал. Юнит, который стартовал параллельно с остальными, загрузку не держал. Смотри critical-chain: задерживает только то, что стоит в цепочке.

verify молчит, а служба не стартует

Опечатка в имени ключа или неверное значение дают код 0 — читай вывод. Если вывода нет, причина не в файле: systemctl status <юнит> покажет код выхода, например status=203/EXEC — нет программы из ExecStart= или у неё нет права на выполнение.

Unit … not found

Requires= или Wants= называет юнит, которого нет: опечатка или пакет не установлен. С Requires= служба не стартует, с Wants= systemd молча пропускает зависимость. Настоящее имя покажет systemctl list-unit-files 'postgres*' — здесь postgresql.service.

Transaction order is cyclic

text
Failed to start cyc-a.service: Transaction order is cyclic.
  See system logs for details.

Юниты ждут друг друга через After=. В журнале — оба участника: Found ordering cycle on cyc-b.service/start. При загрузке systemd разрывает цикл сам и выбрасывает одно из заданий — тогда одна служба молча не стартует. Убери лишний After=.

Если юниты связаны Requires=, systemctl start сразу отвечает ошибкой, как выше. Со связью через Wants= systemd пытается разорвать цикл, и sudo systemctl restart может не вернуть приглашение: оба задания висят в systemctl list-jobs со статусом waiting. Ctrl+C снимает только ожидание команды, задания остаются; после правки юнита и sudo systemctl daemon-reload они выполняются сами. verify на такой цикл пишет Found ordering cycle on …/start и Job …/start deleted to break ordering cycle, код 1.

См. также

  • systemd — юниты, drop-in'ы и зависимости.
  • systemctl — запуск, остановка и состояние служб.
  • journalctl — журнал этой загрузки: journalctl -b.
  • boot-grub — что происходит до systemd.
  • systemd-hardening — systemd-analyze security.
  • systemd-timers — systemd-analyze calendar.
Загрузка…