13 Отчет о состоянии службы и ее журнал



Pdf просмотр
страница6/12
Дата09.11.2016
Размер5.01 Kb.
Просмотров3083
Скачиваний0
1   2   3   4   5   6   7   8   9   ...   12
В
том случае,
если требование совместимости критично,
вы можете за- действовать эти конфигурационные файлы даже в
том случае,
если на- страиваете службы через штатные unit-файлы systemd.
Если ваш файл из sysconfig содержит лишь определения переменных, можно воспользоваться оп- цией
EnvironmentFile=-/etc/sysconfig/foobar
(подробнее об этой опции см.
systemd.exec(5)
), позволяющей прочитать из файла набор переменных окружения,
который будет установлен при запуске службы. Если же для задания настроек вам необходим полноценный язык программирования — ничто не мешает им воспользо- ваться. Например, вы можете создать в /usr/lib// простой скрипт,
который включает соответствующие файлы, а затем запускает бинарник демона через exec. После чего достаточно просто указать этот скрипт в опции ExecStart= вместо бинарника демона.
10
Экземпляры служб
Большинство служб в Linux/Unix являются одиночными (singleton): в каждый мо- мент времени на данном хосте работает только один экземпляр службы. В качестве примера таких одиночных служб можно привести Syslogd, Postfix, Apache. Однако, су- ществуют службы, запускающие по несколько экземпляров себя на одном хосте. На- пример, службы наподобие Dovecot IMAP запускают по одному экземпляру на каждый локальный порт и/или IP-адрес. Другой пример, который можно встретить практически во всех системах — getty, небольшая служба, запускающаяся на каждом TTY (от tty1
до tty6). На некоторых серверах, в зависимости от сделанных администратором настро- ек или параметров загрузки, могут запускаться дополнительные экземпляры getty, для подключаемых к COM-портам терминалов или для консоли системы виртуализации.
Еще один пример службы, работающей в нескольких экземплярах (по крайней мере,
в мире systemd) — fsck, программа проверки файловой системы, которая запускается по одному экземпляру на каждое блочное устройство, требующее такой проверки. И
наконец, стоит упомянуть службы с активацией в стиле inetd — при обращении через сокет, по одному экземпляру на каждое соединение. В этой статье я попытаюсь расска- зать, как в systemd реализовано управление «многоэкземплярными» службами, и какие выгоды системный администратор может извлечь из этой возможности.
Если вы читали предыдущие статьи из этого цикла, вы, скорее всего, уже знаете, что службы systemd именуются по схеме foobar .service, где foobar — строка, идентифици-
41
рующая службу (проще говоря, ее имя), а .service — суффикс, присутствующий в име- нах всех файлов конфигурации служб. Сами эти файлы могут находиться в каталогах
/etc/systemd/systemd и /lib/systemd/system (а также, возможно, и в других
41
). Для служб, работающих в нескольких экземплярах, эта схема становится немного сложнее:
foobar @quux .service, где foobar — имя службы, общее для всех экземпляров, а quux —
идентификатор конкретного экземпляра. Например, serial-gett@ttyS2.service — это служба getty для COM-порта, запущенная на ttyS2.
При необходимости, экземпляры служб можно легко создать динамически. Скажем,
вы можете, безо всяких дополнительных настроек, запустить новый экземпляр getty на последовательном порту, просто выполнив systemctl start для нового экземпляра:
# systemctl start serial-getty@ttyUSB0.service
Получив такую команду, systemd сначала пытается найти файл конфигура- ции юнита с именем, точно соответствующим запрошенному. Если такой файл найти не удается (при работе с экземплярами служб обычно так и происхо- дит), из имени файла удаляется идентификатор экземпляра, и полученное имя используется при поиске шаблона конфигурации. В нашем случае, если отсут- ствует файл с именем serial-getty@ttyUSB0.service, используется файл-шаблон под названием serial-getty@.service. Таким образом, для всех экземпляров дан- ной службы, используется один и тот же шаблон конфигурации. В случае с getty для COM-портов, этот шаблон, поставляемый в комплекте с systemd (файл
/lib/systemd/system/serial-getty@.service) выглядит примерно так:
[Unit]
Description=Serial Getty on %I
BindTo=dev-%i.device
After=dev-%i.device systemd-user-sessions.service
[Service]
ExecStart=-/sbin/agetty -s %I 115200,38400,9600
Restart=always
RestartSec=0
(Заметим, что приведенная здесь версия немного сокращена, по сравнению с реально используемой в systemd. Удалены не относящиеся к теме нашего обсуждения парамет- ры конфигурации, обеспечивающие совместимость с SysV, очистку экрана и удаление предыдущих пользователей с текущего TTY. Если вам интересно, можете посмотреть полную версию
.)
Этот файл похож на обычный файл конфигурации юнита, с единственным отличием:
в нем используются спецификаторы %I и %i. В момент загрузки юнита, systemd заменя- ет эти спецификаторы на идентификатор экземпляра службы. В нашем случае, при об- ращении к экземпляру serial-getty@ttyUSB0.service, они заменяются на «ttyUSB0».
Результат такой замены можно проверить, например, запросив состояние для нашей службы:
$ systemctl status serial-getty@ttyUSB0.service serial-getty@ttyUSB0.service - Getty on ttyUSB0
Loaded: loaded (/lib/systemd/system/serial-getty@.service; static)
Active: active (running) since Mon, 26 Sep 2011 04:20:44 +0200; 2s ago
Main PID: 5443 (agetty)
CGroup: name=systemd:/system/getty@.service/ttyUSB0 5443 /sbin/agetty -s ttyUSB0 115200,38400,9600 41
Прим. перев.: Перечень каталогов, в которых выполняется поиск общесистемных юнит-файлов, при- веден на странице руководства systemd(1)
(раздел «System unit directories»). Указанные выше каталоги
/etc/systemd/systemd и /lib/systemd/system соответствуют значениям по умолчанию для упомянутых там переменных pkg-config systemdsystemconfdir и systemdsystemunitdir соответственно. Начиная с systemd версии 198, данный перечень, в более точной и развернутой форме, присутствует на странице systemd.unit(5)
42

Собственно, это и есть ключевая идея организации экземпляров служб. Как видите,
systemd предоставляет простой в использовании механизм шаблонов, позволяющих ди- намически создавать экземпляры служб. Добавим несколько дополнительных замеча- ний, позволяющих эффективно использовать этот механизм:
Вы можете создавать дополнительные экземпляры таких служб, просто добавляя символьные ссылки в каталоги *.wants/. Например, чтобы обеспечить запуск getty на ttyUSB0 при каждой загрузке, достаточно создать такую ссылку:
# ln -s /lib/systemd/system/serial-getty@.service \
/etc/systemd/system/getty.target.wants/serial-getty@ttyUSB0.service
При этом файл конфигурации, на который указывает ссылка (в нашем случае serial-getty@.service), будет вызван с тем именем экземпляра, которое указанно в названии этой ссылки (в нашем случае — ttyUSB0).
Вы не сможете обратиться к юниту-шаблону без указания идентификатора экземпля- ра. В частности, команда systemctl start serial-getty@.service завершится ошиб- кой.
Иногда возникает необходимость отказаться от использования общего шаблона для конкретного экземпляра (т.е. конфигурация данного экземпляра настолько сильно отли- чается от параметров остальных экземпляров данной службы, что механизм шаблонов оказывается неэффективен). Специально для таких случаев, в systemd и заложен пред- варительный поиск файла с именем, точно соответствующим указанному (прежде чем использовать общий шаблон). Таким образом, вы можете поместить файл с именем, точ- но соответствующим полному титулу экземпляра, в каталог /etc/systemd/system/ —
и содержимое этого файла, при обращении к выбранному экземпляру, полностью пере- кроет все настройки, сделанные в общем шаблоне.
В приведенном выше файле, в некоторых местах используется спецификатор %I, а в других — %i. У вас может возникнуть закономерный вопрос — чем они отличаются? %i всегда точно соответствует идентификатору экземпляра, в то время, как %I соответству- ет неэкранированной (unescaped) версии этого идентификатора. Когда идентификатор не содержит спецсимволов (например, ttyUSB0), результат в обоих случаях одинако- вый. Но имена устройств, например, содержат слеши («/»), которые не могут присут- ствовать в имени юнита (и в имени файла на Unix). Поэтому, перед использованием такого имени в качестве идентификатора устройства, оно должно быть экранировано —
«/» заменяются на «-», а большинство других специальных символов (включая «-») за- меняются последовательностями вида \xAB, где AB — ASCII-код символа, записанный в шестнадцатеричной системе счисления
42
. Например, чтобы обратиться к последова- тельному USB-порту по его адресу на шине, нам нужно использовать имя наподобие serial/by-path/pci-0000:00:1d.0-usb-0:1.4:1.1-port0. Экранированная версия это- го имени — serial-by\x2dpath-pci\x2d0000:00:1d.0\x2dusb\x2d0:1.4:1.1\x2dport0.
%I будет ссылаться на первую из этих строк, %i — на вторую. С практической точки зрения, это означает, что спецификатор %i можно использовать в том случае, когда надо сослаться на имена других юнитов, например, чтобы описать дополнительные зависимо- сти (в случае с serial-getty@.service, этот спецификатор используется для ссылки на юнит dev-%i.device, соответствующий одноименному устройству). В то время как %I
удобно использовать в командной строке (ExecStart) и для формирования читабельных строк описания. Рассмотрим работу этих принципов на примере нашего юнит-файла
43
:
42
Согласен, этот алгоритм дает на выходе не очень читабельный результат. Но этим грешат практи- чески все алгоритмы экранирования. В данном случае, был использован механизм экранирования из udev, с одним изменением. В конце концов, нам нужно было выбрать что-то. Если вы собираетесь ком- ментировать наш алгоритм экранирования — пожалуйста, оставьте свой адрес, чтобы я мог приехать к вам и раскрасить ваш сарай для велосипедов в синий с желтыми полосами. Спасибо!
43
Прим. перев.: как видно из нижеприведенного примера, в командной строке systemctl использу- ется экранированное имя юнита, что создает определенные трудности даже при наличии в оболочке
«умного» автодополнения. Однако, начиная с systemd v186, при работе с systemctl можно указывать неэкранированные имена юнитов.
43

# systemctl start ’serial-getty@serial-by\x2dpath-pci\x2d0000:00:1d.0\x2dusb\x2d0:1.4:1.1\x2dport0.service’
# systemctl status ’serial-getty@serial-by\x2dpath-pci\x2d0000:00:1d.0\x2dusb\x2d0:1.4:1.1\x2dport0.service’
serial-getty@serial-by\x2dpath-pci\x2d0000:00:1d.0\x2dusb\x2d0:1.4:1.1\x2dport0.service - Serial Getty on serial/by-path/pci-0000:00:1d.0-usb-0:1.4:1.1-port0
Loaded: loaded (/lib/systemd/system/serial-getty@.service; static)
Active: active (running) since Mon, 26 Sep 2011 05:08:52 +0200; 1s ago
Main PID: 5788 (agetty)
CGroup: name=systemd:/system/serial-getty@.service/serial-by\x2dpath-pci\x2d0000:00:1d.0\x2dusb\x2d0:1.4:1.1\x2dport0 5788 /sbin/agetty -s serial/by-path/pci-0000:00:1d.0-usb-0:1.4:1.1-port0 115200 38400 9600 44

Как видите, в качестве идентификатора экземпляра используется экранированная версия, в то время как в строке описания и в командной строке getty фигурирует неэкранированный вариант, как и предполагалось.
(Небольшое замечание: помимо %i и %I, существует еще несколько спецификаторов,
и большинство из них доступно и в обычных файлах конфигурации юнитов, а не толь- ко в шаблонах. Подробности можно посмотреть на странице руководства
, содержащей полный перечень этих спецификаторов с краткими пояснениями.)
11
Службы с активацией в стиле inetd
В одной из предыдущих глав (гл.
3
) я рассказывал, как можно преобразовать SysV
init-скрипт в юнит-файл systemd. В этой главе мы рассмотрим, как провести аналогич- ное преобразование для служб inetd.
Начнем с небольшого экскурса в историю вопроса. Уже многие годы inetd считает- ся одной из базовых служб Unix-систем. Работая как суперсервер, он слушает сетевые сокеты от имени различных служб, и активирует соответствующие службы при поступ- лении на эти сокеты входящих соединений. Таким образом, он обеспечивает механизм активации по запросу (on-demand activation). Подобный подход избавляет от необхо- димости держать постоянно работающими все серверные процессы, что позволяет под- держивать множество служб даже на системах с очень ограниченными ресурсами. В
дистрибутивах Linux можно встретить несколько различных реализаций inetd. Наибо- лее популярные из них — BSD inetd и xinetd. Хотя inetd во многих дистрибутивах до сих пор устанавливается по умолчанию, сейчас он уже редко используется для запус- ка сетевых служб — теперь большинство из них запускаются автономно при загрузке системы (основной аргумент в пользу такой схемы — она позволяет обеспечить более высокую производительность служб).
Одной из ключевых возможностей systemd (а также launchd от Apple) является сокет-активация — тот же самый механизм, давным-давно реализованный inetd, од- нако в данном случае ключевые цели немного другие. Сокет-активация в стиле systemd прежде всего ориентирована на локальные сокеты (AF_UNIX), хотя поддерживаются так- же и сетевые сокеты (AF_INET). И более важное отличие — сокет-активация в systemd обеспечивает не только экономию системных ресурсов, но также и эффективную парал- лелизацию работы (так как она позволяет запускать клиентские и серверные процессы одновременно, непосредственно после создания сокета), упрощение процесса конфигура- ции (отпадает необходимость явно указывать зависимости между службами) и увеличе- ние надежности (перезапуск службы, служебный или экстренный — в случае падения
не приводит к недоступности сокета). Тем не менее, systemd ничуть не хуже inetd может запускать службы в ответ на входящие сетевые соединения.
Любая сокет-активация требует соответствующей поддержки со стороны самой за- пускаемой службы. systemd предоставляет очень простой интерфейс, который может быть использован службами для обеспечения сокет-активации. В основе этого простого и минималистичного механизма лежит функция sd_listen_fds()
. Однако, интерфейс,
традиционно используемый в inetd, еще проще. Он позволяет передавать запускаемой службе только один сокет, который формируется из потоков STDIN и STDOUT запу- щенного процесса. Поддержка этого механизма также присутствует в systemd — для обеспечения совместимости со множеством служб, у которых сокет-активация реализо- вана только в стиле inetd.
Прежде, чем мы перейдем к примерам, рассмотрим три различных схемы, исполь- зующих сокет-активацию:
1. Сокет-активация, ориентированная на параллелизацию, упрощение и надежность: сокеты создаются на ранних стадиях загрузки, и единственный эк- земпляр службы тут же начинает обслуживать все поступающие запросы. Такая схема подходит для часто используемых системных служб — очевидно, что такие службы лучше запускать как можно раньше, и по возможности параллельно. В
качестве примера можно привести D-Bus и Syslog.
45

2. Сокет-активация для одиночных служб: сокеты создаются на ранних стади- ях загрузки, и единственный экземпляр службы запускается при получении вхо- дящих запросов. Такая схема больше подходит для редко используемых служб,
позволяя обеспечить экономию системных ресурсов и уменьшить время загруз- ки, просто отложив активацию службы до того момента, когда она действительно понадобится. Пример — CUPS.
3. Сокет-активация для служб, запускающих по экземпляру на каждое со- единение: сокеты создаются на ранней стадии загрузки, и при каждом входящем соединении запускается экземпляр службы, которому передается сокет соедине- ния (слушающий сокет при этом остается у суперсервера, inetd или systemd). Эта схема подходит для редко используемых служб, не критичных по производитель- ности (каждый новый процесс занимает сравнительно немного ресурсов). Пример:
SSH.
Описанные схемы отнюдь не эквивалентны с точки зрения производительности. Пер- вая и вторая схема, после завершения запуска службы, обеспечивают точно такую же производительность, как и в случае с независимой (stand-alone) службой (т.е. службой,
запущенной без использования суперсервера и сокет-активации), так как слушающий сокет передается непосредственно процессу службы, и дальнейшая обработка входящих соединений происходит точно так же, как и в независимой службе. В то же время,
производительность третьей из предложенных схем порою оставляет желать лучшего:
каждое входящее соединение порождает еще один процесс службы, что, при большом количестве соединений, может привести к значительному потреблению системных ре- сурсов. Впрочем, эта схема имеет и свои достоинства. В частности, обеспечивается эф- фективная изоляция обработки клиентских запросов и, кроме того, выбор такой схемы активации значительно упрощает процесс разработки службы.
В systemd наибольшее внимание уделяется первой из этих схем, однако остальные две тоже прекрасно поддерживаются. В свое время, я рассказывал
, какие изменения в коде службы нужно сделать для обеспечения работы по второй схеме, на примере сервера
CUPS. Что же касается inetd, то он предназначен прежде всего для работы по третьей схеме, хотя поддерживает и вторую (но не первую). Именно из-за специфики этой схемы inetd получил репутации «медленного» (что, на самом деле, немного несправедливо).
Итак, изучив теоретическую сторону вопроса, перейдем к практике и рассмотрим,
как inetd-служба может быть интегрирована с механизмами сокет-активации systemd.
В качестве примера возьмем SSH, известную и широко распространенную службу. На большинстве систем, где она используется, частота обращений к ней не превышает од- ного раза в час (как правило, она значительно меньше этой величины). SSH уже очень давно поддерживает сокет-активацию в стиле inetd, согласно третьей схеме. Так как необходимость в данной службе возникает сравнительно редко, и число одновременно работающих процессов обычно невелико, она хорошо подходит для использования по этой схеме. Перерасход системных ресурсов должен быть незначителен: большую часть времени такая служба фактически не выполняется и не тратит ресурсы. Когда кто- либо начинает сеанс удаленной работы, она запускается, и останавливается немедленно по завершении сеанса, освобождая ресурсы. Что ж, посмотрим, как в systemd можно воспользоваться режимом совместимости с inetd и обеспечить сокет-активацию SSH.
Так выглядит строка с конфигурацией службы SSH для классического inetd:
ssh stream tcp nowait root /usr/sbin/sshd sshd -i
Аналогичный фрагмент конфигурации для xinetd:
service ssh {
socket_type = stream protocol = tcp wait = no user = root server = /usr/sbin/sshd server_args = -i
}
46

Б´
ольшая часть опций интуитивно понятна, кроме того, нетрудно заметить, что оба этих текста повествуют об одном и том же. Однако, некоторые моменты не вполне оче- видны. Например, номер прослушиваемого порта (22) не указывается в явном виде.
Он определяется путем поиска имени службы в базе данных /etc/services. Подобный подход был некогда весьма популярен в Unix, но сейчас он уже постепенно выходит из моды, и поэтому в современных версиях xinetd поддерживается возможность явного указания номера порта. Одна из наиболее интересных настроек названа не вполне оче- видно — nowait (wait=no). Она определяет, будет ли служба работать по второй (wait)
или третьей (nowait) схеме. И наконец, ключ -i активирует inetd-режим в SSH-сервере
(без этого ключа он работает в независимом режиме).
На языке systemd эти фрагменты конфигурации превращаются в два файла. Первый из них, sshd.socket, содержит информацию о прослушиваемом сокете:
[Unit]
Description=SSH Socket for Per-Connection Servers
[Socket]
ListenStream=22
Accept=yes
[Install]
WantedBy=sockets.target
Смысл большинства опций вполне очевиден. Сделаю лишь пару замечаний.
Accept=yes соответствует режиму nowait. Надеюсь, предложенное мною название более точно отражает смысл опции — в режиме nowait суперсвервер сам вызывает accept()
для слушающего сокета, в то время как в режиме wait эта работа ложится на про- цесс службы. Опция WantedBy=sockets.target обеспечивает активацию данного юнита в нужный момент при загрузке системы.
Второй из этих файлов — sshd@.service:
[Unit]
Description=SSH Per-Connection Server
[Service]
ExecStart=-/usr/sbin/sshd -i
StandardInput=socket
Большинство представленных здесь опций, как всегда, понятны интуитивно. Особое внимание стоит обратить лишь на строчку StandardInput=socket, которая, собствен- но, и включает для данной службы режим совместимости с inetd-активацией. Опция
StandardInput= позволяет указать, куда будет подключен поток STDIN процесса дан- ной службы (подробности см.
на странице руководства
). Задав для нее значение socket,
мы обеспечиваем подключение этого потока к сокету соединения, как того и требует ме- ханизм inetd-активации. Заметим, что явно указывать опцию StandardOutput= в дан- ном случае необязательно — она автоматически устанавливается в то же значение, что и StandardInput, если явно не указано что-то другое. Кроме того, можно отметить на- личие «-» перед именем бинарного файла sshd. Таким образом мы указываем systemd игнорировать код выхода процесса sshd. По умолчанию, systemd сохраняет коды выхода для всех экземпляров службы, завершившихся с ошибкой (сбросить эту информацию можно командой systemctl reset-failed). SSH довольно часто завершается с ненуле- вым кодом выхода, и мы разрешаем systemd не регистрировать подобные «ошибки».
Служба sshd@.service предназначена для работы в виде независимых экземпляров
(такие службы мы рассматривали в предыдущей главе
10
). Для каждого входящего со- единения systemd будет создавать свой экземпляр этой службы, причем идентификатор экземпляра формируется на основе реквизитов соединения.
Быть может, вы спросите: почему для настройки inetd-службы в systemd требуется два файла конфигурации, а не один? Отвечаем: чтобы избежать излишнего усложне- ния, мы обеспечиваем максимально прозрачную связь между работающими юнитами и
47
соответствующими им юнит-файлами. Такой подход позволяет независимо оперировать юнитом сокета и юнитами соответствующих служб при формировании графа зависимо- стей и при управлении юнитами. В частности, вы можете остановить (удалить) сокет,
не затрагивая уже работающие экземпляры соответствующей службы, или остановить любой из этих экземпляров, не трогая другие экземпляры и сам сокет.
Посмотрим, как наш пример будет работать. После того, как мы поместим оба пред- ложенных выше файла в каталог /etc/systemd/system, мы сможем включить сокет (то есть, обеспечить его активацию при каждой нормальной загрузке) и запустить его (то есть активировать в текущем сеансе работы):
# systemctl enable sshd.socket ln -s ’/etc/systemd/system/sshd.socket’ ’/etc/systemd/system/sockets.target.wants/sshd.socket’
# systemctl start sshd.socket
# systemctl status sshd.socket sshd.socket - SSH Socket for Per-Connection Servers
Loaded: loaded (/etc/systemd/system/sshd.socket; enabled)
Active: active (listening) since Mon, 26 Sep 2011 20:24:31 +0200; 14s ago
Accepted: 0; Connected: 0
CGroup: name=systemd:/system/sshd.socket
Итак, наш сокет уже прослушивается, но входящих соединений на него пока не по- ступало (счетчик Accepted: показывает количество принятых соединений с момента создания сокета, счетчик Connected: — количество активных соединений на текущий момент).
Подключимся к нашему серверу с двух разных хостов, и посмотрим на список служб:
$ systemctl --full | grep ssh sshd@172.31.0.52:22-172.31.0.4:47779.service loaded active running
SSH Per-Connection Server sshd@172.31.0.52:22-172.31.0.54:52985.service loaded active running
SSH Per-Connection Server sshd.socket loaded active listening
SSH Socket for Per-Connection Servers
Как и следовало ожидать, работают два экземпляра нашей службы, по одному на соединение, и их в названиях указаны IP-адреса и TCP-порты источника и получателя.
(Заметим, что в случае с локальными сокетами типа AF_UNIX там были бы указаны идентификаторы процесса и пользователя, соответствующие подключенному клиенту.)
Таким образом, мы можем независимо выслеживать и убивать отдельные экземпляры sshd (например, если нам нужно прервать конкретный удаленный сеанс):


Поделитесь с Вашими друзьями:
1   2   3   4   5   6   7   8   9   ...   12


База данных защищена авторским правом ©nethash.ru 2019
обратиться к администрации

войти | регистрация
    Главная страница


загрузить материал