Systemd – очень быстрый старт
При работе в операционной системе нужно постоянно запускать разные программы. Следить за их состоянием. Перезапускать упавшее. Существует целый пласт утилит, который решает эту задачу (от простейшего init.d до навороченного svc). Сейчас в Linux стандартом де-факто стал systemd – его используют все современные дистрибутивы. Это – очень короткое и очень простое введение в systemd. Минимум текста – максимум пользы.
SystemD – это менеджер загрузки. Он получает управление от ядра при старте операционной системы (init), запускает разные сервисы и следит за их состоянием (например – перезапускает упавшие). Идеолог – Леннар Поттеринг из RedHat. Systemd очень своеобразная штука, умеет она довольно много и устройство у нее довольно сложное. Systemd заслуженно любят за мощный и гибкий функционал – это здорово облегчает жизнь разработчика и админа. Systemd заслуженно ненавидят за тягу к изобретению уже реализованных в операционной системе вещей и спорное поведение в некоторых вопросах безопасности.
Эта статья – краткое практическое руководство. Теории тут – минимум. Если вас интересует устройство systemd – вам в официальное руководство.
Введение и терминология
Systemd отвечает за запуск программ и сервисов после старта операционной системы. Он следит за процессом загрузки, решает что запустить и как. Строго говоря, он делает ровно то же самое, что делал SystemV init. Systemd писали значительно позже init – в нем пытались починить вещи, которые в init сделаны плохо. Например, в init нет возможности сделать зависимость запуска одного сервиса от другого. Так же init не поддерживает параллельной загрузки. Параллельная загрузка сервисов радикально ускоряет старт операционной системы.
Unit
Unit – это описание сервиса (в широком смысле этого слова). Unit-файл описывает все настройки сервиса, как его запускать, когда (очередность, зависимости) и что делать, если запуск не удался. Unit-ы, которые пишет пользователь руками – должны находится в /etc/systemd/system
и иметь окончание .service
в названии. Юниты, которые устанавливают пакеты – находятся в ином месте. Если в нескольких папках лежит юнит с одним и тем же именем – применяется тот, что лежит в /etc/systemd/system
. Пример юнита:
[Unit]
Description=etcd – highly-available key value store
Documentation=https://github.com/coreos/etcd
Documentation=man:etcd
After=network.target
Wants=network-online.target
[Service]
Environment=DAEMON_ARGS=
Environment=ETCD_NAME=%H
EnvironmentFile=-/etc/default/%p
WorkingDir=/var/lib/etcd
Type=notify
User=etcd
PermissionsStartOnly=true
ExecStart=/usr/bin/etcd $DAEMON_ARGS
Restart=on-abnormal
RestartSec=10s
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
Я специально взял юнит посложнее, чтобы пример был наглядным. На что обратить внимание:
- Description – человеко-читаемое описание. Показывается по команде
service <name> status
- After – начать загрузку после того, как начнется загрузка сервиса (или цели)
- Wants – опциональная зависимость. Подробнее ниже, в разделе про зависимости
- Environment – создать переменную окружения при запуске этого сервиса
- WorkingDir – демон запускается из этой папки. Аналогично
cd /var/lib/etcd
перед запуском - Type – тип сервиса. Подробнее ниже
- User – имя пользователя, от которого будет запущен сервис
- PermissionsStartOnly – используется, если перед стартом нужна какая-то специальная подготовка – создание папок, изменение прав и так далее. При
PermissionsStartOnly=true
эти действия будут выполнятся от root. Без – от имени User - ExecStart – что, собственно, запускать. Обязательно полный путь
- RestartOn – при каких условиях перезапускать
- WantedBy – в какой target должен быть установлен сервис. Подробнее – в разделе про target-ы
Виды Unit-ов
Systemd может обслуживать процессы с разным поведением. Тип описывает, как systemd будет с ним взаимодействовать. Есть следующие варианты:
Type=Simple
– самый стандартный тип. Процесс остается в foreground, stdout перехватывается systemd. Это тип по умолчанию.Type=Forking
– прямая противоположность. Процесс должен форкнуться и отсоединится от foreground. Для этого типа юнитов должен быть указан pid через директивуPIDFile
.Type=oneshot
– процесс, который успешно выполняется (не делая fork) и завершается. Пример – монтирование файловых систем. Рекомендуется добавитьRemainAfterExit=yes
в юнит, чтобы результаты работы процесса остался в статусе юнита.Type=notify
– аналог simple, но в этом случае сам процесс сообщит systemd о том, что он закончил загрузку и готов к работе.
Взаимодействие с unit-ами
После каждого изменения файла юнита (создание/изменение/удаление) – нужно перечитывать изменения, так как состояния юнитов systemd кеширует:
systemctl daemon-reload
Запус, состояние, остановка:
#запуск
systemctl start [unit]
service [unit] start
#состояние
systemctl status [unit]
service [unit] status
#остановка
systemctl stop [unit]
service [unit] stop
#сервис в автозагрузку
systemctl enable [unit]
#полностью запретить запуск сервиса (даже команда service [unit] start не поможет)
systemctl mask [unit]
#разрешить запуск обратно
systemctl unmask [unit]
Systemd имеет свою собственную реализацию логирования (хотя по умолчанию в syslog копию сообщения он тоже отправляет). Чтение сообщений от сервисов – командой journalctl. Команда очень мощная, умеет много. Ниже примеры
#чтение информации по юниту
journalctl -u [UNIT]
#чтение по PID
journalctl _PID=12
#аналогично по конкретному файлу
journalctl /usr/bin/atd
#чтение информаций о юнитах, завершившихся с ошибкой
journalctl -xn
#чтение журнала с момента загрузки
journalctl -b
#чтение журнала с определенного момента
journalctl --since="2018-01-24 10:15:10"
journalctl --since "10 minutes ago"
#постоянное отслеживание событий (аналог tail -f)
journalctl -f
#по умолчанию systemd обрезает строки по длине экрана. Запретим ему это:
journalctl -l
#фильтры можно комбинировать
journalctl -u redis -f -l --since "10 minutes ago"
Управление зависимостями, очередность загрузки юнитов
Для управления зависимостями в unit есть ключевые слова Wants
, Requires
и After
:
After
– сервис начнет загрузку после того, как начнет загружаться сервис, указанный вAfter
.Wants
– сервис начнет загрузку после того, как закончит загружаться сервис, указанный вWants
. Статус загрузки этого сервиса не важен – даже если он упал и загрузится не смог – юнит попытается стартовать. То есть зависимость эта опциональная, и нужна она только для того, чтобы наш сервис начал загружаться не раньше, чем другой – закончит.Requires
– сервис начнет загрузку после того, как сервис, указанный вRequires
закончит загрузку успешно. Если сервис-зависимость загрузится не смог – наш сервис так же упадет с ошибкой (точнее – он даже не будет стартовать).
Targets
Target – целевое состояние системы. Именно Target определяет, какие сервисы будут загружены и в каком порядке. Аналог из мира sysV init – runlevel. Основные виды таргетов:
poweroff
– отключение системыrescue
– режим восстановления, однопользовательский (init 1)multi-user
– сетевой режим без графической оболочки, (init 3)graphical
– сетевой режим с графической оболочкой (init 5)reboot
– перезагрузкаemergency
– аварийная командная строка, минимальный функционал
Цели могут наследоваться друг от друга. Например, graphical включает в себя загрузку всего, что есть multiuser + после этого – подгрузку графической оболочки.
Взаимодействие с целями:
#список целей
systemctl list-units --type=target
#перейти в нужную цель (например – загрузится из сетевого режима в графический)
systemctl isolate graphical.target
#выбрать target по умолчанию
systemctl set-default multi-user.target
Заключение
systemd на данный момент - стандарт в linux-based операционных системах. Инструмент мощный, удобный и популярный, пусть и не без особенностей. Надеюсь, эта статья поможет начать им пользоваться.