Управление службами Linux. Пишем systemd Unit файл Запуск и остановка сервиса

Systemd Unit — это скрипт, который выполняет различные действия (все то, что пропишешь в него). Я обычно пишу такие скрипты для запуска различных служб которые компилирую руками или когда использую готовое ПО.

Что дадут вам Systemd юниты?

Units — это объекты, которыми может управлять система (В основном — стандартизированное представление системных ресурсов, которыми может управлять набор демонов).

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

Некоторые функции, которые легко реализовать:

  • Активация на основе сокетов : Сокеты, связанные с сервисом, лучше всего вырываются из самого демона, чтобы обрабатываться отдельно. Это дает ряд преимуществ,- например, задержка запуска службы до тех пор, пока соответствующий сокет не будет доступен. Это также позволяет системе создавать все сокеты до начала запуска процесса, что позволяет параллельно загружать связанные службы.
  • Активация на основе шины : Unit-ы также могут быть активированы на интерфейсе шины, предоставляемом D-Bus-ом. Устройство может быть запущено когда соответствующая шина доступна.
  • Активация на основе пути: Unit может быть запущен на основе активности или наличия определенных путей к файловой системе. Это использует inotify.
  • Активация на основе устройства : Unit-ы также могут быть запущены при первой доступности (подключении) связанного оборудования за счет использования событий udev.
  • Неявное сопоставление зависимостей: Большая часть структуры зависимостей для юнитов может быть построена самой системой. Вы все равно можете добавить информацию о зависимостях, но большая часть тяжелой работы будет решена за вас.
  • Экземпляры и шаблоны: Файлы блока шаблонов могут использоваться для создания нескольких экземпляров одного и того же общего устройства. Это позволяет создавать небольшие вариации или единичные unit-ы, которые обеспечивают одну и ту же общую функцию.
  • Простое упрощение безопасности: Юниты могут реализовать некоторые довольно хорошие функции безопасности, добавив простые директивы. Например, вы можете указать какой доступ можно использовать (чтение, запись) при работе с файловой системой, ограничить возможности ядра, установить приватный /tmp фолдер и сетевой доступ.
  • Drop-ins и snippets : Units можно легко расширить, предоставив фрагменты, которые будут отменять части файла системы. Это позволяет легко переключаться между vanilla и индивидуальными реализациями.

Есть много других преимуществ, которые имеют системные юниты над рабочими элементами других систем, но это должно дать вам представление о мощности, которую можно использовать с помощью собственных конфигурационных директив.

Как я могу найти/ Где находятся Systemd Unit в Unix/Linux?

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

Все копии системных файлов системы (я имею ввиду всех юнитов) можно найти в /lib/systemd/system каталоге. Файлы, хранящиеся здесь, могут быть запущены и остановлены по требованию, но во время сеанса. Это будет общий файл ванильной единицы, который часто записывается сторонними разработчиками проекта, которые должны работать в любой системе, которая развертывает systemd в своей стандартной реализации. Вы не должны редактировать файлы в этом каталоге. Вместо этого вы должны переопределить файл (если необходимо) используя другое расположение файла, которое заменит файл в этом месте.

Если вы хотите изменить работу некоторого устройства, то для этого нужно перейти в /etc/systemd/system директорию, потому что — файлы, найденные в этом каталоге, имеют приоритет над любыми другими местами в файловой системы.

Если вы хотите переопределить только определенные директивы из системных unit файлов, вы можете фактически предоставить фрагменты unit файла в подкаталоге. Они будут добавлять или изменять директивами копии системы, позволяя указать только параметры, которые вы хотите изменить.

Правильный способ сделать это — создать каталог с именем после файла с добавлением «.d» в конце. Таким образом, для единицы под названием my_test.service нужно создать подкаталог my_test.service.d. В этом каталоге файл, заканчивающийся на.conf, может использоваться для переопределения или расширения атрибутов unit файла.

Также есть место для определения run-time unit-ов в /run/systemd/system. Файлы, найденные в этом каталоге, имеют приоритет между /etc/systemd/system и/lib/systemd/system.

Файлы в этом месте дают меньший приоритет, чем прежнее местоположение, но больший приоритет, чем последнее. Сам процесс systemd использует это местоположение для динамически создаваемых unit файлов, созданных во время выполнения. Этот каталог можно использовать для изменения поведения устройства системы в течение всего сеанса. Все изменения, внесенные в этот каталог, будут потеряны при перезагрузке сервера.

Типы systemd Unit файлов

Самый простой способ определить тип устройства — это посмотреть на его суффикс, который добавляется к концу имени ресурса (юнита). В следующем списке описаны типы unit-ов, доступных для systemd:

  • .service : Данный юнит описывает, как управлять службой или приложением на сервере. Он в себе будет включать запуск или остановку службу, при каких обстоятельствах она должна автоматически запускаться, а также информацию о зависимости для соответствующего программного обеспечения.
  • .socket : Файл сокет-устройства описывает сетевой, IPC-сокет или FIFO буфер, который systemd использует для активации сокета. Они всегда имеют связанный.service файл, который будет запущен при сокет активности, которая определяет этот блок.
  • .device : Юнит, который описывает устройство, которое было указано как необходимое для управления systemd с помощью udev или файловой системы sysfs. Не все устройства имеют файлы.device. Некоторые скрипты, которым может потребоваться устройства.device, предназначены для монтирования и доступа к устройствам.
  • .mount : Этот блок определяет точку монтирования в системе, которой управляет systemd. Они называются по пути монтирования, при этом «/» изменены на тире. Записи внутри /etc/fstab могут иметь блоки, созданные автоматически.
  • .automount : Модуль.automount настраивает точку монтирования, которая будет автоматически установлена и должен быть определен после точки монтирования, на которую они ссылаются, и должны иметь соответствующий модуль.mount для определения особенностей монтирования.
  • .swap : Данный юнит, описывает SWAP (пространство подкачки) в системе. Название этих блоков должно отражать путь к устройству или файлу.
  • .target : Данный юнит используется для обеспечения точек синхронизации для других устройств при загрузке или изменения состояний. Они также могут использоваться для перевода системы в новое состояние.
  • .path : Данный юнит, определяет путь, который может использоваться для активации на основе пути. По умолчанию, юнит будет запущен с тем же базовым именем. Это использует inotify для отслеживания изменения путей.
  • .timer : Этот юнит, определяет таймер, который будет управляться systemd (аналог cron джоб) для задержки или запланированной активации. При достижении таймера будет запускаться соответствующий блок.
  • .snapshot : Это юнит, создается автоматически командой snapshot systemctl. Он позволяет вам восстановить текущее состояние системы после внесения изменений. Снимки не сохраняются во время сеансов и используются для отката временных состояний.
  • .slice : Он связан с узлами группы управления Linux, что позволяет ограничить ресурсы или назначить их для любых процессов, связанных с slice. Имя отражает его иерархическую позицию внутри дерева групп. Юниты размещаются в определенных slice по умолчанию в зависимости от их типа.
  • .scope : Они создаются автоматически systemd из информации которую получили от его интерфейса шины. Они используются для управления наборами системных процессов, созданных извне.

Как вы можете видеть, существует множество различных юнитов с которыми взаимодействует система. Многие из них работают вместе, чтобы добавить функциональности. В основном, используется.service из-за их полезности.

Структура systemd Unit файлов

Внутренняя структура файлов организована с помощью разделов. Разделы обозначаются двумя квадратными скобками «[» и «]» с именем раздела, заключенного внутри. Каждый раздел продолжается до начала следующего раздела или до конца файла.

Названия разделов хорошо определены и учитывают регистр. Таким образом, раздел не будет интерпретироваться правильно, если он записан как . Если вам нужно добавить нестандартные разделы для анализа (отличными от systemd), вы можете добавить X-префикс к имени раздела.

В этих разделах поведение устройства и метаданные определяются с помощью простых директив с использованием формата ключа-значения с назначением, обозначенным знаком равенства, например:

Directive1=value Directive2=value .......

В случае переопределения файла (например, содержащегося в каталоге unit.type.d) директивы могут быть сброшены путем назначения пустой строкой. Например, копия единичного файла системы может содержать директиву, заданную таким образом:

Directive1=default_value

Значение default_value можно исключить в файле переопределения, указав Directive1 (без значения), например:

Directive1=

Сейчас рассмотрим каждый из юнитов по отдельности.

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

Хотя порядок разделов не имеет значения для systemd при парсинге файла, этот раздел часто размещается сверху, потому что он предоставляет обзор устройства. Некоторые общие директивы, которые вы найдете в разделе :

  • Description= : Эта директива может использоваться для описания имени и основных функций устройства. Он возвращается различными инструментами systemd, поэтому полезно установить что-то короткое, конкретное и информативное.
  • Documentation= : Эта директива предоставляет местоположения документации. Это могут быть внутренние доступные справочные страницы, либо доступные в интернете (URL-адреса). Команда «status system your_service» выведет эту информацию.
  • Requires= : В этой директиве перечислены все юниты, от которых зависит этот юнит (служба или устройство). Если текущий блок активирован, перечисленные здесь юниты также должны успешно активироваться, иначе он потерпит неудачу. Эти блоки запускаются параллельно с текущим устройством по умолчанию.
  • Wants= : Эта директива похожа на Requires=, но менее строгая. Systemd будет пытаться запустить любые юниты, перечисленные здесь, когда это устройство активировано. Если эти устройства не обнаружены или не запущены, текущий блок будет продолжать функционировать. Это рекомендуемый способ настройки большинства зависимостей. Опять же, это подразумевает параллельную активацию, если не изменено другими директивами.
  • BindsTo= : Эта директива аналогична Requires =, но также приводит к остановке текущего устройства, когда соответствующий узел завершается.
  • Before= : Юниты, перечисленные в этой директиве, не будут запущены до тех пор, пока текущий блок не будет отмечен как запущенный, если они будут активированы одновременно.
  • After= : Юниты, перечисленные в этой директиве, будут запущены до запуска текущего устройства (юнита).
  • Conflicts= : Данный юнит можно использовать для отображения юнитов, которые нельзя запускать одновременно с текущим устройством. Запуск устройства с этой связью приведет к остановке других устройств.
  • Condition…= : Существует ряд директив, начинающихся с условия, которые позволяют администратору протестировать определенные условия до запуска устройства. Это можно использовать для предоставления файла универсального элемента, который будет запускаться только в соответствующих системах. Если условие не выполнено, юнит — пропускается.
  • Assert…= : Подобно директиве что выше (Condition…=), но установленные директивы проверяют различные аспекты рабочей среды, чтобы решить, следует ли активировать устройство. Однако, в отличие от директив Condition…=, отрицательный результат вызывает сбой в этой директиве.

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

В самом конце файла, расположен — секция. Этот раздел является дополнительным и используется для определения поведения или юнита, если он включен или отключен. Включение устройства означает, что он автоматически запускается при загрузке. По сути, это достигается путем фиксации рассматриваемого устройства на другом блоке, который находится где-то в строке единиц, которые нужно запустить при загрузке.

Из-за этого, только юниты, которые могут быть включены, будут иметь этот раздел. Директивы внутри, говорят что должно произойти, когда устройство включено:

  • WantedBy= : Данная директива, является наиболее распространенным способом определения того, как устройство должно быть включено. Эта директива позволяет вам указать зависимость (аналогично директиве Wants = в разделе ). Разница в том, что эта директива включена в вспомогательную единицу, позволяющую первичному блоку оставаться относительно чистым. Когда устройство с этой директивой определено, то будет создана в системе папка в /etc/systemd/, названная в честь указанного устройства, но с добавлением «.wants» в самом конце. В этом случае будет создана символическая ссылка на текущий блок, создающий зависимость. Например, если текущий блок имеет WantedBy = multi-user.target, каталог с именем multi-user.target.wants будет создан в/etc/systemd/system (если он еще не доступен) и символическая ссылка на текущий блок будут размещены внутри. Отключение этого устройства удаляет связь и удаляет зависимость.
  • RequiredBy= : Эта директива очень похожа на директиву WantedBy =, но вместо этого указывает требуемую зависимость, которая приведет к сбою активации, если не будет выполнена. Когда включено, юнит с этой директивой создаст каталог, заканчивающийся на.requires.
  • Alias= : Эта директива позволяет включить устройство под другим именем. Среди других применений это позволяет доступным нескольким провайдерам функции, чтобы соответствующие подразделения могли искать любого провайдера с общим псевдонимом.
  • Also= : Эта директива позволяет включать или отключать блоки в качестве набора. Здесь можно указать поддерживающие устройства, которые всегда должны быть доступны, когда это устройство активно. Они будут управляться как группа для задач установки.
  • DefaultInstance= : Для template units (более поздних), которые могут создавать экземпляры блоков с непредсказуемыми именами, это может использоваться как резервное значение для имени, если соответствующее имя не предоставляется.

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

Директива Type= может быть одной из следующих:

  • simple : Основной процесс службы указан в стартовой строке. Это значение по умолчанию, если директивы Type= и Busname= не установлены, но установлен ExecStart=. Любое сообщение должно обрабатываться вне устройства через второй блок соответствующего типа (например, через блок.socket, если эта служба должна обмениваться данными с помощью сокетов).
  • forking : Этот тип службы используется, когда служба форкает дочерние процессы при мгновенном покидании родительского процесса. Это сообщает systemd, что процесс все еще работает, даже несмотря на то, что родитель завершил сеанс. Хорошо подходит, например для запуска php-fpm, nginx, tomcat.
  • oneshot : Этот тип указывает, что процесс будет недолговечным и система должна ждать завершения процесса, прежде чем продолжить работу с другими устройствами. Значение по умолчанию для Type= и ExecStart= не установлены. Он используется для одноразовых задач.
  • dbus : Это означает, что устройство будет использовать имя от D-Bus шины. Когда это произойдет, systemd продолжит обработку следующего блока.
  • notify : Это указывает на то, что служба отправит уведомление, когда закончит запуск. Процесс systemd будет ждать, пока это произойдет, прежде чем переходить к другим устройствам.
  • idle : Это означает, что служба не будет запущена до тех пор, пока не будут отправлены все задания.

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

  • RemainAfterExit= : Эта директива обычно используется с типом onehot. Это означает, что сервис следует считать активным даже после выхода из процесса.
  • PIDFile= : Если тип службы отмечен как «forking», эта директива используется для установки пути файла, который должен содержать идентификационный номер процесса основного «ребенка», который должен контролироваться.
  • BusName= : Эта директива должна быть установлена на имя шины D-Bus, которое служба попытается получить при использовании типа службы «dbus».
  • NotifyAccess= : Эта директива указывает на доступ к сокету, который должен использовать для прослушивания уведомлений при выборе «notify». Она может быть «none», «main» или «all». По умолчанию «none» игнорирует все сообщения о состоянии. «main» вариант будет прослушивать сообщения из основного процесса, а опция «all» приведет к обработке всех членов контрольной группы службы.

До сих пор мы обсуждали некоторую предварительную информацию, но фактически не говорили, как управлять нашими услугами. Для этого имеются следующие директивы:

  • ExecStart= : Нужно указать полный путь и аргументы команды, которая должна быть выполнена для запуска процесса. Это может быть указано только один раз (кроме сервисов «onehot»). Если для пути к команде предшествует символ «-» тире, будут приняты ненулевые статусы выхода без маркировки активации устройства как сбой.
  • ExecStartPre= :Нужно указать полный путь и аргументы команды, которые должны быть выполнены до запуска основного процесса. Это можно использовать несколько раз.
  • ExecStartPost= : Это имеет те же самые качества, что и ExecStartPre = за исключением того, что данная дириктива указывает команды, которые будут запускаться после запуска основного процесса.
  • ExecReload= : Эта необязательная директива указывает команду, необходимую для перезагрузки конфигурации службы, если она доступна.
  • ExecStop= : Это указывает на команду, необходимую для остановки службы. Если это не указано, процесс будет немедленно уничтожен, когда служба будет остановлена.
  • ExecStopPost= : Это можно использовать для указания команд для выполнения после остановке команды.
  • RestartSec= : Если автоматический перезапуск службы включен, это указывает время ожидания перед попыткой перезапуска службы.
  • Restart= : Это указывает на обстоятельства, при которых systemd будет пытаться автоматически перезапустить службу. Она может быть установлена как значения «always», «on-success», «on-failure», «on-abnormal», «on-abort» или «on-watchdog». Это приведет к перезапуску в соответствии с тем, как служба была остановлена.
  • TimeoutSec= : Это устанавливает время, в течение которого система будет ждать остановки службы, прежде чем пометить ее как неудачную или убитую принудительно. Вы можете установить отдельные таймауты с помощью TimeoutStartSec = и TimeoutStopSec =.

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

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

Чтобы указать фактический сокет, эти директивы являются общими:

  • ListenStream= : Это определяет адрес для сокет stream, который поддерживает последовательную, надежную связь. Службы, использующие TCP, должны использовать этот тип сокета.
  • ListenDatagram= : Это определяет адрес для сокет datagram, который поддерживает быстрые, ненадежные пакеты связи. Службы, использующие UDP, должны устанавливать этот тип сокета.
  • ListenSequentialPacket= : Это определяет адрес для последовательной, надежной связи с максимальными дейтаграммами, которые сохраняют границы сообщений. Это чаще всего встречается для Unix сокетов.
  • ListenFIFO : Наряду с другими типами прослушивания вы также можете указать буфер FIFO вместо сокета.

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

Другие характеристики сокетов можно контролировать с помощью дополнительных директив:

  • Accept= : Это определяет, будет ли запущен дополнительный экземпляр службы для каждого соединения. Если установлено значение false (по умолчанию), один экземпляр будет обрабатывать все соединения.
  • SocketUser= : С помощью Unix-сокета задается его владелец. Если не указать, то будет пользователь root.
  • SocketGroup= : С помощью Unix-сокета задается владелец группы. Это будет root группа, если не указано ни то, ни другое. Если установлен только SocketUser =, systemd попытается найти подходящую группу.
  • SocketMode= : Для сокетов Unix или буферов FIFO это устанавливает разрешения для созданного объекта.
  • Service= : Если имя службы не совпадает с именем .socket , эта служба может быть указана с помощью этой директивы.

Модули монтирования позволяют управлять точкой монтирования изнутри systemd. Точки монтирования называются в соответствии с управляемым им каталогом с применением алгоритма перевода.

Например, ведущая косая черта (слеш или «/») удаляется, все остальные косые черты переводится в тире «-», и все тире и непечатаемые символы заменяются escape-кодами стиля С. Результат этого перевода используется как имя узла монтирования. Unit-ы монтирования будут иметь неявную зависимость от других монтировок над ней в иерархии.

Mount юниты часто переводятся непосредственно из файла /etc/fstab во время процесса загрузки. Для автоматически созданных определений единиц и те, которые вы хотите определить в единичном файле:

  • What= : Абсолютный путь к ресурсу, который необходимо смонтировать.
  • Where= : Абсолютный путь точки монтирования, в которой должен быть установлен ресурс. Это должно быть таким же, как имя файла устройства, за исключением использования стандартной записи файловой системы.
  • Type= : Тип файловой системы для монтирования.
  • Options= : Любые параметры монтирования, которые необходимо применить. Это список, разделенный запятыми.
  • SloppyOptions= : Логическое значение (0 или 1), которое определяет произойдет ли сбой монтирования, если есть параметр непризнанного монтирования.
  • DirectoryMode= : Если родительские каталоги необходимо создать для точки монтирования, это определяет режим разрешений этих папок.
  • TimeoutSec= : Настраивает время, в течение которого система будет ждать, пока операция монтирования не будет отмечена как сбой.

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

Раздел довольно прост, разрешены только следующие два варианта:

  • Where= : Абсолютный путь automount в файловой системе. Это будет соответствовать имени файла, за исключением того, что вместо перевода используется условное обозначение пути.
  • DirectoryMode= :Если необходимо создать automount или любые родительские каталоги, это определит настройки разрешений для этих компонентов пути.

Swap модули используются для настройки подкачки (свопинга) в системе. Юниты должны быть названы по названию файла или устройства подкачки, используя тот же перенос файловой системы, о котором говорилось выше.

Как и mount параметры, блоки swap могут быть автоматически созданы из /etc/fstab или могут быть сконфигурированы через выделенный unit файл.

Раздел может содержать следующие директивы для конфигурации:

  • What= : Абсолютный путь к местоположению подкачки (будь то файл или устройство).
  • Priority= : Данная опция принимает целое число, которое задает приоритет для настройки подкачки.
  • Options= : Любые параметры, которые обычно задаются в файле /etc/fstab, могут быть установлены с помощью этой директивы. Используется список, разделенный запятыми.
  • TimeoutSec= : Данная опция задает временя, в течение которого, система ожидает активацию своп-а, прежде чем отмечать операцию как зафейленую.

Блок path, определяет путь файловой системы, который systmed может отслеживать изменения. Должен существовать другой блок, который будет активирован, когда определенная активность будет обнаружена в местоположении пути. Активность пути определяется тем, что он не влияет на события.

Раздел может содержать следующие директивы:

  • PathExists= : Эта директива используется для проверки того, существует ли этот путь. Если путь существует, активируется соответствующий блок.
  • PathExistsGlob= : Данная опция такая как и выше, но поддерживает глобальные выражения для определения существования пути.
  • PathChanged= : Данную опцию используют для отслеживания изменений местоположение пути. Связанный блок активируется, если обнаружено изменение (проверяется когда файл закрыт).
  • PathModified= : Данная опция такая как и выше, но активируется при записи файлов, а также когда файл закрыт.
  • DirectoryNotEmpty= : Эта директива позволяет systemd активировать связанный блок, когда каталог больше не пустой.
  • Unit= : Это указывает, что устройство активируется, когда условия пути, указанные выше, выполняются. Если это будет опущено, systemd будет искать файл.service, который имеет то же имя базового юнита, что и этот блок.
  • MakeDirectory= : Данная директива определяет, будет ли systemd создавать структуру каталогов перед просмотром.
  • DirectoryMode= : Если вышеуказанная директива включена, то данная опция установит режим разрешения любых компонентов пути, которые должны быть созданы.

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

Раздел может содержать некоторые из следующих директив:

  • OnActiveSec= : Эта директива позволяет активировать соответствующий блок относительно активации модуля.timer.
  • OnBootSec= : Эта директива задает время, когда будет активироваться соответствующее устройство после загрузки системы.
  • OnStartupSec= : Эта директива аналогична указанному выше таймеру, но задает когда будет активироваться systemd процесс после загрузки системы.
  • OnUnitActiveSec= : Это устанавливает таймер в зависимости от того, когда последний активировался.
  • OnUnitInactiveSec= : Это устанавливает таймер в отношении того когда unit был неактивный.
  • OnCalendar= : Это позволяет активировать соответствующий блок путем определения абсолютного (вместо относительно).
  • AccuracySec= : Данный юнит используется для установки уровня точности с таймером, который должен быть приклеен. По умолчанию, связанный с ним блок будет активирован в течение одной минуты.
  • Unit= : Эта директива используется для указания того, что необходимо активировать когда таймер истечет. Если не установлены, Systemd будет искать блок с именем.service.
  • Persistent= : Если это установлено, Systemd запустит триггер соответствующего блока.
  • WakeSystem= : Установка этой директивы позволяет «будить систему» из режима ожидания, если таймер будет достигнут.

Раздел на самом деле, не имеет.slice специфической конфигурации. Вместо этого он может содержать некоторые директивы управления ресурсами, которые перечислялись выше.

Хватит теории, перейдем к практике.

Пишем systemd Unit файл

Первое что стоит сделать — так это проверить инициализацию:

# ps -s1| awk "{print $4}"| grep -Ev "CMD"

PS: Вот небольшая статья:

Файлы шаблонов блоков могут быть определены, потому что они содержат символ @ после имени базового блока и перед суффиксом блокового типа. Имя файла блока с шаблоном может выглядеть следующим образом:

[email protected]

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

[email protected]

Мощность файлов шаблонных модулей в основном проявляется благодаря возможности динамически подставлять соответствующую информацию в определение устройства в соответствии со средой (ENV). Это делается путем установки директив в файл шаблона как обычно, но заменяя определенные значения или части значений спецификаторами переменных.

Ниже приведены некоторые из наиболее распространенных спецификаторов, которые будут заменены, когда юнит-экземпляра интерпретируется с соответствующей информацией:

  • %n : В любом месте, где это используется, будет добавлено полное имя элемента.
  • %N : Это то же самое, что и выше, но любое экранирование, такое как те, что присутствуют в шаблонах пути файла, будет отменено.
  • %p : Это указывает префикс имени юнита. Это часть названия юнита, которая предшествует символу @.
  • %P : Это то же самое что и выше, но с любым отступлением.
  • %i : Это ссылается на имя экземпляра, которое является идентификатором, следующим за @ в экземпляре. Это один из наиболее часто используемых спецификаторов, поскольку он динамичный. Использование этого идентификатора поощряет использование значимых идентификаторов конфигурации. Например, порт, в котором будет выполняться служба, может использоваться как идентификатор экземпляра, и шаблон может использовать этот спецификатор для настройки спецификации порта.
  • %I : Этот спецификатор такой же, как и выше, но с любым отступлением.
  • %f : Это будет заменено на имя неэкранированного юнита или имя префикса, добавленное к «/».
  • %c : Это будет указывать на управляющую группу устройства со стандартной родительской иерархией /sys/fs/cgroup/ssytemd.
  • %u : Имя пользователя, настроенного для запуска юнита.
  • %U : То же, что и выше, UID вместо имени.
  • %H : Имя хоста системы, на котором выполняется юнит.
  • %% : Это используется для вставки буквенного знака процента.

Используя приведенные выше идентификаторы в шаблоне файла, Systemd заполнит правильные значения при интерпретации шаблона для создания юнит-экземпляра.

Примеры systemd Unit файлов

Рассмотрим следующий пример, — это написания systemd скрипта для запуска tomcat-а. Для этого, открываем файл:

# vim /etc/systemd/system/tomcat9.service

И записываем в него следующий код:

Description=Tomcat9 After=syslog.target network.target Type=forking User=tomcat Group=tomcat Environment=CATALINA_PID=/usr/local/tomcat9/tomcat9.pid Environment=TOMCAT_JAVA_HOME=/usr/bin/java Environment=CATALINA_HOME=/usr/local/tomcat9 Environment=CATALINA_BASE=/usr/local/tomcat9 Environment=CATALINA_OPTS= #Environment="CATALINA_OPTS=-Xms512M -Xmx1024M -server -XX:+UseParallelGC" Environment="JAVA_OPTS=-Dfile.encoding=UTF-8 -Dnet.sf.ehcache.skipUpdateCheck=true -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+UseParNewGC -XX:MaxPermSize=128m -Xms512m -Xmx512m" ExecStart=/usr/local/tomcat9/bin/startup.sh ExecStop=/bin/kill -15 $MAINPID WantedBy=multi-user.target

Перезагрузим службу:

# systemctl daemon-reload

Добавим томкат в автозагрузку ОС:

# systemctl enable tomcat9

Перезапускаем томкат:

# systemctl restart tomcat9

Чтобы проверить статус, выполняем:

# systemctl status tomcat9 ● tomcat9.service - Tomcat9 Loaded: loaded (/etc/systemd/system/tomcat9.service; enabled) Active: active (running) since Tue 2017-05-09 22:04:58 EEST; 6s ago Process: 28528 ExecStop=/bin/kill -15 $MAINPID (code=exited, status=0/SUCCESS) Process: 28531 ExecStart=/usr/local/tomcat9/bin/startup.sh (code=exited, status=0/SUCCESS) Main PID: 28541 (java) CGroup: /system.slice/tomcat9.service └─28541 /usr/bin/java -Djava.util.logging.config.file=/usr/local/tomcat9/conf/logging.properties -Djava.ut... May 09 22:04:58 debian systemd: Starting Tomcat9... May 09 22:04:58 debian startup.sh: Tomcat started. May 09 22:04:58 debian systemd: Started Tomcat9.

Как видим, все четко работает. На этом, у меня все! Больше примеров будет дальше. Я буду добавлять их по мере необходимости. Но в основном, я использую шаблон что выше (только немного его видоизменяю).

Статья «Пишем systemd Unit файл» завершена.

Большинство дистрибутивов Linux в качестве менеджера системы и сервисов используют systemd.

systemctl является основной командой для управления сервисами в systemd.

В данной статье я покажу, как создать service-файл в systemd, который позволит управлять вашим сервисом с помощью команды systemctl , как без перезагрузки перезапустить systemd, чтобы он перечитал unit-файлы и как активировать ваш новый сервис.

Также я приведу описание наиболее важных опций используемых в service-файлах с примерами реальных service-файлов.

Создание Сервиса в Systemd

Создайте service-файл /etc/systemd/system/foo-daemon.service (замените foo-daemon на имя вашего сервиса):

$ sudo touch /etc/systemd/system/foo-daemon.service $ sudo chmod 664 /etc/systemd/system/foo-daemon.service

Откройте файл foo-daemon.service и пропишите минимальные настройки, которые позволят управлять сервисом с помощью systemctl:

Description=Foo ExecStart=/usr/sbin/foo-daemon WantedBy=multi-user.target

Путь К Демону: Если вы не знаете путь к демону, попробуйте which foo-daemon .

После создания нового service-файла необходимо перезапустить systemd:

$ sudo systemctl daemon-reload

Теперь вы можете делать start , stop , restart и проверять status сервиса:

$ sudo systemctl start foo-daemon $ sudo systemctl stop foo-daemon $ sudo systemctl restart foo-daemon $ systemctl status foo-daemon

Чтобы добавить сервис в автозагрузку, необходимо активировать его:

$ sudo systemctl enable foo-daemon

Чтобы проверить логи сервиса, выполните:

$ journalctl -u foo-daemon

Опции Service-файла в Systemd

Service-файла в systemd обычно состоит из трех секций.

Общие элементы конфигурации сервиса настраиваются в секциях и

Параметры конфигурации конкретного сервиса настраиваются в секции .

Важные Опции Секции

Опция Описание
Description Краткое описание юнита.
Documentation Список ссылок на документацию.
Before , After Порядок запуска юнитов.
Requires Если этот сервис активируется, перечисленные здесь юниты тоже будут активированы. Если один из перечисленных юнитов останавливается или падает, этот сервис тоже будет остановлен.
Wants Устанавливает более слабые зависимости, чем Requires . Если один из перечисленных юнитов не может успешно запуститься, это не повлияет на запуск данного сервиса. Это рекомендуемый способ установления зависимостей.
Conflicts Если установлено что данный сервис конфликтует с другим юнитом, то запуск последнего остановит этот сервис и наоборот.

Список всех опций секции :

$ man systemd.unit

Важные Опции Секции

Список всех опций секции :

$ man systemd.unit

Важные Опции Секции

Опция Описание
Type Настраивает тип запуска процесса. Один из:
simple (по умолчанию) — запускает сервис мгновенно. Предполагается, что основной процесс сервиса задан в ExecStart .
forking — считает сервис запущенным после того, как родительский процесс создает процесс-потомка, а сам завершится.
oneshot — аналогичен типу simple , но предполагается, что процесс должен завершиться до того, как systemd начнет отслеживать состояния юнитов (удобно для скриптов, которые выполняют разовую работу и завершаются). Возможно вы также захотите использовать RemainAfterExit=yes , чтобы systemd продолжал считать сервис активным и после завершения процесса.
dbus — аналогичен типу simple , но считает сервис запущенным после того, как основной процесс получает имя на шине D-Bus.
notify — аналогичен типу simple , но считает сервис запущенным после того, как он отправляет systemd специальный сигнал.
idle — аналогичен типу simple , но фактический запуск исполняемого файла сервиса откладывается, пока не будут выполнены все задачи.
ExecStart Команды вместе с аргументами, которые будут выполнены при старте сервиса. Опция Type=oneshot позволяет указывать несколько команд, которые будут выполняться последовательно. Опции ExecStartPre и ExecStartPost могут задавать дополнительные команды, которые будут выполнены до или после ExecStart .
ExecStop Команды, которые будут выполнены для остановки сервиса запущенного с помощью ExecStart .
ExecReload Команды, которые будут выполнены чтобы сообщить сервису о необходимости перечитать конфигурационные файлы.
Restart Если эта опция активирована, сервис будет перезапущен если процесс прекращен или достигнут timeout, за исключением случая нормальной остановки сервиса с помощью команды systemctl stop
RemainAfterExit Если установлена в значение True , сервис будет считаться запущенным даже если сам процесс завершен. Полезен с Type=oneshot . Значение по умолчанию False .

Список всех опций секции :

$ man systemd.service

Примеры Service-файлов в Systemd

Description=The NGINX HTTP and reverse proxy server After=syslog.target network.target remote-fs.target nss-lookup.target Type=forking PIDFile=/run/nginx.pid ExecStartPre=/usr/sbin/nginx -t ExecStart=/usr/sbin/nginx ExecReload=/bin/kill -s HUP $MAINPID ExecStop=/bin/kill -s QUIT $MAINPID PrivateTmp=true WantedBy=multi-user.target Description=The Apache HTTP Server After=network.target remote-fs.target nss-lookup.target Type=notify EnvironmentFile=/etc/sysconfig/httpd ExecStart=/usr/sbin/httpd $OPTIONS -DFOREGROUND ExecReload=/usr/sbin/httpd $OPTIONS -k graceful ExecStop=/bin/kill -WINCH ${MAINPID} KillSignal=SIGCONT PrivateTmp=true WantedBy=multi-user.target Description=Redis persistent key-value database After=network.target ExecStart=/usr/bin/redis-server /etc/redis.conf --daemonize no ExecStop=/usr/bin/redis-shutdown User=redis Group=redis WantedBy=multi-user.target

В операционной системе linux, так же как и в Windows, кроме обычных программ, которые могут взаимодействовать с пользователем есть еще один вид программ. Это работающие в фоне службы. Важность служб тяжело переоценить, они следят за состоянием системы, обеспечивают автоматическое подключение внешних устройств и сети, позволяют процессам взаимодействовать с оборудованием (dbus), а также в виде служб реализованы различные веб-серверы и серверы баз данных. В отличие от пользовательских программ, службы выполняются в фоне, и пользователь не имеет к ним прямого доступа. Пользователь еще не вошел в систему, только началась загрузка а основные службы уже запущенны и работают.

В этой статье мы рассмотрим управление службами Linux. Мы не будем трогать уже устаревшие системы, такие как SysVinit, сосредоточимся только на Systemd. Вы узнаете, как посмотреть запущенные службы linux, а также останавливать и запускать их самому.

Чтобы всем этим управлять нужна основная служба - система инициализации, которая будет запускать службы linux в нужный момент, следить чтобы они нормально работали, записывать сообщения логов, и самое главное позволять останавливать службы. Раньше, для управления службами использовались скрипты. Я уже говорил, что можно запустить службу из терминала, так вот, каждая служба запускалась в фоновом режиме одна за другой, без возможности параллельного запуска и возвращала свой PID процесса скрипту инициализации, он сохранялся и потом с помощью этого PID можно было проверить работает ли служба и остановить службу linux если это нужно. Все это можно сделать и вручную.

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

Служба в Systemd описывается файлом юнита, в нем описано что с ней нужно делать и как себя вести. Существуют такие типы служб:

  • service - обычная служба, программа
  • target - группа служб
  • automount - точка автоматического монтирования
  • device - файл устройства, генерируется на этапе загрузки
  • mount - точка монтирования
  • path - файл или папка
  • scope - процесс
  • slice - группа системных служб systemd
  • snapshot - сохраненное состояние запущенных служб
  • socket - сокет для взаимодействия между процессами.

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

Утилита systemctl

В Systemd есть специальный инструмент для управления службами в Linux - systemctl. Эта утилита позволяет делать очень много вещей, начиная от перезапуска службы linux и проверки ее состояния, до анализа эффективности загрузки службы. Синтаксис у утилиты такой:

$ systemctl опции команда служба служба...

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

Рассмотрим все по порядку. Опции очень сильно зависят от команд, поэтому рассмотрим их позже, а пока пройдемся по командах:

  • list-units - посмотреть все службы (юниты), аналог опции -t
  • list-sockets - посмотреть все службы сокетов
  • start - запустить службу linux
  • stop - остановить службу linux
  • reload - обновить конфигурацию службы из файла юнита
  • restart - перезапустить службу
  • try-restart - перезапустить службу, только если она запущена
  • reload-or-restart - обновить конфигурацию затем выполнить перезапуск службы linux, если не поддерживается - только перезапустить
  • isolate - запустить только одну службу вместе с ее зависимостями, все остальные остановить
  • kill - отправить сигнал завершения процессу используется вместе с опциями --signal и --kill-who
  • is-active - проверить запущена ли служба linux
  • is-failed - проверить не завершилась ли служба с ошибкой
  • status - посмотреть состояние и вывод службы
  • show - посмотреть параметры управления службой в Linux
  • reset-failed - перезапустить службы linux, завершившиеся с ошибкой
  • list-dependencies - посмотреть зависимости службы linux
  • list-unit-files - вывести все установленные файлы служб
  • enable - добавить службу в автозагрузку
  • disable - удалить службу из автозагрузки
  • is-enabled - проверить если ли уже служба в автозагрузке
  • reenable - сначала выполнить disable потом enable для службы
  • list-jobs - все запущенные службы linux независимо от типа
  • snapsot - сохранить состояние служб, чтобы потом восстановить
  • daemon-reload - обновить конфигурацию всех служб
  • mask - сделать юнит недоступным
  • unmask - вернуть файл службы linux

А теперь основные опции:

  • -t, --type - тип служб для вывода
  • -a, --all - показать все известные службы, даже не запущенные
  • -q - минимальный вывод
  • --version - версия программы
  • --no-pager - не использовать постраничную навигацию
  • --no-legend - не выводить подробное описание
  • -f - принудительное выполнение команды
  • --runtime - не сохранять вносимые изменения после перезагрузки
  • -n - количество строк вывода лога для команды status
  • --plain - использовать обычный текстовый режим вместо деревьев
  • --kill-who - задать процесс, которому нужно отправить сигнал
  • --signal - сигнал, который нужно отправить.
  • --state - отфильтровать список служб по состоянию.

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

Управление службами Linux

Теперь, когда вы уже знаете все основы, команды и параметры можно переходить к делу. Со всеми остальными тонкостями разберемся по пути. Сначала давайте посмотрим запущенные службы linux. Нас будут интересовать только программы, а не все эти дополнительные компоненты, поэтому воспользуемся опцией type:

systemctl list-units --type service

Команда отобразила все службы, которые известны systemd, они сейчас запущены или были запущены. Программа не пересматривает все файлы, поэтому будут показаны только те службы, к которым уже обращались. Состояние loaded - означает, что конфигурационный файл был успешно загружен, следующая колонка active - служба была запущена, а running или exited значит выполняется ли сейчас служба или она успешно завершила свою работу. Листать список можно кнопками вверх/вниз.

Следующая команда позволяет получить список служб linux, в который входят все службы, даже не запущенные, те, которые не запускались, но известны systemd, но это еще не все службы в системе:

systemctl list-units --type service -all

systemctl list-units --type service --state running

Или те, которые завершились с ошибкой:

systemctl list-units --type service --state failed

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

systemctl list-unit-files

Теперь отфильтруем только службы linux:

systemctl list-unit-files --type service

Здесь вы тоже можете использовать фильтры по состоянию. Теперь вы знаете как посмотреть запущенные службы linux, идем дальше.

Чтобы запустить службу используется команда start, например:

sudo systemctl start application.service

Причем расширение service можно опустить, оно и так подставляется по умолчанию. Если запуск прошел хорошо, программа ничего не выведет.

Остановить службу linux можно командой:

sudo systemctl stop application

Посмотреть состояние службы позволяет команда status:

sudo systemctl status application

Здесь вы можете видеть, состояние running, exited, dead, failed и т д. А также несколько последних строчек вывода программы, которые очень помогут решить проблему с запуском если она возникнет.

Как вы знаете, systemd позволяет автоматически загружать службы при запуске системы по мере их надобности. Команда list-unit-files показывает добавлена ли служба в автозагрузку.

Вообще, здесь может быть несколько состояний - enabled - в автозагрузке, disabled - автозагрузка отключена, masked - служба скрыта и static - значит что служба в автозагрузке, но вы не можете ее отключить.

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

systemctl list-unit-files --state enabled

Все службы, запускаемые по умолчанию. Можете также посмотреть службы static. Чтобы добавить службу в автозагрузку linux используйте команду enable:

sudo systemctl enable application

А для того чтобы убрать ее из автозагрузки.

Здесь покажу как писать инитники для автозапуска какого-либо демона в системе с systemd.

Systemd оперирует unit-файлами в качестве конфигурационных файлов. Это как.conf-файл для upstart или init-скрипты для initd. Unit-файлы также могут описывать и другие системные сущности, но в данном случае они нас интересуют как конфиг для автостарта сервиса. В Ubuntu 16.04 они лежат в /etc/systemd/ . Напишем свой unit-файл.

Допустим у нас есть программа, которую мы хотим запускать как демон. Вот я писал как создать telegram-бота. У меня получился исполняемый файл, который запускается и висит ждет сообщений. Теперь хочу чтобы он как демон стартовал при загрузке системы.

Создаем такой файл:

Description = TelegramBotMonitoring After = network.target Type = simple ExecStart = /usr/local/bin/telegram-site-monitoring -telegrambottoken 397______:___________WRDsIU -chatid -14____640 Restart = always WantedBy = multi-user.target

и кладем его в /etc/systemd/system/telegram-bot.service

Конфиг похож на обычный ini-файл:

    After - запускать этот юнит после определенных демонов или целей (цель - это набор юнитов). Здесь указан network.target , это значит, что демон запустится после того как поднимутся сетевые интерфейсы.

    Type - тип того, как запускается демон. Чаще всего используется simple , forking или one-shot . simple - демон запускается и начинает ожидать на консоле и не отфоркивается. forking - демон запускается, потом форкается и завершает родительский процесс. Многие программы именно так и запускаются, чтобы перейти в background режим. Например, nginx запускается по такой схеме. one-shot - используется для запуска скриптов которые запускаются, отрабатывают и завершаются. В моем случае это не скрипт и программа не форкается, поэтому тип - simple

    ExecStart - команда для запуска демона.

    Restart - рестартовать демон, если он завершится/упадет. При always systemd будет перезапускать демон независимо от того почему он завершился. Можно указать on-failure , тогда будет перезапускаться если демон вышел с ненулевым кодом возврата или был завершен по сигналу (kill DAEMONPID)

    WantedBy - говорим, что запускать этот демон когда система грузится в multi-user режиме

# systemctl daemon-reload

И добавляем созданный юнит в автозагрузку:

# systemctl enable telegram-bot.service Created symlink from /etc/systemd/system/multi-user.target.wants/telegram-bot.service to /etc/systemd/system/telegram-bot.service.```

Все. Сервис добавлен в автозагрузку, но еще не запущен. Запустим:

# systemctl start telegram-bot

Запустился:

# systemctl status telegram-bot ● telegram.service - TelegramBotMonitoring Loaded: loaded (/etc/systemd/system/telegram-bot.service; enabled; vendor preset: enabled) Active: active (running) since Thu 2017-07-13 17:10:19 MSK; 5s ago Main PID: 1825 (telegram-site-m) Tasks: 4 Memory: 4.4M CPU: 39ms CGroup: /system.slice/telegram-bot.service └─1825 /usr/local/bin/telegram-site-monitoring -telegrambottoken 3972____:__________Gi03WRDsIU -chatid -14____40 Jul 13 17:10:19 ubuntu systemd: Started TelegramBotMonitoring. Jul 13 17:10:19 ubuntu telegram-site-monitoring: 2017/07/13 17:10:19 {} Jul 13 17:10:19 ubuntu telegram-site-monitoring: 2017/07/13 17:10:19 Authorized on account Jul 13 17:10:19 ubuntu telegram-site-monitoring: 2017/07/13 17:10:19 Config file: config.json Jul 13 17:10:19 ubuntu telegram-site-monitoring: 2017/07/13 17:10:19 ChatID: Jul 13 17:10:19 ubuntu telegram-site-monitoring: 2017/07/13 17:10:19 Starting monitoring thread

Шпаргалка с коммандами для управления демонами systemd -

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

Systemd: быстрее света

Схема загрузки типичного Linux-дистрибутива выглядит примерно так: ядро инициализирует железо и запускает процесс /sbin/init, который, в свою очередь, запускает инициализационные скрипты. Скрипты монтируют файловые системы, настраивают сеть, различные устройства и начинают последовательный запуск демонов: syslogd, cron, cups и прочих, которые перечислены в конфигурационных файлах. В самом конце init запускает менеджер входа в систему: getty или xdm (kdm, gdm). Просто и логично, не так ли? Однако такая схема довольно примитивна, а в сравнении с Windows и Mac OS X так и вообще архаична. Их системы инициализации запускают задачи параллельно, не дожидаясь завершения одной, чтобы передать управление следующей. Если одна из них застопоривается на операции ввода-вывода, управление сразу получает другая, так что общее время загрузки сокращается, да так существенно, что традиционная система оказывается далеко позади.

В мире Linux так ведет себя только Ubuntu, да и то только последние два года. Все остальные продолжают по старинке последовательно грузить систему или используют самосборные костыли, которые распараллеливают процесс загрузки неумело и часто ошибочно (Gentoo и Arch, привет!). По-настоящему универсальное решение не найдено до сих пор, поэтому над идеей параллельной системы инициализации работают многие программисты.

Леннарт Поттеринг, сотрудник Red Hat и автор PulseAudio, один из них. Его последнее достижение - демон systemd, очередной претендент на звание убийцы /sbin/init, мимо которого можно было бы спокойно пройти, если бы идея, заложенная в его основу, не оказалась столь интересной и правильной.

Systemd отличается от любой другой системы инициализации тем, что намеренно делает сложные вещи простыми. 99% всех остальных параллельных систем инициализации провалились просто потому, что в сравнении с простым и понятным даже дикарю /sbin/ init они выглядели тяжеловесными монстрами. Чтобы обеспечить возможность параллельного запуска, не введя ОС в противоречивое состояние (которое может возникнуть, если, например, пытаться настроить сеть до загрузки сетевых драйверов или запустить демоны, не смонтировав нужную ФС), использовались различные методы синхронизации. В основном это были своеобразные «метки зависимостей», которые не давали очередному шагу инициализации отработать, если не был пройден шаг, описанный в его зависимостях.
Например, cron зависит от syslog, потому что ему надо вести логи; syslog зависит от настойки сети, потому что он способен принимать логи от удаленных машин и так далее. Из-за этого инициализационные скрипты превращались в запутанную вереницу блоков, а их составление значительно усложнялось. Systemd организован намного проще, он не следит за зависимостями, а просто запускает все, что есть, одновременно.

Я не шучу. Systemd использует механизмы контроля зависимостей только на самых ранних этапах инициализации, которые так или иначе должны происходить последовательно (монтирование корневой файловой системы, подключение swap, загрузка модулей и так далее). Когда же дело доходит до демонов, на запуск которых уходит 90% всего времени инициализации ОС, systemd забывает о зависимостях и стартует их всех сразу, показывая просто потрясающую скорость.

Это работает благодаря тому, что systemd знает об особенности работы демонов и их связи между собой.

Ведь на самом деле демонам нужны вовсе не другие демоны, а только «коммуникационные каналы», обеспечивающие обмен данными: cron не зависит от syslog, ему нужен сокет /dev/log, в который он сможет записывать свои логи, это же справедливо и в отношении любого другого демона. Все они общаются через сокеты, и единственная причина, почему демон A должен быть запущен раньше демонов B, C и D, заключается в том, что демон A должен создать сокет, который им нужен. Systemd учитывает эту особенность, поэтому его механизм параллельного запуска основан на сокетах, которые он создает для каждого демона заблаговременно, а затем запускает демоны одновременно. При этом ответственность за синхронизацию и «отслеживание зависимостей» теперь перекладываются на ядро, в рамках которого и реализован механизм сокетов.

Если, например, cron получит управление раньше syslog, ничего страшного не произойдет - cron найдет свой любимый /dev/log и даже сможет писать в него сообщения (если захочет, конечно), которые будут, нет, не выброшены, а буферизированы в сокете, но только до тех пор, пока cron не захочет записать в сокет сообщение, способное его переполнить. В этом случае ядро заблокирует процесс cron и передаст управление следующему процессу в очереди на исполнение (следующему демону). Вскоре (а может быть и сразу) очередь дойдет и до syslog, который запустится, прочитает сообщения, скопившиеся в /dev/log, обработает их и сам на чем-нибудь заблокируется (либо истратит отведенное ему время), и управление перейдет следующему демону. Типичная многозадачность без лишних костылей.

Более того, благодаря такой схеме большинство демонов могут быть запущены только тогда, когда в них возникнет реальная необходимость. Так, например, CUPS вовсе не обязательно запускать во время инициализации ОС, когда нагрузка на систему и без того высока. Логичнее стартануть его, когда на печать будет отправлен первый документ. Systemd позволяет сделать такое, следя за активностью вокруг сокетов, и применяет похожий подход для монтирования файловых систем, подключая их к точкам монтирования только при попытке получить доступ к файлам (также демоны могут быть запущены при появлении в системе определенного файла-устройства).

Справедливости ради стоит сказать, что столь гениальное решение проблемы зависимостей было придумано и реализовано в Mac OS X с самого начала ее существования, но до автора systemd почему-то никто не обращал на это внимания.

Кстати, у самого systemd есть другая и явно уникальная для Linux характеристика: он умеет группировать процессы с помощью cgroups с установкой различных лимитов среды исполнения на всю группу (ограничения ресурсов, рабочий и корневой каталоги, umask, настройки OOM killer, параметр nice, приоритет операций ввода-вывода, приоритеты использования процессора и многое другое). То есть демонов теперь можно помещать в виртуальные окружения без использования какого бы то ни было дополнительного ПО, просто прописав в файл настроек systemd несколько строк.

Systemd уже доступен для скачивания и возможно будет включен в один из будущих релизов Fedora в качестве альтернативной системы инициализации. Инструкции по установке в другие дистрибутивы можно найти на официальной страничке: freedesktop.org/wiki/Software/systemd .

Установить Systemd в Ubuntu можно, выполнив следующие команды:

$ sudo add-apt-repository ppa:andrew-edmunds/ppa
$ sudo apt-get update
$ sudo apt-get install systemd

Далее следует отредактировать /boot/grub/grub.cfg, добавив к параметрам ядра строчку init=/sbin/systemd. После перезагрузки дистрибутив будет работать с новой системой инициализации, в чем можно убедиться с помощью команды:

$ sudo systemctl units-list

Для проверки состояния, запуска, остановки и включения служб используются аргументы status, start, stop и enable.

Ulatencyd: мгновенная реакция

Какой, на твой взгляд, самый важный параметр десктопной операционной системы? Хороший графический интерфейс? Количество доступных приложений? Простота использования?

Да, все это имеет значение, но сегодня, когда этими свойствами легко наделить даже серверные ОС, решающими становятся совсем другие факторы, важнейший из которых - отзывчивость системы.

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

Для разработчиков операционных систем это прописная истина, поэтому во все времена они стремились сделать свои ОС более интерактивными. У одних это получалось хорошо (привет BeOS), у других плохо (куда же без MS), но были и такие, у кого это не получалось вообще. Долгое время разработчики Linux совершенно не интересовались темой отзывчивости Linux на десктопах. Кон Коливас множество раз указывал им на проблемы, говорил о неповоротливости и медлительности Linux, писал патчи, ночами кодил новые планировщики задач, добивался их включения в ядро. Все напрасно, раз за разом патчи отвергали, а самого автора грубо отстраняли от дел.

Однако со временем труды Кона Коливаса окупились. Инго Молнар начитался его исходников и написал планировщик CFS (Completely Fair Scheduler), а Линукс незамедлительно включил его в ядро 2.6.23. После этого положение дел на десктопах существенно улучшилось, и Linux стал намного быстрее (при этом реализация Кона все равно продолжала показывать более впечатляющие результаты).

Второй важной вехой на пути Linux к десктопу стало включение знаменитого 200-страничного патча в ядро 2.6.38, а также появление его аналога на языке bash. Так Linux научился отделять интерактивные процессы от всех остальных демонов, серверов и bash-скриптов и наделять их более высокими приоритетами. Это событие еще больше улучшило ситуацию и сделало ее практически идеальной: теперь Linux не тормозил даже тогда, когда в фоне шла пересборка ядра в несколько потоков.
Наконец, третьим важным шагом для десктопного Linux (здесь я подхожу к самому главному) стало появление демона ulatencyd, способного регулировать отзывчивость системы динамически, подстраивая ее под изменяющиеся обстоятельства.

Как и ядерный патч, ulatencyd использует механизм cgroups для группировки интерактивных процессов и изменения их приоритетов, но на этом его работа не заканчивается. Демон использует эвристические алгоритмы для выявления «наиболее интерактивных» процессов, а также явных вредителей системы, таких как форк-бомбы и программы с большими утечками памяти. При этом первые получают еще больший приоритет, а вторые жестко урезаются в возможностях (получая низкий приоритет, ограничения на доступную память), изолируются или уничтожаются. Но что самое главное, в любой момент демон можно обучить новым правилам отбора процессов, так что мы теперь можем назначать самые высокие (даже реалтаймовые) приоритеты любимым играм, видеоплеерам и браузерам.

Демон поддерживает плагины, поэтому его функциональность может быть расширена до просто фантастических возможностей. Например, уже сейчас доступен плагин (причем в стандартной комплектации), который следит за работой пользователя в иксах и назначает самые высокие приоритеты вновь открытым приложениям и процессам, окна которых находятся на переднем плане.

Чтобы установить ulatencyd, необходимо скачать его исходники со страницы и собрать с помощью стандартных cmake и make:

$ cmake
$ make
$ sudo make install

$ sudo /usr/local/sbin/ulatencyd -v 2

И понаблюдать за тем, как он группирует процессы по приоритетам:

$ ps xaf -eo pid,session,args,cgroup

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

relayd: по трем фронтам

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

Возьмем, к примеру, достаточно распространенную в узких кругах функцию сервера под названием «распределение нагрузки между несколькими DNS-серверами». Что нужно для ее решения? Во-первых, умение перенаправлять DNS-трафик на другой хост (от балансировщика к одному из реальных DNS-серверов).

Это можно сделать с помощью брандмауэра или особым образом настроенного BIND (несколько тяжеловесный вариант). Вовторых, умение выбирать наиболее подходящего кандидата для обработки запроса из списка DNS-серверов. Это уже сложнее, и здесь может понадобиться специализированное ПО или опять же брандмауэр (но очень хороший). В-третьих, умение проверять DNS-сервера на доступность и удалять упавших из списка. Нужен скрипт, пингующий хосты и управляющий списками, либо особые возможности брандмауэра (а такой есть?). В общем, долгое нудное велосипедостроение или покупка специализированного решения для конкретной задачи по нереальным ценам. А что если завтра вдруг понадобится сделать нечто подобное для SMTP?

Все править или вновь открывать кошелек? Не стоит, содержимое кошелька все-таки лучше приберечь, а костыли с велосипедами оставить спортсменам. Демон relayd, появившийся в OpenBSD 4.3, позволяет решить эту и еще огромное количество других, подобных и не очень, задач всего за несколько минут.

Он включает в себя возможности балансировщика нагрузки для протоколов уровней 3, 4 и 7, прокси уровня 7 (релей) и сервиса проверки доступности сетевых узлов (из которого и вырос).

На основе relayd можно строить самые разные конфигурации, начиная от простых прокси-серверов или SSL-акселераторов и заканчивая сложными решениями вроде прозрачных web-прокси с фильтрацией запросов и распределением нагрузки между несколькими web-серверами. И все это с помощью простого конфигурационного файла, длина которого даже в самых сложных конфигурациях редко превышает 50 строк.

Да, конечно, без примера все это только слова. Так что вот рабочий конфиг, полностью удовлетворяющий требованиям, выдвинутым в начале раздела:

# vi /etc/relayd.conf

Переменные и макросы

Адрес и порт нашего релея

relayd_addr="127.0.0.1"
relayd_port="8053"

Адреса трех DNS-серверов, которые будут обрабатывать

запросы

table { 192.168.1.1, 192.168.1.2, 192.168.1.3 }

Общие настройки

Интервал между проверками хостов на доступность

(10 секунд)

Таймаут для проверки хостов на доступность методом TCP

(если хост не отвечает дольше 200 мс - он в дауне)

Разделяем сервер на 5 процессов для более эффективной

обработки запросов

Логировать результаты проверки хостов на доступность

Настройки DNS-протокола

Параметры оптимизации соединения

dns protocol "dnsfi lter" { tcp { nodelay, sack, socket buffer 1024, backlog 1000 } }

Настройки релея

relay dnsproxy {

Прослушиваемый адрес и порт

listen on $relayd_addr port $relayd_port

Работаем с описанным ранее протоколом

protocol "dnsfi lter"

Оправляем DNS-пакеты одному из перечисленных в таблице

DNS-серверов, предварительно проверив его на доступность

forward to port 53
mode loadbalance check tcp
}

Наиболее важные части этого конфига находятся в теле директив dns protocol и relay. Первая представляет собой нечто вроде шаблона, который используется для того, чтобы не повторять одни и те же настройки протоколов в других частях конфигурационного файла (relayd поддерживает три протокола: HTTP, DNS и TCP). Вторая - это настройка релея, в котором указаны прослушиваемый порт, проксируемый протокол, его настройки и информация о том, каким образом и какому хосту должны быть перенаправлены пакеты. В нашем случае прокси должен отправить DNS-запрос одному из трех серверов с предварительной проверкой на доступность (здесь используется проверка методом TCP-рукопожатия, но relayd поддерживает множество других методов, начиная от ping и заканчивая попыткой установить SSLсоединение). При этом, если один из DNS-серверов окажется в дауне, relayd автоматически исключит его из списка до тех пор, пока плановая проверка на доступность (интервал между проверками указан в опции interval) не покажет его работоспособность.

Для тестирования конфигурации можно использовать следующую форму запуска relayd:

# relayd -d -vv -f /etc/relayd.conf

Так демон не уйдет в фон и будет вести подробную распечатку всех своих действий. После отладки конфигурации можно настроить запуск демона во время загрузки системы. Для этого достаточно поместить строку relayd_flags=»» в файл /etc/rc.conf.local.

FreeBSD fscd: красота минимализма

Этого раздела не должно было быть в статье. Демон fscd настолько простой инструмент, что писать о нем отдельно мне казалось излишним. С другой стороны, не писать о нем нельзя, потому как это один из ярчайших примеров правильного решения задачи в стиле UNIX. А задача у разработчиков FreeBSD была следующая.
Различные системные и не очень демоны могут время от времени падать (или начинать вести себя как идиоты, что еще хуже). На домашней машине это не страшно, упавшего можно перезапустить руками или отправить комп в перезагрузку. Но что делать на сервере, где админ бывает редко?

Сервисы надо мониторить и перезапускать по мере необходимости. Как это сделать? Естественно, встроить эту функциональность в систему инициализации (ведь именно она занимается запуском демонов). И, например, в Solaris так и сделано, да настолько экстравагантно, что сам Линус Торвальдс ногу сломит, пока разберется с его настройкой. Разработчики FreeBSD поступили проще. Они написали отдельный демон, который способен работать со скриптами инициализации FreeBSD, оставаясь совершенно независимой системой. Вся соль в том, что fscd получился настолько простым, что им можно пользоваться, не читая man-страниц и не беспокоясь о том, что он может упасть. Посуди сам, чтобы заставить fscd следить за, например, sshd, нужно ввести всего одну команду:

# fscadm enable sshd /var/run/sshd.pid

Все, fscd запомнит этот выбор и автоматически включит мониторинг после перезагрузки машины. Единственное условие: у подконтрольного демона должен быть инициализационный файл в каталоге /etc/rc.d (или /usr/local/etc/rc.d) и запись в файле /etc/rc.conf, включающая его (это очевидно).

Демон fscd будет доступен только во FreeBSD 9.0, но его вполне можно скачать с официальной странички (people.freebsd.org/~trhodes/fsc) и собрать для восьмерки.

Выводы

Каждый день в мире UNIX появляется что-то новое, но очень редко это новое оказывается чем-то стоящим нашего внимания. В этой статье я рассказал о четырех системных компонентах UNIX, которые не только заслуживают особого внимания, но и несут реальную пользу. Кто знает, возможно в будущем они будут такой же неотъемлемой частью UNIX, как команда grep или демон syslog.

Links

  • дом systemd под крылом: freedesktop.org/wiki/Software/systemd ; freedesktop.org
  • исходные тексты systemd: cgit.freedesktop.org/systemd ;
  • код ulatencyd: github.com/poelzi/ulatencyd ;
  • исходники fscd: people.freebsd.org/~trhodes/fsc .

Info

  • В долгосрочной перспективе автор собирается превратить systemd в полноценный менеджер сессий, способный заменить gnomesession и kdeinit.
  • Кроме всего прочего, systemd обладает функциями монитора для демонов, так что возможности fscd встроены в него от рождения.
  • Отказ от использования скриптов - один из методов ускорить процесс загрузки. Многие задачи инициализации systemd способен произвести через прямой вызов нужных команд, без использования скриптов.
  • Прежнее название relayd - hostated (от слов host state, «состояние хоста»), было изменено на теперешнее в связи с расширением функционала.