Управление службами в GNU/Linux #
Васильев Андрей Михайлович, 2024
Версии презентации
Загрузка системы #
Существует несколько стандартов для реализации систем загрузки
- BIOS, Basic Input Output System
 - UEFI, Unified Extensible Firmware Interface
 - EFI, Extensible Firmware Interface
 - Множество закрытых решений
 
Решают общие задачи:
- Первоначальная инициализация устройств
 - Поиск загрузчика ОС на устройствах хранения
 - Поиск загрузчика ОС на сетевых устройствах
 
Загрузчик ОС #
Задачей загрузчика ОС является поиск ядра ОС с дальнейшей передачей ему управления
Современные загрузчики:
- Являются отдельными бинарными данными на ПЗУ
 - Обычно располагаются на ФС в отдельном разделе в виде файла
 - Настраиваются с помощью внешнего файла-конфигурации
 - Могут считывать данные из файловых систем
 - Могут иметь интерактивный интерфейс для выбора ОС
- Решение для загрузки разных ОС
 - Решение для загрузки разных ядер для одной ОС
 
 
Ядро ОС #
Ядро ОС обеспечивает эффективное использование технического обеспечения множеством конкурирующих друг с другом процессов
Операционная система помимо ядра предоставляет набор системных приложений
- Позволяющих настраивать параметры работы ядра
 - Позволяющих управлять процессами внутри ОС
 
Файлы ядра Linux #
В GNU/Linux для загрузки ОС применяется связка из
- ядра ОС
 - initrd, Initial RAM Disk, образа файловой системы, необходимой ядру для первичной загрузки операционной системы
 
Оба компонента поставляются файлами, лежащими на ФС
В рамках initrd поставляются:
- Модули ядра ОС, необходимые для работы с оборудованием на данном компьютере
 - Служебные приложения, нужные для загрузки модулей и настройки системы
 
Модули могут поставляться отдельно от ядра:
- Для поддержки проприетарных решений
 - Для решения задачи поддержки множества разных конфигураций оборудования
 
Процесс загрузки ОС #
В рамках загрузки ядра ОС происходит поиск всего оборудования и его инициализация
После того как всё оборудование инициализировано, ядро запускает процесс-инициализации init
- Процесс работает до окончания работы ОС
 - Процесс имеет идентификатор, равный 1
 - Процесс запускает все остальные процессы в системе
 
Существует множество систем инициализации: SysVinit, systemd, openrc, runit, initware
- Для выбора системы инициализации ядру можно передать аргумент init с полным путём к исполняемому файлу
 - Сравнение разных систем предоставляет проект Gentoo
 
Задачи системы инициализации #
- Запуск служб, постоянно работающих фоновых процессов
- В процессе запуска операционной системы
 - При обращении к службе посредством сети, D-Bus, сокету и т.д.
 
 - Перезапуск служб в случае их падения
 
Система инициализации может также выполнять действия других системных служб
- Подключение файловых систем (обычно да)
 - Настройка сетевых интерфейсов (обычно нет)
 - Запуск задач по расписанию (обычно нет)
 - Журналирование работы служб (обычно нет)
 - Управление входом пользователей в систему (обычно нет)
 - Создание виртуальных файлов для устройств (обычно нет)
 - Управление временем системы (обычно нет)
 
Стадии загрузки ОС #
При запуске системы обычно рассматриваются следующие стадии:
- Настройка ключевых служб (диски, системы обмена сообщениями и т.д.)
 - Поддержка работы сессии суперпользователя
 - Поддержка работы множества пользователей
 - Запуск служб, обеспечивающих продуктовые функции системы (SSH, Веб-сервер, …)
 - Поддержка работы с графическим пользовательским интерфейсом
 
Также система инициализации должна выполнять корректное выключение системы, т.е. выключать службы в правильном порядке и сохранить данные на ПЗУ в случаях:
- Завершения работы
 - Перезагрузки системы
 
Система инициализации позволяет переходить между стадиями, выбирать разные стадии при запуске
Варианты реализации стадий #
| SysV Runlevel | systemd Target (Цель) | Записки | 
|---|---|---|
| 0 | poweroff.target, runlevel0.target | Выключить систему. | 
| 1, s, single | rescue.target, runlevel1.target | Режим одного пользователя. | 
| 2, 4 | multi-user.target, runlevel2.target, runlevel4.target | Определённые пользователем (системой). По умолчанию 3 | 
| 3 | multi-user.target, runlevel3.target | Многопользовательская инициализация, не графическая. Пользователи могут входить по сети или через терминалы. | 
| 5 | graphical.target, runlevel5.target | Многопользовательская с поддержкой графического интерфейса. | 
| 6 | reboot.target, runlevel6.target | Перезагрузка. | 
| emergency | emergency.target | Экстренный доступ для суперпользователя. | 
Службы #
Служба обычно представляет собой некоторое приложение, которое постоянно запущено и которое предоставляет полезные услуги пользователю или другим приложениям
- Приложение реагирует на некоторые сигналы и выполняет некоторые действия
- Сигнал от ядра ОС о добавлении нового устройства
 - Сигнал по шине взаимодействия, запрос на выполнение операции
 - Сигнал по сетевому интерфейсу, выполнение действия
 
 - Приложению для работы необходимо, чтобы все его зависимости были доступны на момент запуска:
- Файловая система с файлами службы уже доступна на момент запуска
 - Сетевой интерфейс уже настроен и готов обрабатывать запросы
 - Служба базы данных запущена и готова обрабатывать запросы
 
 
Службы также могут представлять собой действия, выполняющиеся с некоторой регулярностью, например по расписанию
Указание зависимостей #
Точное указание зависимостей позволяет ускорить загрузку системы, избежать проблем при запуске служб
- В классическом SysVInit необходимо было следить за включением службы в нужный уровень выполнения
- Подразумевается, что на третьем уровне уже доступны файловая система и сеть
 - На последнем уровне можем надеяться, что служба базы данных тоже доступна
 
 - Следующие системы инициализации предложили способы для указания зависимостей на уровне конкретных служб
 - В рамках systemd каждая служба указывает список зависимостей:
- Явные зависимости от других служб
 - Неявные зависимости через включение в конкретную цель
 
 
Настройка службы #
Вопрос запуска службы помимо момента запуска достаточно сложный:
- Где располагается исполняемый файл?
 - Какие аргументы необходимо передать исполняемому файлу?
 - Какие аргументы передавать через переменные окружения?
 - С правами какого пользователя нужно запустить файл?
 - Какие элементы должны быть доступны службы?
 
Помимо запуска есть много вопросов поддержки работы:
- Что делать в случае остановки службы?
 - Как корректно выполнить перезапуск службы?
 - Как проверить текущий статус работы службы?
 
Классическое решение #
Задача настройки параметров запуска службы достаточно сложная, разработчиками SysVInit решена не была, поэтому процесс запуска служб делегировали Shell-скриптам
- Требует высокой квалификации от разработчика
 - Требуют запуска интерпретатора Shell для своей работы
 - Сложно реализовать систему отслеживания работы
 - Невозможно реализовать другие способы запуска:
- Запуск службы при обращении по шине D-Bus
 - Запуск службы по расписанию
 - Запуск службы по обращению к порту
 
 
Каждый из последних потребуют написания своей конфигурации
Настройка systemd #
Для описания всех компонентов systemd используются файлы модулей (unit), в которых описывается поведение конкретного компонента. В настоящий момент systemd поддерживает следующие виды модулей:
| Тип модуля | Описание | 
|---|---|
| Target | Синхронизационная точка для ряда модулей. Используется во время загрузки системы в определённом состоянии. | 
| Service | Модуль, который запускает, останавливает, перезапускает, даёт команду на обновление конфигурации различных служб, например Веб-сервер. | 
| Timer | Модуль данного типа активирует или деактивирует указанную службу по времени наступления таймера или истечения времени. | 
| Socket | Модуль данного типа активирует или деактивирует службу, когда приходит входящее подключение по указанному сокету. | 
| Device | A unit of this type implements device-based activation such as a device driver. | 
| Тип модуля | Описание | 
|---|---|
| Mount | A unit of this type controls the file-system mount point. | 
| Automount | A unit of this type provides and controls on-demand mounting of file systems. | 
| Swap | A unit of this type encapsulates/activates/deactivates swap partition. | 
| Path | A unit of this type monitors files/directories and activates/deactivates a service if the specified file or directory is accessed. | 
| Snapshot | A unit that creates and saves the current state of all running units. This state can be used to restore the system later. | 
| Slice | A group of units that manages system resources such as CPU, and memory. | 
| Scope | A unit that organizes and manages foreign processes. | 
| busname | A unit that controls DBus system. | 
Каталоги конфигурации systemd #
Список можно посмотреть в man-руководстве systemd.unit
- Пути для поиска модулей системного уровня
/etc/systemd/system.control/*/run/systemd/system.control/*/etc/systemd/system/*— Файлы, созданные администратором/run/systemd/system/*/lib/systemd/system/*— Файлы, установленные вместе с пакетом приложения- …
 
 - Пути для поиска модулей пользовательского уровня
~/.config/systemd/user.control/*~/.config/systemd/user/*$XDG_CONFIG_DIRS/systemd/user/*/etc/systemd/user/*$XDG_RUNTIME_DIR/systemd/user/*/run/systemd/user/*- …
 
 
Настройка systemd-службы #
Конфигурационные файлы служб systemd являются текстовыми файлами в формате INI
- Все типы конфигурационных файлов являются unit-файлами
 - Все элементы конфигурации unit-файлов можно написать в других типах файлов
 - Полную документацию можно найти в страницах 
systemd.unit,systemd.service,systemd.timerи т.д - Ввиду многообразия задач, решаемых systemd, конфигурационные файлы достаточно сложные
 
Простая служба на systemd #
[Unit]
Description=Start n service
After=default.target
[Service]
Type=simple
ExecStart=/usr/bin/n
[Install]
WantedBy=default.target- Секция 
Unitописывает общую информацию о службе, часть unit-файловDescription— краткое описание назначения службеAfter— указание зависимости одной службы от другой
 - Секция 
Installописывает параметры автоматического запуска службы при старте системы, часть unit-файловType— поведение приложения после запускаWantedBy— указание зависимости от цели запуска
 - Секция 
Serviceописывает детали запуска службыExecStart— команда для запуска приложения, должна включать полный путь к исполняемому файлу, может включать аргументы
 
Что нужно настроить #
Требований к параметрам работы служб много — скорее всего на настоящий момент в systemd есть поддержка для решения данной задачи
Указание учётной записи #
Если не указать пользователя, то приложение будет работать с правами суперпользователя, что является проблемой для большинства служб
В секции Service необходимо указать пользователя с помощью параметра User
Настройка параметров перезапуска #
Служба не всегда ведёт себя как следует и может упасть. Политика перезапуска определяется в секции Service с помощью параметра Restart
on-failure— перезапускать в случае паденияalways— перезапускать даже когда хорошо завершилось
Добавление новых служб #
- Создайте текстовый файл службы в каталоге 
/etc/systemd/system- Созданный файл должен иметь расширение 
.service - Предположим, что файл называется 
example.service 
 - Созданный файл должен иметь расширение 
 - Заполните необходимые поля в созданном файле:
- Заполните секцию 
[Unit], указав описание и зависимости - Заполните секцию 
[Service], указав параметры запуска приложения - Заполните секцию 
[Install], указав параметры автоматического запуска 
 - Заполните секцию 
 - Скажите демону systemd перечитать конфигурацию с жёсткого диска, выполнив команду 
systemctl daemon-reload - Запустите службу средствами systemd, выполнив команду 
systemctl start exampleили целикомsystemctl start example.service - Посмотрите на статус запущенной службы 
systemctl status example 
Для остановки службы используйте команду systemctl stop example
Автоматический запуск службы #
Команды start и stop приложения systemctl пытаются запустить или остановить процесс, который находится под управлением systemd
Для настройки автоматического запуска необходимо:
- Добавить секцию 
[Install]внутри конфигурационного файла - Выполнить команду 
systemctl enable example.service 
Последняя команда настраивает запуск службы при старте ОС, не запускает её сейчас
- Для запуска службы необходимо вызвать команду 
start - При включении автоматического запуска можно передать аргумент 
--now:systemctl enable --now example.start. В этом случае будет настроен автоматический запуск при старте и будет выполнена попытка запуска службы сейчас 
Для выключения автоматического systemctl disable example или systemctl disable --now example
Управление службами systemd #
Анализ поведения системы
| Дейтвие | Команда | 
|---|---|
| Отобразить состояние systemd | systemctl status | 
| Отобразить список запущенных модулей | systemctl или systemctl list-units | 
| Отобразить список упавших модулей | systemctl --failed | 
| Отобразить список конфигурационных файлов модулей | systemctl list-unit-files | 
| Отобразить статус модуля по PID процесса | systemctl status PID | 
Анализ поведения службы
| Дейтвие | Команда | Комментарий | 
|---|---|---|
| Отобразить страницу руководства, ассоциированную с данной службой | systemctl help UNIT | 
если указана в конфигурации модуля | 
| Отобразить статус модуля | systemctl status UNIT | 
показывает запущен или остановлен модуль | 
| Проверить включён ли модуль | systemctl is-enabled UNIT | 
Управление состоянием службы. Все в данном разделе требуют прав суперпользователя
| Дейтвие | Команда | 
|---|---|
| Запустить модуль | systemctl start UNIT | 
| Остановить работу модуля | systemctl stop UNIT | 
| Перезапустить модуль | systemctl restart UNIT | 
| Дать команду службе перечитать конфигурацию | systemctl reload UNIT | 
| Перечитать конфигурацию systemd | systemctl daemon-reload | 
| Настроить автоматический запуск модуля при старте системы | systemctl enable UNIT | 
| Настроить автоматический запуск модуля при старте системы и сразу запустить его | systemctl enable --now UNIT | 
| Отключить автоматический запуск модуля при старте системы | systemctl disable UNIT | 
| Отключить и настроить автоматический запуск модуля | systemctl reenable UNIT | 
Управление состоянием системы #
Данные действия надо выполнять от имени суперпользователя
| Дейтвие | Команда | 
|---|---|
| Выключить ОС и перезагрузить компьютер | systemctl reboot | 
| Выключить ОС и обесточить компьютер | systemctl poweroff  | 
| Приостановить работу ОС с сохранением данных в оперативной памяти | systemctl suspend | 
| Приостановить работу ОС с сохранением данных в разделе подкачки | systemctl hibernate | 
| Приостановить работу ОС с гибридным сохранением состояния | systemctl hybrid-sleep | 
Запуск служб по расписанию #
Ряд служб могут быть приложениями, которые выполняют поставленную задачу:
- Выполняют резервное копирование
 - Выполняют обновление ключей безопасности
 - Очищают каталоги с журналами
 
Они не работают постоянно, а выполнив свою задачу, отключаются
Для управления такого рода службами можно добавить таймеры, которые будут выполнять запуск службы согласно некоторому расписанию
Виды таймеров #
systemd поддерживает следующие таймеры:
- Таймеры по расписанию: активируются при наступлении некоторого события по времени
 - Монотонные таймеры: выполняют действия через некоторое время после наступления события
 
Настройка таймера для службы #
- Конфигурационные файлы таймера располагаются рядом с 
.service-файлами службы - Расширение файла с конфигурацией таймера — 
.timer - Название файлов без расширения должны совпадать
 - Описание полей находится в man-странице 
systemd.timer 
Простой таймер #
Предположим, что мы описали службу с помощью файла system-n.service
Создадим таймер, который будет запускать эту службу каждую ночь
[Unit]
Description=Запускаем службу каждую ночь
[Timer]
OnCalendar=01:00
[Install]
WantedBy=timers.targetДля проверки параметров запуска по календарю удобно воспользоваться приложением systemd-analyze calendar 01:00
Запуск таймера #
После добавления конфигурационного файла таймера его также, как и службу необходимо:
- Запустить с помощью команды 
systemctl start - Настроить автоматический запуск с помощью 
systemctl enable 
Автоматический запуск таймера #
Таймеры сами по себе не предоставляют какую-либо дополнительную функциональность в отличие от служб, поэтому их надо связывать с целью timers.target.