Привет, Хабр! Systemd - скелет современного Linux. Он управляет не только службами, но и таймерами, монтированием, логированием... Понимать его = значительно повысить эффективность администрирования системы.

Данное руководство - исключительно технические аспекты: архитектура, юниты, cgroups, работа с журналами. Только команды и конфиги.

Введение

В мире Linux и Unix-подобных систем исторически доминировала система инициализации SysVinit. Её процесс загрузки был последовательным и понятным: она запускала скрипты (обычно расположенные в /etc/rc.d/ или /etc/init.d/) по одному, в строгом порядке, определяемом симлинками в каталогах rcN.d.

Но с развитием Linux и увеличением сложности систем - недостатки SysVinit стали вполне очевидны:

  1. Последовательный запуск: скрипты выполнялись один за другим. Если служба не зависела от другой - она всё равно ждала своей очереди, что замедляло загрузку.

  2. Отсутствие контроля за процессами. Init только запускал скрипты, но не мог отслеживать состояние запущенных служб (упала? требует перезагрузки?).

  3. Разрозненность логирования. Каждая служба вела логи "по-своему" (в /var/log/), не было какого-то единого централизованного интерфейса для просмотра логов.

Полагаю, именно эти проблемы привели к появлению альтернатив (Upstart, OpenRC), но именно systemd, представленный Леннартом Поттерлингом в 2010 году, стал неким "де-факто" стандартом для множества крупных дистрибутивов (Fedora, Debian, Ubuntu, Arch и др.). Он был разработан как кардинально новое решение для преодоления ограничений.

Технические аспекты systemd

1. Архитектура и основные компоненты

Systemd - не единая бинарная программа, а набор связанных компонентов, образующих "каркас" системы. Его ядро - демон systemd (PID 1), который является прямым потомком ядра и управляет всеми остальными процессами.

Ключевые технические компоненты:

  • systemd (PID 1): главный демон. Отвечает за запуск и поддержание юнитов, отслеживание процессов (через cgroups), обработку сигналов от ядра.

  • systemctl: основной CLI-инструмент для взаимодействия с демоном. Не просто запускает скрипты, а отправляет D-Bus-сообщения демону systemd, который выполняет запрошенные действия.

  • journald: демон журналирования. Интегрирован напрямую с демоном systemd, что позволяет захватывать stdout/stderr каждого сервиса и "обогащать" записи лога метаданными (например, SYSTEMDUNIT=ssh.service).

  • udev менеджер устройств. Управляет устройственными узлами в /dev, загружает firmware и модули ядра. Тесно интегрирован с systemd для активации устройств.

  • networkdtimedatedlogind: специализированные демоны для управления сетью, системным временем и сеансами пользователей (логином).

2. Юниты

Юниты - абстракции системных ресурсов. Их конфигурация описывается в декларативных файлах (обычно в /usr/lib/systemd/system/, переопределяются в /etc/systemd/system/).

Основные типы юнитов:

  • Service (.service): cамый распространённый тип. Описывает процесс (демон) и команды для управления им (ExecStart=ExecStop=).

  • Socket (.socket): описывает сокет (сетевой, IPC или FIFO). Механизм "socket-based activation" - ключевая особенность: демон systemd прослушивает сокет и запускает соответствующий сервис (.service) только при поступлении первого подключения. Это экономит ресурсы для редко используемых служб.

  • Timer (.timer): альтернатива cron. Позволяет планировать задачи на основе календарных выражений (OnCalendar=*-*-* 00:00:00) или относительных интервалов (OnBootSec=OnUnitActiveSec=). Главное преимущество - полная интеграция с зависимостями и журналированием, как у любого другого юнита.

  • Mount (.mount) и Automount (.automount): точки монтирования. Automount обеспечивает отложенное монтирование в момент первого обращения к каталогу.

  • Path (.path): активирует сервис при изменении файловой системы (например, появлении файла в определённом каталоге). Аналог incron.

  • Target (.target): аналог runlevel из SysVinit, но более гибкий. Это группа юнитов, которую нужно достичь (например, multi-user.target — многопользовательский режим без GUI).

Структура файла юнита:
Каждый файл разбит на секции. [Unit] содержит общие метаданные (описания, зависимости), а специализированная секция (например, [Service] или [Timer]) — параметры конкретного типа.

# Пример: /etc/systemd/system/app.service
[Unit]
Description=My Custom Application
After=network.target # Запускать после активации сети
Requires=postgresql.service # Жёсткая зависимость
Wants=redis.service # Мягкая зависимость

[Service]
Type=simple
ExecStart=/usr/bin/myapp --serve
Restart=on-failure # Автоматически перезапускать при падении
RestartSec=5
User=myappuser
Group=myappgroup

[Install]
WantedBy=multi-user.target 
# |- В какой "уровень" включить этот юнит при активации через `systemctl enable`
Подробнее про Type

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

  • Type=simple - значение по умолчанию

Поведение: процесс, указанный в ExecStart, считается главным. Systemd считает сервис запущенным (active) и готовым к работе сразу после того, как породил (fork) этот процесс. Он не ждёт никаких подтверждений от самого процесса.

Когда использовать: для простых демонов, которые не превращаются сами в фоновый процесс. Лучше не использовать данный тип, если процесс "демонизируется" самостоятельно - systemd просто потеряет его из виду.

  • Type=forking

Поведение: классическая модель "демона". Процесс, указанный в ExecStart, запускается, создает своего потомка (fork) и сразу завершается. Systemd отслеживает завершение этого родительского процесса и после этого считает запущенным именно дочерний процесс.

Обязательный параметр: почти всегда требуется указать PIDFile=... (к примеру, PIDFile=/run/nginx.pid). Это необходимо, чтобы systemd точно знал, какой PID (идентификатор процесса) у главного дочернего процесса и, соответственно, мог его корректно перезапустить или остановить.

Когда использовать: для традиционных демонов, написанных под SysV init (к примеру, nginx, Apache, MySQL и прочие), которые самостоятельно отсоединяются от терминала.

  • Type=oneshot

Поведение: предназначен для скриптов, которые должны выполниться один раз и завершиться. Systemd будет терпеливо ожидать полного завершения процесса ExecStart, и только после этого перейдет к запуску следующих юнитов, зависящих от этого сервиса.

Комбинация: часто используется с RemainAfterExit=yes. Эта опция говорит systemd считать сервис активным даже после завершения его процесса. Это полезно для сервисов, которые подготавливают систему (к примеру, монтируют диски или настраивают сеть), но сами по себе не имеют постоянно работающего процесса.

Когда использовать: для запуска скриптов инициализации, разовых задач, которые должны быть выполнены для старта других сервисов.

  • Type=notify

Поведение: процесс сервиса должен самостоятельно отправить systemd уведомление (сигнал sd_notify), когда он полностью проинициализировался и готов к работе. Systemd будет ждать этого уведомления, прежде чем считать сервис активным и запускать зависящие от него юниты.

Требования: приложение должно иметь поддержку API systemd для отправки уведомлений (через библиотеку libsystemd или низкоуровневый сокет).

Когда использовать: для современных демонов, которые изначально разрабатываются для работы под systemd (например, systemd itself, sshd). Это обеспечивает точное управление зависимостями.

  • Type=dbus - не самый распорстраненный

Поведение: сервис считается готовым, когда он регистрирует определенное имя на системной шине D-Bus.

Когда использовать: для сервисов, которые предоставляют интерфейс через D-Bus.

Пример конфигурации для forking-домена (например, веб-сервера):

[Unit]
Description=My Forking Web Service

[Service]
Type=forking
PIDFile=/run/my-webservice.pid
ExecStart=/usr/bin/my-webservice --daemon --pid-file=/run/my-webservice.pid
ExecReload=/bin/kill -HUP $MAINPID

[Install]
WantedBy=multi-user.target

3. Зависимости и порядок запуска

Systemd строит ориентированный граф зависимостей между юнитами. Анализируя директивы AfterBeforeRequiresWants, он вычисляет оптимальный порядок активации, позволяя запускать максимальное количество независимых юнитов параллельно. Не слепой параллелизм, а управляемое распараллеливание на основе явно заданных правил.

4. Cgroups и отслеживание процессов

Это фундаментальное техническое отличие от SysVinit. Systemd помещает каждый запущенный сервис в свою собственную cgroup (control group). Это позволяет:

  • Точно отслеживать все процессы службы: даже если главный процесс (ExecStart) породил множество дочерних процессов (fork), они все останутся внутри cgroup. Команда systemctl status точно покажет все процессы, принадлежащие сервису.

  • Изолировать и ограничивать ресурсы: через директивы юнита (MemoryMax=CPUQuota=) можно легко установить лимиты на потребление памяти, CPU, I/O сервисом.

  • Гарантированно завершать все процессы службы: при остановке сервиса systemd гарантированно завершает все процессы в его cgroup, устраняя проблему "зомби"-процессов.

Теория реализуется на практике с помощью двух инструментов (systemd-cgls и systemd-cgtop), которые предоставляет systemd для визуализации и мониторинга контрольных групп.


Практическое использование: инструменты systemd-cgls и systemd-cgtop.

  • Systemd-cgls - показывает иерархическое дерево всех существующих cgroup в системе, аналогично тому, как ls показывает файлы. Это лучший способ понять и увидеть структуру, которую создаёт systemd. Посмотрим на примере:

# Показать всё древо cgroup для сервиса nginx 
systemd-cgls /system.slice/nginx.service

# Будут показаны все процессы в данной cgroup:
# |-1234 /usr/sbin/nginx -g daemon off:
# |-1235 nginx: worker process
# |-1236 nginx: cache loader process
  • systemd-cgtop - показывает потребление ресурсов (CPU, память, ввод/вывод) всеми cgroup в реальном времени, аналогично тому, как top показывает это для процессов. Посмотрим пример вывода:

Control Group               Tasks   %CPU   Memory  Input/s Output/s
/                            257    15.8     3.9G       0B     0B
/system.slice/nginx.service    3     0.1    30.0M       0B     0B
/system.slice/mysql.service   45    12.5     1.1G       0B     0B
/user.slice/user-1000.slice  120     3.2   800.0M       0B     0B

5. Журналирование (journald)

  • Бинарный формат логов: в отличие от текстовых файлов syslogjournald по умолчанию использует структурированный бинарный формат для хранения логов (обычно в /var/log/journal/). Это позволяет эффективно индексировать и запрашивать логи по метаданным.

  • Метаданные: каждая запись в журнале содержит не только сообщение, но и массу системной информации: PIDUIDSYSTEMDUNITEXECMDLINE_HOSTNAME и т.д.

  • Запросы: инструмент journalctl позволяет строить сложные запросы, используя эти метаданные как поля для фильтрации:

# Логи для nginx с PID 1234 и логи для php-fpm
journalctl _SYSTEMD_UNIT=nginx.service _PID=1234 + _SYSTEMD_UNIT=php-fpm.service 

6. D-Bus

Взаимодействие между systemctl, другими утилитами (journalctlhostnamectl) и демоном systemd происходит через D-Bus (системную шину сообщений). Это обеспечивает стандартизированный, безопасный и гибкий IPC-механизм. Например, когда вы выполняете systemctl start nginxsystemctl отправляет D-Bus-сообщение методу StartUnit на системную шину, которое перехватывает и обрабатывает демон systemd.

Подробнее про D-Bus

D-Bus или Desktop Bus - система обмена сообщениями между программами в LInux, некий "посредник" для общения.

Понимаю, что кому-то может быть непонятно (когда-то в попытках разобраться - чуток утонул в официальном Wiki), поэтому приведу простую аналогию:

D-Bsu - диспетчер в такси. Человек (systemctl) звонит диспетчеру со словами: "Запустите службу nginx". Диспетчер (D-Bus) передаёт команду водителю (systemd), который её выполняет.

То есть, на практике:

# Используется команда
systemctl start nginx

# systemctl через D-Bus отправляет сообщение: "Запусти nginx"
# systemd через D-Bus получает это сообщение
# systemd запускает службу и через D-Bus возвращает статус

Практическое применение

Теория - всегда хорошо, но куда без практики? Далее рассмотрим практические примеры применения "всего и вся".

1. Управление состоянием службы: запуск, остановка, перезагрузка

Данные команды управляют текущим, активным состоянием службы. Действуют немедленно, не влияют на автозагрузку при следующей перезагрузке.

# Запускает службу немедленно
systemctl start <service_name>
# Например:
sudo systemctl start nginx
# Останавливает службу немедленно
systemclt stop <service_name>
# Например:
sudo systemctl stop sshd
# Полная перезагрузка службы. Последовательно выполнит stop, затем start
# Применяется, когда необходима загрузка с "чистого" состояния
systemctl restart <service_name>
# Например:
sudo systemctl restart apache2
# Перезагружает конфигурацию службы без остановки основного процесса
# Важно! Это не перезапускает дочерние службы. Сервис продолжает работать и обрабатывать запросы, лишь обновляя свои настройки
# Применяется, когда необходимо внести изменения в конфиг для минимизации простоев
systemctl reload <service_name>
# Например:
sudo systemctl reload nginx # обновится конфиг без разрыва соединений 
# Показывает подробный статус службы: 
# активен/неактивен, добавлен ли в автозагрузку, последние записи в лог, а также инфо о процессе 
systemctl status <service_name>
Например:
systemctl status cron

2. Управление автозагрузкой

Данные команды управляют тем, будет ли служба запущена автоматически при загрузке системы или при определенной цели (target). Не запускают и не останавливают службу немедленно.

# Включает автозагрузку службы при старте системы
systemctl enable <service_name>

Что она делает? Создает символические ссылки (симлинки) из юнит-файла службы (обычно в /usr/lib/systemd/system/) в соответствующие каталоги .wants/ для целей (например, multi-user.target.wants/ или graphical.target.wants). При запуске цели система видит эти ссылки и запускает все необходимые службы.

# Отключает автозагрузку службы
systemctl disable <service_name>

Что делает? Удаляет символические ссылки, тем самым исключая саму службу из процесса автозагрузки.

# Показывает статус автозагрузки службы
# Например: enabled, disabled, static, masked
systemctl is-enabled <service_name>

Использование в скрипте, как пример: if ! systemctl is-enabled --quiet nginx; then sudo systemctl enable nginx; fi

Также, хочу упомянуть про "масикровку", которая является "абсолютным запретом". Это альтернатива disableDisable лишь убирает симлинки, но сервис все еще можно запустить вручную через startMask создает симлинк на /dev/null, что делает запуск сервиса невозможным в принципе. Это критически важно для сервисов, которые конфликтуют между собой (например, разные сетевые менеджеры NetworkManager и systemd-networkd).

# Полностью запрещает запуск службы (любым способом)
sudo systemctl mask <service_name>
# Разрешает запуск службы (обратное действие)
sudo systemctl unmask <service_name>
# Например: если Вы используете networkd, стоит замаскировать NetworkManager
sudo systemctl mask NetworkManager.service

3. Просмотр состояния системы и служб

Эти команды помогают получить общую картину о состоянии всех служб в системе.

# Показывает краткий обзор состояния системы: 
# список загруженных юнитов, состояние по умолчанию, журналы...
systemctl status # без аргументов
# Возвращает состояние службы (active, inactive, activating, failed)
# Вывод краткий, подходит для скриптов
systemctl is-active <service_name>
# Например:
systemctl is-active docker # вернет active или inactive
# Проверяет, находится ли служюа в состоянии сбоя
# Возвращает failed (если не запустилась) или active/inactive иначе
# Полезно для автоматического оповещения
systemctl is-failed <service_name>
# Показывает все запущенные в данный момент сервисы
# Полезная команда для получения списка того, что работает на сервере
systemctl list-units --type=service --state=running
Альтернативный короткий синтаксис:
systemctl --type=service --state=running
# Покажет все доступные сервисы и статус их автозагрузки (enabled, disabled, static, masked)
# Команда показывает, будет ли запущена служба при загрузке, а не в данный момент
systemclt list-unit-files --type=service
Альтернативный короткий синтаксис:
systemctl --type=service --all

Статус static означает, что юнит-файл не имеет секции [Install], поэтому его нельзя "enable". Обычно запускается как зависимость другого сервиса.

Далее, в этом же блоке, стоит обратить внимание на возможность просмотра зависимостей:

# Показывает, от каких юнитов зависит данный сервис
systemctl list-dependencies <service_name>

# Показывает, какие юниты зависят от данного сервиса (кто его требует)
systemctl list-dependencies --reverse <service_name>

# Показывает дерево зависимостей в виде древовидной структуры
systemctl list-dependencies --tree <service_name>
# Например:
systemctl list-dependencies graphical.target --tree | head -20

# А также:
# --all - показать все зависимости (включая неактивные)
# --reverse или -r - показать, какие юниты зависят от указанного (кто использует эту службу)
# --before / --after - показать юниты, которые запускаются "до или после" указанного

Приведу конкретный пример для лучшего понимания. Например, необходимо проверить, какие службы зависят от сетевого менеджера:

systemctl list-dependencies --reverse NetworkManager.service

Это поможет понять, какие службы перестанут работать корректно, если остановить NetworkManager.

4. Просмотр логов конкретной службы

Основной инструмент для работы с журналами в systemd - journalctl. Все команды обычно требуют sudo для просмотра полного лога.

# Показывает все записи в журнале (логи) для указанной службы за всё доступное время
journalclt -u <service_name>
# Показывает логи службы с указанного времени
journalctl -u <service_name> --since today
# Например: 
sudo journalctl -u sshd --since "1 hour ago" # что происходило с SSH за последний час?

Фильтрация по времени позволяет сузить ввод, дабы не листать массивные файлы журналов. Вместо today можно использовать yesterday, "2025-01-01 00:00:00" и тд.

# "Режим" реального времени (follow). Команда выводит текущие логи
# и продолжает показывать новые записи по мере их появления
journalctl -u <service_name> -f
# Например: 
sudo journalctl -u apache2 -f # наблюдать за логами Apache в реальном времени

5. Поиск причин проблем с загрузкой

Эти команды помогают быстро найти службы, которые не смогли запуститься, и понять общую картину здоровья системы.

# Быстрый просмотр всех юнитов (не только служб), которые находятся в failed
systemctl --failed
# Покажет сообщения журнала с уровнем важности "ошибка" и выше (crit, alert...)
journalctl -p err..alert
# Показывает все логи с момента последней загрузки системы
# Полезно для анализа проблем, которые могли возникнуть в момент старта системы
journalclt -b
# Например:
sudo journalctl -b -1 # Что привело к падению системы в прошлый раз?

6. Анализ времени загрузки

Следующий блок - инструменты для анализа производительности и поиска "узких мест" в процессе загрузки.

# Показывает общее время, затраченное на загрузку ядра и пользовательского пространства
# Полезно для формирования общего представления о скорости загрузки
systemd-analyze
# Пример вывода:
Startup finished in 4.567s (kernel) + 1min 12.345s (userspace) = 1min 16.912s
# Демонстрирует скисок всех сервисов, которые дольше всего грузились
# Сортирует по времени их инициализации
systemd-analyze blame

Команда очень полезная, тк позволяет сразу увидеть, какая служба задерживает старт системы (к примеру, медленный сервис или антивирус). Помогает в оптимизации времени загрузки.

# Визуализирует цепочку запуска для конкретного сервиса
systemd-analyze critical-chain <service_name>
# Например:
systemd-analyze critical-chain graphical.targer # Почему граф. интерфейс так долго грузится?

Данная команда показывает зависимости: какие службы должны были запуститься до него, сколько времени занял каждый шаг. Выделяет "узкие места", задерживающие старт целевой службы.

Краткая шпаргалка для анализа:

Задача

Команда

Посмотреть логи службы

journalctl -u <имя_службы>

Логи в реальном времени

journalctl -u <имя_службы> -f

Кто не запустился?

systemctl --failed

Критические ошибки

journalctl -p err..alert

Логи прошлой загрузки

journalctl -b -1

Что грузилось дольше всех?

systemd-analyze blame

Почему грузился X?

systemd-analyze critical-chain

Разбор примеров

1. Создание и настройка простого сервиса

Итак, задача следующая: создать службу, которая запускает простой эхо-сервер на Python и гарантирует его работу (рестартит после падения).

Создаем скрипт сервиса (/usr/local/bin/echo-server.py):

#!/usr/bin/env python3
import socket

HOST = '0.0.0.0'
PORT = 12345

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.bind((HOST, PORT))
    s.listen()
    print(f"Echo server listening on {HOST}:{PORT}")
    while True:
        conn, addr = s.accept()
        with conn:
            print(f"Connected by {addr}")
            data = conn.recv(1024)
            if not data:
                break
            conn.sendall(data)

Создаем юнит-файл (/etc/systemd/system/echo.service):

[Unit]
Description=Simple Echo Server
Documentation=https://example.com
# Запускаться после того, как сеть будет готова
After=network.target

[Service]
Type=simple
# Указываем полный путь к интерпретатору и скрипту
ExecStart=/usr/bin/python3 /usr/local/bin/echo-server.py
# Перезапускать сервис всегда, если он завершился не так, как ожидалось
Restart=on-failure
# Ждать 5 секунд перед перезапуском
RestartSec=5
# Запускать от имени непривилегированного пользователя для безопасности
User=nobody
Group=nogroup

# Важные настройки безопасности для изоляции сервиса
# Запрещаем любые права root
NoNewPrivileges=yes
# Ограничиваем возможности процесса
CapabilityBoundingSet=
# Запрещаем запись в большинство файловых систем
ProtectSystem=strict
ProtectHome=read-only
PrivateTmp=yes

[Install]
WantedBy=multi-user.target

Применяем конфигурацию и управляем сервисом:

# Перезагружаем конфигурацию systemd для чтения нового юнита
sudo systemctl daemon-reload

# Включаем автозагрузку сервиса
sudo systemctl enable echo.service

# Запускаем сервис
sudo systemctl start echo.service

# Проверяем статус и логи
sudo systemctl status echo.service
journalctl -u echo.service -f

# Тестируем работу эхо-сервера
echo "Hello, Habr!" | nc localhost 12345

# Проверяем, что сервис перезапускается. Симулируем падение:
sudo kill -TERM $(systemctl show echo.service --property=MainPID --value)
# Через 5 секунд проверяем статус снова - сервис должен быть активен
sudo systemctl status echo.service

В данном примере я продемонстрировал создание сервиса с нуля, настройку политик, перезапуск и некоторые настройки безопасности, которые следует использовать по умолчанию.

2. Ограничение ресурсов сервиса с помощью cgroups

Задача: необходимо ограничить потребление памяти эхо-сервером до 50МБ и ограничить использование CPU, к примеру, до 50% одного ядра.

Модифицируем юнит-файл сервиса (/etc/systemd/system/echo.service), добавляя в секцию [Service]:

[Service]
...
# Memory Limits
# Жесткий лимит памяти. Процесс будет убит (OOM Killer), если превысит его.
MemoryMax=50M
# Мягкий лимит. Systemd начнет агрессивно пытаться освободить память (например, сбрасывая кеши),
# если процесс превысит этот лимит, но не убьет его сразу.
MemoryHigh=40M

# CPU Limits
# Ограничивает использование CPU до 50% одного ядра.
CPUQuota=50%
...

# После изменения файла не забываем
sudo systemctl daemon-reload
sudo systemctl restart echo.service

Проверяем ограничения:

# Смотрим, в какую cgroup помещен наш сервис
systemd-cgls /system.slice/echo.service

# Мониторим потребление ресурсов этой cgroup в реальном времени
systemd-cgtop -p /system.slice/echo.service

# Можно посмотреть параметры cgroup напрямую
cat /sys/fs/cgroup/system.slice/echo.service/memory.max
# Должно быть: 52428800 (50 МБ в байтах)

3. Поиск и устранение проблемы с помощью journalctl

Задача: найти причину падения службы my-app.service (запускается, но сразу падает).

# 1. Смотрим статус - видим, что сервис в состоянии failed
sudo systemctl status my-app.service
# В выводе часто уже есть последняя строка ошибки

# 2. Смотрим логи службы за последний запуск
journalctl -u my-app.service -n 20 --no-pager

# 3. Если лога нет или его недостаточно, смотрим логи с момента последней загрузки с подробным выводом
journalctl -u my-app.service --since "10 min ago" -o verbose
# Ключ `-o verbose` покажет все метаданные (например, UID, GID, командную строку),
# что может быть критически важно для диагностики

# 4. Допустим, в логах видна ошибка подключения к порту 5432.
# Ищем, что происходило с PostgreSQL (который должен предоставлять этот порт) в то же время
journalctl -u postgresql.service --since "10 min ago" | tail -20

# 5. Или ищем все сообщения об ошибках (уровень ERR и выше) в системе за этот период
journalctl -p err..alert --since "10 min ago"

# 6. Если проблема в разрешении зависимостей, смотрим, что мешает запуску
# Включаем подробный вывод systemd при запуске сервиса
sudo systemctl status my-app.service -l --no-pager
# Или пытаемся запустить вручную с выводом в консоль
sudo systemctl start my-app.service

Journalctl с правильными ключами фильтрации (-u, -p, -since, -o) - главный и невероятно полезный инструмент диагностики проблем со службами.

Заключение

Systemd - комплексная, глубоко интегрированная система инициализации и управления, которая стала неотъемлемым скелетом современных дистрибутивов Linux. Её архитектура, построенная вокруг концепции юнитов, cgroups и централизованного журналирования, кардинально меняет подход к администрированию системы.

В данном руководстве я постарался охватить все основные и ключевые технические аспекты и, что для меня лично являлось приоритетом, по возможности собрать всё воедино, без воды. Надеюсь, получилось.

Данная статья является продолжением статьи по cron, так как systemd был затронут, но не раскрыт.

P.S. В моей группе в Телеграмм разбираем практические кейсы: скрипты (Python/Bash/PowerShell), тонкости ОС и инструменты для эффективной работы.

Комментарии (24)


  1. vviz
    02.09.2025 14:04

    "Systemd - комплексная, глубоко интегрированная система инициализации и управления, которая..." превращает управление сервером в общение с "черным ящиком".
    Для домохозяек с десктопом - самое то. Но сервер... Я на 100% должен контролировать его состояние, а не кланятся systrmd с просьбой сказать мне, зачем он 100 раз перегрузил упавший демон, а потом "ой, всё". Бездумно перегружать, надеясь, что все чудесным образом исправится - это подход Microsoft... ИМХО.
    Мой выбор - Devuan


    1. DikSoft
      02.09.2025 14:04

      Бездумно перегружать, надеясь, что все чудесным образом исправится - это подход Microsoft... ИМХО.

      1) У MS это настраивается, один, два или более раз рестартануть нужно/можно. )

      MS way

      2) Если бы только их..., как работают контейнеры в K8S вы наверняка знаете?


      1. ReadOnlySadUser
        02.09.2025 14:04

        А что в контейнерах-то особо сложного? Сами контейнеры - это сахар над cgroups, namespaces и capabilities (и ещё парой плюшек безопасности в Linux). Машинерия сложная, но в целом мне понятная. А ведь даже не админ)


        1. DikSoft
          02.09.2025 14:04

          Т.е. там Вас подход "навернулось - пересоздай" не смущает?


          1. ReadOnlySadUser
            02.09.2025 14:04

            Почти)

            Навернулось -> залогировал -> отправил уведомление -> пересоздал.

            Вот так прям хорошо.

            Мой опыт работы в HFT говорит, что можно торговать миллиардами с таким подходом :)


            1. grumbler70
              02.09.2025 14:04

              Навернулось -> залогировал -> отправил уведомление -> пересоздал.

              Вот так прям хорошо

              Можно подумать, что эти уведомления хоть кто-то читает. Поубивал бы за такой подход, ставший повсеместным. Бардак и полный пофигизм.


              1. ReadOnlySadUser
                02.09.2025 14:04

                Так их и не надо читать) События надо классифицировать, группировать, и выводить статусы на дашборды.


                1. grumbler70
                  02.09.2025 14:04

                   и выводить статусы на дашборды.

                  Ага, типичный дашборд современного приложения:


                  1. ReadOnlySadUser
                    02.09.2025 14:04

                    Ожидаемый ответ


    1. Femistoklov
      02.09.2025 14:04

      Если бы все хардкорные программисты (да-да, плюсовики-линуксоиды) всегда нормально делали обработку ошибок, то и не было бы ситуаций "не работает, почему - неизвестно, перезагрузил - заработало". Так что не в ту сторону воюете.


      1. DikSoft
        02.09.2025 14:04

        Попробуйте эту же мысль высказать фанатикам микросервисов )

        Холивар будет обеспечен ))

        Адекватные промолчат, вайб-кодеры засыпят фразами про stateless и "а чо такова?"


      1. ReadOnlySadUser
        02.09.2025 14:04

        Почему бы не пойти дальше и сразу код без багов уже начать писать)


        1. Femistoklov
          02.09.2025 14:04

          Так код без багов не спасёт. Представьте: две какие-то утилиты несовместимые стоят и пытаются сделать одно и то же разными способами. Бага ни в одной из них нет и каждая по отдельности работает правильно, но вот только вместе их нельзя использовать.


          1. ReadOnlySadUser
            02.09.2025 14:04

            Но ведь это самый обычный race condition и в каждой утилите есть баг. Изменяемый ресурс надо блокировать на запись или использовать транзакции.


            1. Femistoklov
              02.09.2025 14:04

              Муж и жена хотят отвезти ребёнка в школу, каждый думает, что поедут на его/её машине. Жена первая берёт за руку ребёнка, выводит и садит в свою машину. Муж ждёт. Жена отпускает ребёнка, муж его берёт за руку и начинает вытаскивать из машины жены, чтобы посадить в свою. Вроде с общим ресурсом правильно обращаются (не тянут одновременно за руки в разные стороны), но всё равно что-то не то...


              1. ReadOnlySadUser
                02.09.2025 14:04

                1. Аналогии ничего не доказывают

                2. Аналогии в данном случае и не нужны. Тот же пример можно построить на любых объектах в ОС/БД и на каждый такой объект найдется своя лочка


                1. Femistoklov
                  02.09.2025 14:04

                  и на каждый такой объект найдется своя лочка

                  Вы в этом уверены? Что каждый объект всегда можно без негативных последствий заблокировать на любое нужное для правильной работы время?


                  1. ReadOnlySadUser
                    02.09.2025 14:04

                    Конечно уверен) При отсутствии конкуренции за ресурсы - все эти лочки почти ничего не стоят.

                    При наличии конкуренции за ресурсы - все эти лочки - необходимое зло и работает оно только в том случае, если корректно обеспечена синхронизация.

                    Без синхронизации система в принципе не работает, а значит добавление лочек не создаёт никакого влияния на работающую систему.


                    1. Femistoklov
                      02.09.2025 14:04

                      если корректно обеспечена синхронизация.

                      Без синхронизации система в принципе не работает

                      Ну началооось : )


    1. eternaladm Автор
      02.09.2025 14:04

       Бездумно перегружать, надеясь, что все чудесным образом исправится

      Поставьте вместо Restart=on-failuer - Restart=no и все проблемы с "бесконтрольной" перезагрузкой уйдут)

      А вот как раз на серверах Systemd работает отлично за счёт точного управления зависимостями, изоляции через cgroup и тд.


    1. mayorovp
      02.09.2025 14:04

      Я на 100% должен контролировать его состояние

      Иными словами, если демон упал - поднимать его вы будете строго вручную? Это полностью противоположно современным требованиям к серверам (да и не только современным).


      1. edo1h
        02.09.2025 14:04

        а что вы понимаете под «поднимать строго вручную»? сказать systemctl start xxx?


        1. mayorovp
          02.09.2025 14:04

          Любые действия, которые не могут произойти без вмешательства сисадмина.


  1. mayorovp
    02.09.2025 14:04

    Disable лишь убирает симлинки, но сервис все еще можно запустить вручную через start.

    Запуск вручную не так страшен, в конце концов можно просто не запускать вручную. Гораздо важнее что mask не даёт юниту запускаться даже если он напрямую указан в зависимостях другого юнита.