Found ordering cycle on basic.target — Циклические зависимости systemd
Архитектура ошибки и симптомы сбоя
Сообщение «Cannot add dependency job for unit: Circular dependency detected» (или «systemd[1]: Found ordering cycle on basic.target/start») генерируется планировщиком графов systemd Dependency Graph Engine. Ошибка означает, что в конфигурациях служб создана кольцевая логическая зависимость (Deadlock Cycle). Например: Сервис A настроен запускаться После Сервиса B (After=B.service), а Сервис B настроен запускаться После Сервиса A (After=A.service). Планировщик systemd не может построить линейную последовательность запуска и аварийно вырезает случайные критические юниты из графа.
Диагностическая таблица параметров сбоя
| Параметр | Значение | Инженерный смысл сбоя |
|---|---|---|
| Ordering Cycle | Directed Graph Loop | Замкнутый цикл между директивами Before= и After=. |
| Breaking Cycle Action | Job Deletion | systemd принудительно удаляет один из конфликтующих юнитов (часто sysinit.target или сетевой стек). |
| DefaultDependencies | implicit After=basic.target | По умолчанию все сервисы неявно запускаются ПОСЛЕ basic.target. |
Пошаговое дерево решений и сценарии траблшутинга
Сценарий 1: Локализация точной петли зависимостей через systemd-analyze
# Проверка валидности всех установленных сервисов на предмет циклов:
sudo systemd-analyze verify /etc/systemd/system/*.service
# Поиск сообщений о разрыве циклов в системном логе:
sudo journalctl -b -p warning..emerg | grep -i -E "(cycle|breaking|deleting job)"Сценарий 2: Распутывание классического конфликта раннего запуска (DefaultDependencies)
Если низкоуровневый сервис (ранний драйвер, VPN до сети, генератор ключей) требует запуска ДО basic.target:
# В unit-файле раннего сервиса ОБЯЗАТЕЛЬНО отключите дефолтные зависимости:
[Unit]
Description=Early Boot Custom Service
DefaultDependencies=no
After=sysinit.target
Before=basic.target
# Без директивы DefaultDependencies=no возникнет гарантированный ordering cycle!Сценарий 3: Построение графического дерева зависимостей
# Экспорт графа зависимостей в формат SVG (требуется пакет graphviz):
systemd-analyze dot --to-pattern='*.target' --from-pattern='*.target' | dot -Tsvg > /tmp/boot_graph.svgСценарий 4: Применение изменений и перезагрузка планировщика
# Перезагрузка графа юнитов ядра systemd:
sudo systemctl daemon-reloadITSTM проведет комплексный аудит unit-файлов, распутает графы зависимостей и выстроит детерминированный порядок инициализации.
Частые вопросы (FAQ)
Что такое DefaultDependencies=yes в systemd?
По умолчанию systemd неявно добавляет каждому сервису зависимости Requires=basic.target, After=basic.target и Before=shutdown.target. Если ваш сервис сам нужен для старта basic.target, возникает кольцевая зависимость.
Как systemd решает, какой юнит удалить при обнаружении цикла?
Алгоритм systemd удаляет случайную транзакционную задачу (Job) из петли. Часто жертвой становится базовая системная цель (например, sysinit.target/start), что приводит к полной недоступности дисков или сети.
В чем разница между Requires= и After=?
Requires= задает жесткую зависимость по наличию сервиса (если сервис B упал, сервис A не запустится). After= задает строго временной порядок старта (сервис A стартует строго после того, как сервис B начал работу).
Может ли директива Wants= вызвать циклический конфликт?
Сама директива Wants= (мягкая зависимость) циклов не создает, но в комбинации с противоречивыми директивами Before= и After= она формирует неразрешимый граф.