Systemd для администратора: полный гайд по управлению службами Linux
Когда Lennart Poettering выложил первый коммит systemd в 2010 году, интернет разделился на два лагеря. Одни называли это революцией, другие — катастрофой. Прошло полтора десятка лет, и спорить уже не о чем: systemd работает под капотом практически каждого Linux-сервера, который Вы встретите в продакшене. RHEL, Debian, Ubuntu, SUSE, Arch — все перешли. А значит, разбираться придётся.
Эта статья — не философский трактат «за и против». Это концентрированный гайд по управлению сервисами Linux через systemd: от базовых systemd команд до таймеров, логов и безопасности unit-файлов. Если Вы администрируете хотя бы один Linux-сервер — Вам сюда.
Что такое systemd и зачем он вытеснил SysVinit
SysVinit запускал демоны последовательно, один за другим, по цепочке shell-скриптов в /etc/init.d/. Каждый скрипт — отдельный bash-файл с start, stop, restart. Работало? Работало. Медленно, непредсказуемо, с race conditions на зависимостях — но работало. Хотите узнать, почему сервис не стартовал? Читайте 200-строчный bash-скрипт и гадайте, где именно он свалился.
Systemd подошёл к задаче принципиально иначе: параллельный запуск процессов, отслеживание через cgroups, декларативные конфигурационные файлы вместо императивных bash-простыней. Сервер с SysVinit загружался за 40–90 секунд — systemd сокращает это время до 10–20 секунд на том же железе. Для организации с сотнями серверов разница между минутой и пятнадцатью секундами простоя при перезагрузке — это деньги и SLA.
Но systemd — это не «улучшенный init». Это целая экосистема, которая тянет за собой:
- journald — централизованное бинарное логирование с индексацией и фильтрацией
- systemd-networkd — настройка сетевых интерфейсов без NetworkManager
- systemd-timesyncd — синхронизация времени (замена ntpd для простых сценариев)
- systemd-resolved — локальный DNS-резолвер с кэшированием
- таймеры — полноценная замена cron с привязкой к событиям, а не только к расписанию
Systemd умеет заменять cron (таймеры с привязкой к событиям и персистентностью), watchdog (встроенный контроль heartbeat), chroot (пространства имён и sandboxing), часть fstab (автомонтирование через .mount и .automount юниты) и отдельные сетевые утилиты. Зоопарк разрозненных демонов и конфигов схлопывается в единую систему с единым интерфейсом. Для организации это означает стандартизацию: одни и те же systemd команды работают на RHEL, Ubuntu, Debian и SUSE, а новые сотрудники не тратят недели на изучение уникальных особенностей каждого сервера.
Unit-файлы: анатомия управления
Вся магия systemd строится на unit-файлах. Unit — это декларативное описание «чего-то, чем systemd должен управлять». Типов юнитов хватает:
| Тип unit | Расширение | Назначение |
|---|---|---|
| service | .service | Демоны и фоновые процессы |
| socket | .socket | Сокетная активация (ленивый запуск сервиса по первому подключению) |
| timer | .timer | Планировщик задач (замена cron) |
| mount | .mount | Точки монтирования |
| target | .target | Группировка юнитов (аналог runlevel) |
| slice | .slice | Распределение ресурсов через cgroups |
| path | .path | Реакция на изменения в файловой системе |
Unit-файлы живут в трёх местах, и приоритет между ними имеет значение. /etc/systemd/system/ — Ваши кастомные юниты и переопределения, они побеждают всё остальное. /run/systemd/system/ — рантайм-юниты, пропадают после перезагрузки. /lib/systemd/system/ (или /usr/lib/systemd/system/) — юниты из пакетов дистрибутива, которые трогать руками не стоит. Если нужно изменить поведение пакетного юнита — копируйте его в /etc/systemd/system/ или используйте drop-in через systemctl edit.
Типичный service-файл выглядит так:
[Unit]
Description=My Application Server
After=network.target postgresql.service
Requires=postgresql.service
[Service]
Type=notify
User=appuser
Group=appgroup
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/bin/server --config /etc/myapp/config.yaml
Restart=on-failure
RestartSec=5
WatchdogSec=30
[Install]
WantedBy=multi-user.target
Секция [Unit] задаёт зависимости: After определяет порядок запуска, Requires — жёсткую зависимость (если postgresql упал — наш сервис тоже остановится). Есть ещё Wants — мягкая зависимость, при которой падение связанного сервиса не утянет за собой Ваш. Выбор между Requires и Wants — это осознанное архитектурное решение, а не формальность.
Секция [Service] описывает, как именно запускать процесс, под каким пользователем, что делать при падении. Параметр Type= заслуживает отдельного внимания: simple (по умолчанию) считает сервис запущенным сразу, forking ждёт, пока процесс форкнется, notify — пока сервис сам сообщит о готовности через sd_notify. Неправильный Type — одна из частых причин, по которой systemctl зависает при старте сервиса или считает его упавшим.
Политика рестартов тоже заслуживает осмысленного подхода. Restart=on-failure перезапустит сервис только при ненулевом коде выхода или сигнале, Restart=always — при любом завершении, включая штатное. RestartSec=5 добавляет паузу между падением и перезапуском, а StartLimitBurst=5 и StartLimitIntervalSec=60 ограничивают количество попыток — без них systemd будет бесконечно пытаться поднять заведомо неработающий сервис.
Секция [Install] указывает, в какой target включить сервис при автозагрузке служб. WantedBy=multi-user.target — это эквивалент старого runlevel 3, стандартный режим для серверов без графики.
Systemctl: швейцарский нож администратора
Systemctl — единственная утилита, через которую Вы будете взаимодействовать с systemd в 90% случаев. Забудьте service, chkconfig, update-rc.d — всё это теперь обёртки или рудименты.
Базовые операции — старт, стоп, перезапуск, статус:
systemctl start nginx.service
systemctl stop nginx.service
systemctl restart nginx.service
systemctl status nginx.service
Суффикс .service можно опускать, systemctl поймёт и без него. Но для таймеров, сокетов и маунтов указывать тип обязательно — иначе systemctl попытается угадать, и не всегда верно.
Есть ещё reload — для сервисов, которые умеют перечитывать конфигурацию без полного перезапуска (nginx, PostgreSQL, HAProxy). А try-restart перезапустит сервис, только если он уже запущен — удобно для скриптов, где неизвестно текущее состояние.
Управление автозагрузкой служб:
systemctl enable nginx # включить автозапуск
systemctl disable nginx # отключить автозапуск
systemctl enable --now nginx # включить и сразу запустить
systemctl is-enabled nginx # проверить статус автозагрузки
Команда enable создаёт симлинк в директории нужного target, disable — удаляет его. Никакой магии.
Полезные команды для диагностики:
systemctl list-units --failed # упавшие юниты
systemctl list-dependencies nginx # дерево зависимостей
systemctl show nginx --property=MainPID # конкретное свойство
systemctl cat nginx # содержимое unit-файла
systemctl edit nginx # безопасное переопределение через drop-in
Отдельного внимания заслуживает systemctl edit. Эта команда создаёт drop-in файл в /etc/systemd/system/nginx.service.d/override.conf, который переопределяет нужные параметры без редактирования оригинального юнита. При обновлении пакета Ваши изменения не потеряются. А systemctl edit --full откроет полную копию юнита для редактирования, если drop-in недостаточно.
После любых изменений в unit-файлах нужен systemctl daemon-reload — без него systemd продолжит работать со старой версией конфигурации. Это частая ловушка: правите файл, перезапускаете сервис, а изменения не применились.
Ещё одна полезная команда — systemctl mask. В отличие от disable, которая просто убирает сервис из автозагрузки, mask создаёт симлинк на /dev/null, делая запуск сервиса невозможным даже вручную. Используйте это, когда нужно гарантировать, что никто случайно не запустит что-то опасное на продакшен-сервере.
Логи systemd: journald вместо grep по /var/log
Journald собирает логи всех юнитов в единое бинарное хранилище с метаданными. Вам не нужно угадывать, в какой файл демон пишет свои логи, не нужно настраивать ротацию для каждого сервиса отдельно, не нужно парсить разнородные форматы. Всё собрано, проиндексировано и доступно через одну утилиту — journalctl.
Для руководителей и тех, кто отвечает за compliance, это тоже имеет значение: централизованные логи с временными метками и привязкой к юнитам упрощают аудит и расследование инцидентов. Если Вы готовитесь к проверке, посмотрите отдельно на подготовку серверной инфраструктуры к ИБ-аудиту — journald там играет не последнюю роль. Вопрос «что происходило на сервере между 2:00 и 2:15?» решается одной командой, а не экспедицией по десятку лог-файлов.
journalctl -u nginx # логи конкретного сервиса
journalctl -u nginx --since "1 hour ago" # за последний час
journalctl -u nginx -f # follow-режим (аналог tail -f)
journalctl -p err # только ошибки
journalctl -b -1 # логи предыдущей загрузки
journalctl --disk-usage # сколько места занимают логи
Фильтрация по приоритету (-p), по времени (--since, --until), по юниту (-u), по PID — всё это работает с единым хранилищем. Когда в три часа ночи нужно разобраться, почему упал сервис, разница между «грепать десять файлов» и «один запрос к journalctl» ощущается физически.
Формат вывода тоже гибкий. По умолчанию journalctl показывает привычный syslog-подобный формат, но с помощью -o json можно получить структурированный JSON для скриптов и автоматизации. А -o verbose выдаст все метаданные записи: PID, UID, имя юнита, приоритет, уникальный идентификатор сообщения. Это бывает критично при разборе сложных инцидентов, когда нужно восстановить точную последовательность событий.
Ротация логов настраивается в /etc/systemd/journald.conf:
[Journal]
SystemMaxUse=2G
SystemMaxFileSize=256M
MaxRetentionSec=30day
Таймеры: почему cron уже не нужен
Systemd-таймеры — это полноценная замена cron, и во многих аспектах они удобнее. Таймер — это пара файлов: .timer (расписание) и .service (что запускать).
# /etc/systemd/system/backup.timer
[Unit]
Description=Daily backup timer
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
RandomizedDelaySec=600
[Install]
WantedBy=timers.target
Persistent=true — если сервер был выключен в момент запуска, задача выполнится при следующей загрузке. RandomizedDelaySec — случайная задержка до 10 минут, чтобы сотня серверов не ударила в бэкап-хранилище одновременно. Если Вы строите резервное копирование на базе Proxmox, полезно заранее разобраться с настройкой Proxmox Backup Server для виртуальных машин — таймеры systemd и PBS хорошо работают в связке.
Чем таймеры лучше cron? Логи задачи идут в journald автоматически — больше никакого >> /var/log/backup.log 2>&1. Зависимости от других сервисов задаются декларативно. Ресурсные лимиты через cgroups работают из коробки — Ваш бэкап-скрипт не сожрёт всю память на сервере. И никакого парсинга писем от crond с надеждой, что MAILTO настроен правильно.
Синтаксис OnCalendar мощнее cron-выражений. OnCalendar=Mon,Fri *-*-* 09:00:00 — запуск по понедельникам и пятницам в 9 утра. OnCalendar=*-*-01 00:00:00 — первого числа каждого месяца. Проверить расписание можно через systemd-analyze calendar "Mon,Fri *-*-* 09:00:00" — утилита покажет следующие запланированные срабатывания.
Безопасность и изоляция служб
Unit-файлы дают мощные инструменты sandboxing, и пренебрегать ими — значит оставлять дверь открытой. Большинство администраторов знают про User= и Group=, но на этом останавливаются. А между тем systemd позволяет выстроить многослойную защиту, которая ограничивает влияние скомпрометированного сервиса на остальную систему. Несколько директив, которые стоит добавлять в каждый production-сервис:
[Service]
# Запуск от непривилегированного пользователя
User=appuser
Group=appgroup
# Файловая система
ProtectSystem=strict # / монтируется read-only, кроме /dev, /proc, /sys
ProtectHome=true # /home, /root, /run/user недоступны
ReadWritePaths=/var/lib/myapp # явные исключения
PrivateTmp=true # изолированный /tmp
# Сеть и ядро
PrivateNetwork=true # нет доступа к сети (если не нужна)
NoNewPrivileges=true # запрет эскалации привилегий
ProtectKernelTunables=true # /proc/sys и /sys read-only
Systemd через cgroups позволяет ограничить ресурсы каждого сервиса: процессорное время (CPUQuota=200% — лимит в два ядра), память (MemoryMax=2G — жёсткий потолок, при превышении OOM killer убьёт процесс), IOPS (IOWriteBandwidthMax=/dev/sda 50M). Один взбесившийся процесс не положит весь сервер — cgroups не дадут ему сожрать все ресурсы. Это принципиально меняет уровень изоляции между сервисами на одном хосте — тот же механизм лежит в основе ресурсного контроля в гипервизорах: если Вы выбираете платформу виртуализации для продакшена, принципы изоляции останутся теми же.
Проверить текущий уровень защиты юнита можно командой:
systemd-analyze security nginx.service
Команда покажет оценку от 0 до 10 (где 0 — полная изоляция) и подсветит, какие директивы стоит добавить.
Мониторинг и метрики: что systemd даёт «из коробки»
Systemd отдаёт информацию о состоянии каждого сервиса структурированно. Это данные, готовые для интеграции с системами мониторинга:
systemctl show nginx --property=ActiveState,SubState,NRestarts,ExecMainStartTimestamp
NRestarts — счётчик рестартов за текущий цикл жизни. Если видите, что сервис рестартовал 47 раз за сутки — это не «работает», это flapping, и проблему нужно искать в логах, а не радоваться, что Restart=on-failure справляется. Journald дополняет картину: частота ошибок, время отклика watchdog, причины каждого рестарта — всё это доступно без дополнительных агентов мониторинга.
Для анализа загрузки есть systemd-analyze:
systemd-analyze # общее время загрузки
systemd-analyze blame # рейтинг самых медленных юнитов
systemd-analyze critical-chain # цепочка критического пути загрузки
Эти данные помогают не только «тушить пожары», но и выстраивать проактивный подход к управлению инфраструктурой. Аудит состояния служб, отслеживание деградации, планирование ёмкости — всё начинается с видимости, и systemd эту видимость обеспечивает.
Интеграция с внешними системами мониторинга тоже упрощается. Prometheus-экспортёр для systemd умеет вытягивать состояние юнитов, счётчики рестартов, использование ресурсов через cgroups — и всё это без написания кастомных скриптов. Grafana-дашборд со списком всех сервисов, их аптаймом и частотой падений — вопрос получаса настройки, а не недели разработки.
Что дальше
Systemd продолжает развиваться. Systemd-homed меняет подход к управлению домашними директориями, портируя пользовательские данные между машинами. Systemd-oomd реагирует на нехватку памяти до того, как ядро начнёт убивать процессы через OOM killer, давая администратору шанс сохранить контроль. Systemd-sysext расширяет корневую файловую систему иммутабельными слоями — привет, контейнерный подход на уровне хоста.
Нравится Вам это или нет — systemd задаёт стандарты того, как Linux-системы управляются в продакшене. Разобраться в нём глубоко — это не факультативное упражнение. Это часть профессии. И чем раньше Вы перестанете относиться к systemd как к «чёрному ящику, который просто запускает демоны», тем увереннее будете чувствовать себя в три часа ночи, когда что-нибудь обязательно упадёт.


