Compare commits

...

3 Commits
v8.1 ... v8.4

Author SHA1 Message Date
nnz1024
5c9a949351 Version v8.4 (2011-12-29 21:34) [AUTO] 2017-08-17 23:05:38 +03:00
nnz1024
f1fbbf08cf Version v8.3 (2011-12-15 03:23) [AUTO] 2017-08-17 23:05:38 +03:00
nnz1024
0905f9aa1d Version v8.2 (2011-12-15 02:11) [AUTO] 2017-08-17 23:05:38 +03:00

318
s4a.tex
View File

@@ -16,7 +16,11 @@ pdfauthor={Lennart Poettering, Sergey Ptashnick}}
% Несколько сокращений
\newcommand{\sectiona}[1]{\section*{#1}\addcontentsline{toc}{section}{#1}}
\newcommand{\hreftt}[2]{\href{#1}{\texttt{#2}}}
\newcommand{\llangl}{\reflectbox{\rotatebox[origin=c]{270}{$\neg$}}}
\newcommand{\tbs}{\textbackslash}
% Примерный аналог символа \testSFii (присутствует в листингах),
% но без использования пакета pmboxdraw, средствами graphicx
\newcommand{\mytextSFii}{\raisebox{0pt}[0pt][\depth]{%
\reflectbox{\rotatebox[origin=t]{270}{$\neg$}}}}
% Настройка макета страницы
\setlength{\hoffset}{-1.5cm}
\addtolength{\textwidth}{2cm}
@@ -581,6 +585,7 @@ systemd именует группы в соответствии с назван
последующих статьях мы обсудим этот вопрос более подробно.
\section{HOW-TO: преобразование SysV init-скрипта в systemd service-файл}
\label{sec:convert}
Традиционно, службы Unix и Linux (демоны) запускаются через SysV init-скрипты.
Эти скрипты пишутся на языке Bourne Shell (+/bin/sh+), располагаются в
@@ -2112,6 +2117,7 @@ systemd?
вместо бинарника демона.
\section{Экземпляры служб}
\label{sec:instances}
Большинство служб в Linux/Unix являются одиночными (singleton): в каждый момент
времени на данном хосте работает только один экземпляр службы. В качестве
@@ -2193,7 +2199,7 @@ serial-getty@ttyUSB0.service - Getty on ttyUSB0
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
\llangl{} 5443 /sbin/agetty -s ttyUSB0 115200,38400,9600
\mytextSFii{} 5443 /sbin/agetty -s ttyUSB0 115200,38400,9600
\end{Verbatim}
Собственно, это и есть ключевая идея организации экземпляров служб. Как видите,
systemd предоставляет простой в использовании механизм шаблонов, позволяющих
@@ -2203,8 +2209,8 @@ systemd предоставляет простой в использовании
Вы можете создавать дополнительные экземпляры таких служб, просто добавляя
символьные ссылки в каталоги +*.wants/+. Например, чтобы обеспечить запуск getty
на ttyUSB0 при каждой загрузке, достаточно создать такую ссылку:
\begin{Verbatim}
# ln -s /lib/systemd/system/serial-getty@.service \
\begin{Verbatim}[commandchars=\\\{\}]
# ln -s /lib/systemd/system/serial-getty@.service \tbs{}
/etc/systemd/system/getty.target.wants/serial-getty@ttyUSB0.service
\end{Verbatim}
При этом файл конфигурации, на который указывает ссылка (в нашем случае
@@ -2240,15 +2246,9 @@ systemd и заложен предварительный поиск файла
результат. Но этим грешат практически все алгоритмы экранирования. В данном
случае, были использован механизм экранирования из udev, с одним изменением. В
конце концов, нам нужно было выбрать что-то. Если вы собираетесь комментировать
наш алгоритм экранирования~--- пожалуйста, также укажите, где вы живете, чтобы я
мог приехать к вам и раскрасить ваш велосипедный гараж в синий с желтыми
полосами. Спасибо!}\,\footnote{Прим. перев.: В предыдущем примечании автор снова
намекает на Паркинсовский Закон Тривиальности. Действительно, выбор алгоритма
экранирования практически не~влияет ни на работу systemd, ни на управлением им
(файлы/юниты с экранированными именами, как правило либо создаются специальными
программами-генераторами, либо формируются systemd на лету; при выводе состояния
показываются и неэкранированные имена; при вводе команд, как обычно, на
помощь приходит автодополнение оболочки).}. Например, чтобы обратиться
наш алгоритм экранирования~--- пожалуйста, оставьте свой адрес, чтобы я
мог приехать к вам и раскрасить ваш сарай для велосипедов в синий с желтыми
полосами. Спасибо!}. Например, чтобы обратиться
последовательному USB-порту по его адресу на шине, нам нужно использовать имя
наподобие +serial/by-path/pci-0000:00:1d.0-usb-0:1.4:1.1-port0+. Экранированная
версия этого имени~---
@@ -2262,7 +2262,7 @@ systemd и заложен предварительный поиск файла
строке (+ExecStart+) и для формирования читабельных строк описания. Рассмотрим
работу этих принципов на примере нашего юнит-файла:
\begin{landscape}
\begin{Verbatim}[fontsize=\small]
\begin{Verbatim}[fontsize=\small,commandchars=|\{\}]
# 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
@@ -2270,7 +2270,7 @@ serial-getty@serial-by\x2dpath-pci\x2d0000:00:1d.0\x2dusb\x2d0:1.4:1.1\x2dport0.
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
|mytextSFii{} 5788 /sbin/agetty -s serial/by-path/pci-0000:00:1d.0-usb-0:1.4:1.1-port0 115200 38400 9600
\end{Verbatim}
\end{landscape}
Как видите, в качестве идентификатора экземпляра используется экранированная
@@ -2284,6 +2284,294 @@ serial-getty@serial-by\x2dpath-pci\x2d0000:00:1d.0\x2dusb\x2d0:1.4:1.1\x2dport0.
руководства}, содержащей полный перечень этих спецификаторов с краткими
пояснениями.)
\section{Службы с активацией в стиле inetd}
В одной из предыдущих глав (гл.~\ref{sec:convert}) я рассказывал, как можно
преобразовать 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 предоставляет очень простой интерфейс, который может
быть использован службами для обеспечения сокет-активации. В основе этого
\href{http://0pointer.de/blog/projects/socket-activation.html}{простого и
минималистичного} механизма лежит функция
\hreftt{http://0pointer.de/public/systemd-man/sd_listen_fds.html}{sd\_listen\_fds()}.
Однако, интерфейс, традиционно используемый в inetd, еще проще. Он позволяет
передавать запускаемой службе только один сокет, который формируется из потоков
STDIN и STDOUT запущенного процесса. Поддержка этого механизма также
присутствует в systemd~--- для обеспечения совместимости с множеством служб
поддерживающих сокет-активацию только в стиле inetd.
Прежде, чем мы перейдем к примерам, рассмотрим три различных схемы, использующих
сокет-активацию:
\begin{enumerate}
\item \textbf{Сокет-активация, ориентированная на параллелизацию,
упрощение и надежность:} сокеты создаются на ранних стадиях
загрузки, и единственный экземпляр службы тут же начинает
обслуживать все поступающие запросы. Такая схема подходит для
часто используемых системных служб~--- очевидно, что такие
службы лучше запускать как можно раньше, и по вомзожности
параллельно. В качестве примера можно привести D-Bus и Syslog.
\item \textbf{Сокет-активация для одиночных служб:} сокеты создаются на
ранних стадиях загрузки, и единственный экземпляр службы
запускается при получении входящих запросов. Такая схема больше
подходит для редко используемых служб, позволяя обеспечить
экономию системных ресурсов и уменьшить время загрузки, просто
отложив активацию службы до того момента, когда она
действительно понадобится. Пример~--- CUPS.
\item \textbf{Сокет-активация для служб, запускающих по экземпляру на
каждое соединение:} сокеты создаются на ранней стадии загрузки,
и при каждом входящем соединении создается экземпляр службы,
которому передается сокет соединения (слушающий сокет при этом
остается у суперсервера, inetd или systemd). Эта схема подходит
для редко используемых служб, не~критичных по производительности
(каждый новый процесс занимает сравнительно немного ресурсов).
Пример: SSH.
\end{enumerate}
Описанные схемы отнюдь не~эквивалентны с точки зрения производительности. Первая
и вторая схема, после завершения запуска службы, обеспечивают точно такую же
производительность, как и в случае с независимой (stand-alone) службой (т.е.
службой, запущенной без использования суперсервера и сокет-активации), так как
слушающий сокет передается непосредственно процессу службы, и дальнейшая
обработка входящих соединений происходит точно так же, как и в независимой
службе. В то же время, производительность третьей из предложенных схем порою
оставляет желать лучшего: каждое входящее соединение порождает еще один процесс
службы, что, при большом количестве соединений, может привести к значительному
потреблению системных ресурсов. Впрочем, эта схема имеет и свои достоинства.
В частности, обеспечивается эффективная изоляция обработки клиентских запросов
и, кроме того, выбор такой схемы активации значительно упрощает процесс
разработки службы.
В systemd наибольшее внимание уделяется первой из этих схем, однако остальные
две тоже прекрасно поддерживаются. В свое время, я
\href{http://0pointer.de/blog/projects/socket-activation2.html}{рассказывал},
какие изменения в коде службы нужно сделать для обеспечения работы по второй
схеме, на примере сервера CUPS. Что же касается inetd, то он предназначен прежде
всего для работы по третьей схеме, хотя поддерживает и вторую (но не~первую).
Именно из-за специфики этой схемы inetd получил репутации <<медленного>> (что,
на самом деле, немного несправедливо).
Итак, изучив теоретическую сторону вопроса, перейдем к практике и рассмотрим,
как inetd-служба может быть интегрирована с механизмами сокет-активации systemd.
В качестве примера возьмем SSH, известную и широко распространенную службу. На
большинстве систем, где она используется, частота обращений к ней не~превышает
одного раза в час (как правило, она \emph{значительно} меньше этой величины).
SSH уже очень давно поддерживает сокет-активацию в стиле inetd, согласно третьей
схеме. Так как необходимость в этой службе возникает сравнительно редко, и
число одновременно работающих процессов обычно невелико, она хорошо подходит для
использования по этой схеме. Перерасход системных ресурсов должен быть
незначителен: большую часть времени такая служба фактически не~выполняется и
не~тратит ресурсы. Когда кто-либо начинает сеанс удаленной работы, она
запускается, и останавливается немедленно по завершению сеанса, освобождая
ресурсы. Что ж, посмотрим, как в systemd можно воспользоваться режимом
совместимости с inetd и обеспечить сокет-активацию SSH.
Так выглядит строка с конфигурацией службы SSH для классического inetd:
\begin{Verbatim}
ssh stream tcp nowait root /usr/sbin/sshd sshd -i
\end{Verbatim}
Аналогичный фрагмент конфигурации для xinetd:
\begin{Verbatim}
service ssh {
socket_type = stream
protocol = tcp
wait = no
user = root
server = /usr/sbin/sshd
server_args = -i
}
\end{Verbatim}
Б\'{о}льшая часть опций интуитивно понятна, кроме того, нетрудно заметить, что оба
этих текста повествуют об одном и том же. Однако, некоторые моменты не~вполне
очевидны. Например, номер прослушиваемого порта (22) не~указывается в явном
виде. Он определяется путем поиска имени службы в базе данных +/etc/services+.
Подобный подход был некогда весьма популярен в Unix, но сейчас он уже постепенно
выходит из моды, и поэтому в современных версиях xinetd поддерживается возможность
явного указания номера порта. Одна из наиболее интересных настроек названа
не~очень очевидно~--- +nowait+ (+wait=no+). Она определяет, будет ли служба
работать по второй (+wait+) или третьей (+nowait+) схеме. И наконец, ключ +-i+
активирует inetd-режим в SSH-сервере (без этого ключа он работает в независимом
режиме).
На языке systemd эти фрагменты конфигурации превращаются в два файла. Первый из
них, +sshd.socket+, содержит информацию о прослушиваемом сокете:
\begin{Verbatim}
[Unit]
Description=SSH Socket for Per-Connection Servers
[Socket]
ListenStream=22
Accept=yes
[Install]
WantedBy=sockets.target
\end{Verbatim}
Смысл большинства опций вполне очевиден. Сделаю лишь пару замечаний.
+Accept=yes+ соответствует режиму +nowait+. Надеюсь, предложенное мною название
более точно отражает смысл опции~--- в режиме +nowait+ суперсвервер сам вызывает
+accept()+ для слушающего сокета, в то время как в режиме +wait+ эта работа
ложится на процесс службы. Опция +WantedBy=sockets.target+ обеспечивает
активацию данного юнита в нужный момент при загрузке системы.
Второй из этих файлов~--- +sshd@.service+:
\begin{Verbatim}
[Unit]
Description=SSH Per-Connection Server
[Service]
ExecStart=-/usr/sbin/sshd -i
StandardInput=socket
\end{Verbatim}
Большинство представленных здесь опции, как и раньше, понятны интуитивно. Особое
внимание стоит обратить лишь на +StandardInput=socket+, которая, собственно, и
включает для данной службы режим совместимости с inetd-активацией. Опция
+StandardInput=+ позволяет указать, куда будет подключен поток STDIN процесса
данной службы (подробности см.
\href{http://0pointer.de/public/systemd-man/systemd.exec.html}{на странице
руководства}). Задав для нее значение +socket+, мы обеспечиваем подключение
этого потока к сокету соединения, как того и требует механизм inetd-активации.
Заметим, что явно указывать опцию +StandardInput=+ в данном случае
необязательно~--- она автоматически устанавливается в то же значение, что и
+StandardInput+, если явно не~указано что-то другое. Кроме того, можно отметить
наличие <<+-+>> перед именем бинарного файла sshd. Таким образом мы указываем
systemd игнорировать код выхода процесса sshd. По умолчанию, systemd сохраняет
коды выхода для всех экземпляров службы, завершившихся с ошибкой (сбросить эту
информацию можно командой +systemctl reset-failed+). SSH
периодически откалывает подобные номера, и мы разрешаем systemd
не~регистрировать подобные <<ошибки>>.
Служба +sshd@.service+ предназначена для работы в виде независимых экземпляров
(такие службы мы рассматривали в предыдущей главе~\ref{sec:instances}). Для
каждого входящего соединения systemd будет создавать свой экземпляр этой службы,
причем идентификатор экземпляра формируется на основе реквизитов соединения.
Быть может, вы спросите: почему для настройки inetd-службы в systemd требуется
два файла конфигурации, а не~один? Отвечаем: чтобы избежать излишнего
усложнения, мы обеспечиваем максимально прозрачную связь между работающими
юнитами и соответствующими им юнит-файлами. Такой подход позволяет независимо
оперировать юнитом сокета и юнитами соответствующих служб при формировании графа
зависимостей и при управлении юнитами. В частности, вы можете остановить
(удалить) сокет, не~затрагивая уже работающие экземпляры соответствующей службы,
или остановить любой из этих экземпляров, не~трогая другие экземпляры и сам
сокет.
Посмотрим, как наш пример будет работать. После того, как мы поместим оба
предложенных выше файла в каталог +/etc/systemd/system+, мы сможем включить
сокет (то есть, обеспечить его активацию при каждой нормальной загрузке) и
запустить его (то есть активировать в текущем сеансе работы):
\begin{Verbatim}
# 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
\end{Verbatim}
Итак, наш сокет уже прослушивается, но входящих соединений на него пока
не~поступало (счетчик +Accepted:+ показывает количество принятых соединений
с момента запуска сокета, счетчик +Connected:+~--- количество активных
соединений на текущий момент).
Подключимся к нашему серверу с двух разных хостов, и посмотрим на список служб:
\begin{Verbatim}[fontsize=\small]
$ 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
\end{Verbatim}
Как и следовало ожидать, работают два экземпляра нашей службы, по одному на
соединение, и их в названиях указаны IP-адреса и TCP-порты источника и
получателя. (Заметим, что в случае с локальными сокетами типа +AF_UNIX+ там были
бы указаны идентификаторы процесса и пользователя, соответствующие подключенному
клиенту.) Таким образом, мы можем независимо выслеживать и убивать отдельные
экземпляры sshd (например, если нам нужно прервать конкретный удаленный сеанс):
\begin{Verbatim}
# systemctl kill sshd@172.31.0.52:22-172.31.0.4:47779.service
\end{Verbatim}
Вот и все, что вам нужно знать о портировании inetd-служб в systemd и дальнейшем
их использовании.
Приметительно к SSH, в большинстве случаев схема с inetd-активацией позволяет
сэкономить системные ресурсы и поэтому оказывается более эффективным решением,
чем использование обычного service-файла sshd, обеспечивающего запуск одиночной
службы без использования сокет-активации (поставляется в составе пакета и может
быть включен при необходимости). В ближайшее время я собираюсь направить
соответсвующий запрос относительно нашего пакета SSH в багтрекер Fedora.
В завершение нашей дискуссии, сравним возможности xinetd и systemd, и выясним,
может ли systemd полностью заменить xinetd, или нет. Вкратце: systemd
поддерживает далеко не~все возможности xinetd и поэтому отнюдь не~является его
полноценной заменой на все случаи жизни. В частности, если вы загляните
в \href{http://linux.die.net/man/5/xinetd.conf}{список параметров конфигурации}
xinetd, вы заметите, что далеко не~все эти опции доступны в systemd. Например, в
systemd нет и не~будет встроенных служб +echo+, +time+, +daytime+, +discard+ и
т.д. Кроме того, systemd не~поддерживает TCPMUX и RPC. Впрочем, большинство этих
опций уже не~актуальны в наше время. Подавляющее большинство inetd-служб
не~используют эти опции (в частности, все имеющиеся в Fedora xinetd-службы
принадлежат к этому большинству, и не~используют перечисленные опции). Стоит
отметить, что xinetd поддерживает некоторые интересные возможности,
отсутствующие в systemd, например, списки контроля доступа для IP-адресов (IP
ACL). Однако, большинство администраторов, скорее всего, согласятся, что
настройка барндмауэра является более эффективным решением подобных задач,
а для ценителей устаревших технологий systemd предлагает поддержку tcpwrap.
С другой стороны, systemd тоже предоставляет ряд возможностей, отсутствующих в
xinetd, в частности, индивидуальный контроль над каждым экземпляром службы (см.
выше), и куда более полный
\href{http://0pointer.de/public/systemd-man/systemd.exec.html}{список настроек}
для контроля окружения, в котором запускаются экземпляры. Я надеюсь, что
возможностей systemd должно быть достаточно для решения большинства задач, а в
тех редких случаях, когда вам потребуются специфические опции xinetd~--- ничто
не~мешает вам запустить его в дополнение к systemd. Таким образом, уже сейчас в
большинстве случаев xinetd можно выкинуть из числа обязательных системных
компонентов. В некотором смысле, systemd возвращает функциональность
классического юниксового inetd и вновь возвращает ей ключевую роль в
Linux-системах.
Теперь, вооруженные этими занниями, вы можете портировать свои службы с inetd на
systemd. Но, конечно, будет лучше, если этим займутся разработчики из апстрима
приложения, или сопровождающие вашего дистрибутива.
\end{document}
vim:ft=tex:tw=80:spell:spelllang=ru