На большинстве Linux-серверов для управления службами используется systemd. Он содержит инструменты для работы процессом загрузки и взаимодействия с сервисами системы. Это означает, что systemd является важным механизмом в работе любого сервера. В этой статье мы познакомимся с его функциональностью и как с его помощью можно управлять работой процессов и демонов.
Systemd: что это и для чего используется
Systemd — это фундаментальная подсистема инициализации, менеджер служб и набор инструментов для операционных систем на базе Linux. Её основная задача: управление всеми процессами, службами и ресурсами в современной Linux-системе. 
Ранее для этих задач использовалась система SysVinit или просто init. Она была простой, но и не лишенной недостатков. Давайте кратко рассмотрим основные особенности SysVinit:
- Использует традиционный линейный подход к инициализации системы (службы запускаются в порядке очереди по порядку), что обеспечивает простоту. Может замедлять загрузку в сложных системах, но подходит для систем с меньшим количеством зависимостей сервисов друг от друга;
- Управление осуществляется через обычные shell-скрипты;
- Не обеспечивает логирование по умолчанию. Требуются дополнительные службы.
Systemd фактически вытеснила в 2010-е годы традиционную подсистему SysVinit. Изначально разработанная сотрудниками компании RedHat, уже сейчас эта система содержится почти во всех популярных дистрибутивах Linux. Основные отличия Systemd:
- Может выполнять инициализацию и запуск служб параллельно, если это допускается их конфигурацией;
- Управление службами осуществляется через через специальные сущности — юниты (units);
- По умолчанию поддерживает ведение логов системы.
Есть немало противников systemd, потому что она нарушает один из первоначальных постулатов философии Unix: «Пишите программы, которые делают что-то одно и делают это хорошо». Также у критиков есть ряд других аргументов, например, что исходный код излишне запутанный и содержит множество намеренных багов и уязвимостей.
Но нельзя отрицать, что управление службами стало удобнее, нагляднее и обеспечило стандартизированный подход к управлению службами в различных дистрибутивах Linux. Эта практическая польза для системных администраторов и разработчиков стала одним из решающих факторов, благодаря которому systemd, несмотря на критику, была принята как основа для подавляющего большинства современных дистрибутивов.
Обзор systemd: место на этапе загрузки, типы юнитов и конфигурации служб
В этом разделе мы разберем, как именно systemd интегрирована в процесс загрузки Linux, познакомимся с основными типами конфигурационных файлов (юнитов), а затем детально изучим строение одного из них на практическом примере. 
При подаче питания на сервер загрузка системы проходит несколько этапов. И на одном из последних управление передаётся от загрузчика (чаще всего это GRUB) к systemd. Systemd становится первым процессом в системе с PID 1 и все остальные процессы порождаются от него. Он инициирует загрузку системы, запуская критически важные процессы и службы в соответствии с конфигурацией. В отличие от SysVinit, запускающего процессы строго последовательно, systemd параллельно инициирует службы, учитывая их зависимости и условия.
Systemd работает с конфигурационными файлами, называемыми юнитами (units), которые описывают службы, сокеты, таймеры и другие компоненты системы.
Юнит — это описание сущности в файле конфигурации, которой управляет systemd. Обычно юниты создаются при установке служб на сервере, но некоторые образуются автоматически из других конфигурационных файлов, динамически — из состояния системы или программно — во время выполнения.
Давайте рассмотрим немного подробнее основные типы, которые используются чаще всего:
- service — с этим типом предстоит сталкиваться чаще всего, поскольку именно сервисы управляют демонами в системе. Демон в Linux — это программа, которая работает в фоновом режиме, без прямого участия пользователя, выполняя системные задачи или предлагая службы другим программам.
- socket — описывает конечную точку связи для обмена данными, например, в сети (сетевой сокет, к которому обращаются извне) или между процессами на одной машине (IPC-сокет, т. е. сокет межпроцессного взаимодействия, как Unix domain socket), и позволяет запускать сервис только тогда, когда к нему обращаются.
- timer — таймеры запускают сервисы по расписанию (аналог cron, но с точностью до миллисекунд и поддержкой зависимостей). Преимущества перед cron — может срабатывать относительно времени последнего запуска (например, «каждые 12 часов с момента предыдущего запуска»).
- target — цели, которые определяют текущий и возможные уровни выполнения системы, группируя юниты для достижения определенного состояния системы, т. е. являются аналогом runlevel в SysVinit.
Чтобы увидеть, как устроены юниты на практике, подробно рассмотрим пример конфигурации одного из самых распространенных типов — службы (.service). В качестве примера возьмем конфигурацию для сервера SSH в Ubuntu:
~# cat /lib/systemd/system/ssh.service
[Unit]
Description=OpenBSD Secure Shell server
Documentation=man:sshd(8) man:sshd_config(5)
After=network.target auditd.service
ConditionPathExists=!/etc/ssh/sshd_not_to_be_run
[Service]
EnvironmentFile=-/etc/default/ssh
ExecStartPre=/usr/sbin/sshd -t
ExecStart=/usr/sbin/sshd -D $SSHD_OPTS
ExecReload=/usr/sbin/sshd -t
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartPreventExitStatus=255
Type=notify
RuntimeDirectory=sshd
RuntimeDirectoryMode=0755
[Install]
WantedBy=multi-user.target
Alias=sshd.serviceВ файле юнита можно увидеть несколько секций:
- [Unit] — секция для общего описания юнита. Конкретно в этой конфигурации есть краткое описание (Description), ссылки на документацию man (Documentation), указание на порядок запуска (после network.target и auditd.service) и условие запуска (если файл /etc/ssh/sshd_not_to_be_runотсутствует);
- [Service] — секция для определения запуска и работы сервиса. Конкретно в этой конфигурации есть:- определение файла окружения /etc/default/sshи если он отсутствует, то это не приведёт к проблеме;
- команды для запуска и перезагрузки конфигурации — ExecStartPre, ExecStart и ExecReload соответственно;
- правила для завершения процесса и опции для его перезапуска — KillMode, Restart. RestartPreventExitStatus=255 означает, что с завершением процесса с кодом 255 его перезагрузки не будет — этот код обычно означает проблемы с конфигурацией, поэтому в этом случае перезапуск не нужен;
- тип запуска -type — в этом случае notify будет ждать от процесса сигнала о готовности к подключениям от процесса sshd;
- указание на каталог для запуска и временных файлов процесса (RuntimeDirectory и RuntimeDirectoryMode);
 
- определение файла окружения 
- [Install] — другие опции для запуска сервиса. В этом случае секция указывает, какой уровень загрузки системы (runlevel) необходим для запуска (WantedBy) и псевдоним для юнита (Alias).
Из файла конфигурации юнита systemd понимает, в какой момент запускать службу и какие опции необходимо использовать для корректной работы.
Кроме того, в юнитах могут описываются параметры WantedBy или After — именно они показывают systemd, в какой момент запускать юнит. Посмотреть все дерево зависимостей можно через команду:
systemctl list-dependencies UNIT 
Также можно посмотреть, какие юниты зависят от конкретного юнита с модификацией команды:
systemctl list-dependencies UNIT --reverse 
После краткого обзора конфигурации юнитов можно перейти к рассмотрению способов управления ими.
Основные команды для управления юнитами
После запуска службы на сервере можно проверить его работу и статус с помощью systemctl. Это основная утилита для взаимодействия со службами на сервере. Она также может не только отображать статус, но и останавливать, запускать, проверять наличие автоматического запуска при загрузке ОС и много чего ещё.
Просмотр конфигурации
Возвращаясь к примеру с конфигурацией юнита ssh, можем получить её с помощью следующей команды:
systemctl cat UNIT 
Это бывает удобно, когда нужно быстро посмотреть, какие опции юнит предоставляет, и как systemd им управляет без указания конкретного пути до конфигурации, просто указывая название юнита. Также подобный синтаксис позволяет увидеть включенные в конфигурацию другие файлы, отличные от основного. Синтаксис cat работает не только со службами, но и с таймерами и сокетами. Если вывод команды переполнен различными комментариями и пустыми строками, то убрать все лишнее поможет следующая команда:
systemctl cat UNIT | egrep -v "^$|#"По сути, предыдущая команда cat выводит только ту информацию, в которой описывается юнит, но это далеко не все параметры, какие используются. Большая часть параметров systemd определены неявно и применяются по умолчанию. Чтобы посмотреть полный список , используем команду:
systemctl show UNIT [-p PARAMETR]Через дополнительный ключ -p можно указать, какой из множества параметров нужно вывести. Допускается выводить сразу несколько параметров. Например, получим те, которые использует systemd для nginx:
 
Если мы захотим проверить все параметры, то список будет довольно большим:
 
Дополнительную информацию по возможным параметрам конфигурации юнитов и служб можно найти в документации man:
man 5 systemd.service
man 5 systemd.unitПоиск юнитов и их полный список
Прежде чем перейти к рассмотрению команд непосредственного управления юнитами, необходимо найти нужную службу. В системе довольно много юнитов, о существовании которых даже не догадываешься, пока не стоит задача по управлению конкретной службой через systemd. Получить список юнитов можно через специальную команду:
systemctl list-unitsПо умолчанию эта команда выводит все известные юниты в ОС, группируя их по типу. И, как следствие, сложно найти нужный юнит:
 
Поэтому лучше использовать специальные фильтры при вызове команды:
systemctl list-units [--type=service | --type=socket | --type=timer]Дополнительно можно указать ключ --no-pager, который позволит вывести весь список сразу, что может быть удобно для поиска через grep. Эту опцию можно использовать во многих командах systemd. В ином случае предыдущая команда переходит в режим просмотра, как при вызове утилиты less, и не даёт полного вывода сразу.
Также можно отфильтровать службы по их статусу, то есть какие юниты запущены, остановлены штатно или же с ошибкой:
systemctl  --state [ running |  exited | failed | dead]Управление юнитом
Получить текущий статус определенной службы можно с помощью команды:
systemctl status UNITРассмотрим вывод команды status на примере веб-сервера nginx:
 
На скрине выше цифрами обозначены следующие компоненты в выводе команды:
- Краткое описание службы.
- Основные параметры службы, такие как конфигурационный файл и наличие службы в автозагрузке.
- Дополнительные конфигурации, которые включены в момент запуска.
- Параметры работы службы — такие как текущий статус (active) и время с момента последнего запуска, основной PID, использование CPU и RAM, к какому CGroup (control group) служба относится и текущие дочерние PID.
После всех параметров службы отображаются последние записи в journald (системный журнал в составе systemd, который собирает и хранит логи всех компонентов системы).
Остановить, запустить и перезапустить службу можно следующим образом:
systemctl stop UNIT
systemctl start UNIT
systemctl restart UNIT 
Допускается указывать сразу несколько юнитов для запуска и остановки, например, так:
systemctl stop nginx apache2
systemctl start nginx apache2 
В одном из предыдущих примеров взят nginx. И по своей конфигурации он допускает перезагрузку конфигурации без перезапуска службы, поэтому, чтобы выполнить перезагрузку конфигурации юнита, можно воспользоваться командой:
systemctl reload UNITФакт перезапуска службы (после команды reload) видно в последних логах journald, которые выводятся при просмотре статуса:
 
Добавление в автозагрузку
Добавить или убрать службы из автозагрузки можно, выполнив следующие команды:
systemctl disable UNIT # убираем
systemctl enable UNIT # добавляем 
При этих операциях видно, что именно делает systemd. При добавлении службы в автозагрузку он создаёт ссылку из каталога /lib/systemd/system в каталог /etc/systemd/system/multi-user.target.wants.
Проверить, добавлена ли служба в автозапуск, можно по статусу службы в выводе status или через команду проверки (возвращает либо enabled, либо disable):
systemctl is-enabled UNITЖурнал systemd
Для сбора информации о работе ОС systemd имеет встроенный инструмент journald. С помощью него можно просмотреть основные события в системе и отследить работу определённых служб/юнитов. Управление осуществляется через утилиту journalctl. Разберем основные возможности при работе с журналом.
Файл конфигурации, место хранения журналов в системе и их очистка
Кратко рассмотрим, где хранятся его логи. По умолчанию (в конфигурации описывается как auto) логи systemd хранятся в каталоге /var/log/journal. Сама конфигурация службы содержится в файле /etc/systemd/journald.conf.
В нем можно указать желаемое поведение службы и настроить хранение логов. После изменения файла необходимо перезапустить службу через systemd:
systemctl restart systemd-journaldЕсли этого каталога /var/log/journal нет в системе (например, он был ранее удалён через rm), то логи хранятся в каталоге /run/log/journal.
Важный момент.  Удаление каталога /var/log/journal — плохая идея. Так как  события будут записываться исключительно RAM и в псевдофайловом каталоге /run/log/journal, соответственно, после перезагрузки все записи будут утеряны. 
Для проверки, сколько места занимают журналы, лучше воспользоваться командой:
journalctl --disk-usageЧтобы очистить журнал до нужного размера или времени, задайте в команде необходимые лимиты:
journalctl [--vacuum-size= | --vacuum-time=] 
Просмотр журнала
Чтобы запустить просмотр журнала системы, введите в консоли:
journalctlКоманда выведет все события, которые есть в журнале. Для удобства просмотра можно воспользоваться фильтрами. Например, если нас не интересуют логи системы и нужна информация только по определенной службе, то мы можем воспользоваться командой:
journalctl -u UNIT [--no-pager]Она выведет все записанные логи в журнале.
Можно также фильтровать вывод по времени. Например, в случае, если нас интересует конкретное время или интервал, используем следующие команды:
journalctl -u SERVICE --since "YYYY-MM-DD HH:MM:SS" --until "YYYY-MM-DD HH:MM:SS"
journalctl -u SERVICE --since "YYYY-MM-DD HH:MM:SS" # до текущего времени
journalctl -u SERVICE --since "-1d" # события за 1 день
journalctl -u SERVICE --since "-1h" # события за 1 час
journalctl -u SERVICE --since "-1m" # события за 1 минутуВыводы и итоги
Мы кратко рассмотрели systemd и какими сущностями он может управлять. Разобрали, как использовать systemctl для управления службами и где можно изучить журналы systemd, если возникнет необходимость, показав основные команды и их полезные модификаторы.
Конечно, это далеко не полный перечень возможных взаимодействий со службами через systemd (и другими компонентами системы), а только базовые возможности. Но мы надеемся, что статья будет полезна для выполнения повседневных задач, а также в качестве хорошей основы для более глубокого погружения в тему.
 
    