Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aaa2ec62eb | ||
|
|
56a4425e5a | ||
|
|
5c9a949351 | ||
|
|
f1fbbf08cf | ||
|
|
0905f9aa1d | ||
|
|
50c0e1a158 |
680
s4a.tex
680
s4a.tex
@@ -16,6 +16,11 @@ pdfauthor={Lennart Poettering, Sergey Ptashnick}}
|
||||
% Несколько сокращений
|
||||
\newcommand{\sectiona}[1]{\section*{#1}\addcontentsline{toc}{section}{#1}}
|
||||
\newcommand{\hreftt}[2]{\href{#1}{\texttt{#2}}}
|
||||
\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}
|
||||
@@ -38,7 +43,8 @@ pdfauthor={Lennart Poettering, Sergey Ptashnick}}
|
||||
\author{Lennart P\"{o}ttering (автор)\thanks{Первоисточник (на английском
|
||||
языке) опубликован на сайте автора: \url{http://0pointer.de/blog/projects}}\\%
|
||||
Сергей Пташник (русский перевод)\thanks{Актуальная версия перевода
|
||||
доступна на портале OpenNet: \url{http://wiki.opennet.ru/Systemd}}\\%
|
||||
доступна на личной странице переводчика:
|
||||
\url{http://www2.kangran.su/~nnz/pub/s4a/}}\\%
|
||||
\small Данный документ доступен на условиях лицензии
|
||||
\href{http://creativecommons.org/licenses/by-sa/3.0/legalcode}{CC-BY-SA}}
|
||||
\maketitle
|
||||
@@ -53,7 +59,7 @@ pdfauthor={Lennart Poettering, Sergey Ptashnick}}
|
||||
инициализации. Окончательный переход на systemd произошел лишь в Fedora~15.}.
|
||||
Помимо Fedora, systemd также поддерживает и другие дистрибутивы, в частности,
|
||||
\href{http://en.opensuse.org/SDB:Systemd}{OpenSUSE}\footnote{Прим. перев.:
|
||||
сейчас systemd поддерживается практически во всех популярных дистрибутивах для
|
||||
Сейчас systemd поддерживается практически во всех популярных дистрибутивах для
|
||||
настольных систем.}. systemd предоставляет администраторам целый ряд новых
|
||||
возможностей, значительно упрощающих процесс обслуживания системы. Эта статья
|
||||
является первой в серии публикаций, планируемых в ближайшие месяцы. В каждой из
|
||||
@@ -73,9 +79,9 @@ pdfauthor={Lennart Poettering, Sergey Ptashnick}}
|
||||
systemd эти сообщения будут пробегать все быстрее и быстрее, вследствие чего,
|
||||
читать их будет все труднее. К тому же, многие пользователи применяют
|
||||
графические оболочки загрузки (например, Plymouth), полностью скрывающие эти
|
||||
сообщения. Тем не~менее, информация, которую несут эти сообщения, была и
|
||||
остается чрезвычайно важной~--- они показывают, успешно ли запустилась каждая
|
||||
служба, или попытка ее запуска закончилась ошибкой (зеленое
|
||||
сообщения. Тем не~менее, информация, которую они несут, была и остается
|
||||
чрезвычайно важной~--- они показывают, успешно ли запустилась каждая служба, или
|
||||
попытка ее запуска закончилась ошибкой (зеленое
|
||||
\texttt{[~\textcolor{green}{OK}~]} или красное
|
||||
\texttt{[~\textcolor{red}{FAILED}~]} соответственно). Итак, с ростом скорости
|
||||
загрузки систем, возникает неприятная ситуация: информация о результатах
|
||||
@@ -203,7 +209,9 @@ ntpd.service - Network Time Service
|
||||
\end{Verbatim}
|
||||
|
||||
systemd сообщает нам, что ntpd был запущен (с идентификатором процесса 953) и
|
||||
аварийно завершил работу (с кодом выхода 255).
|
||||
аварийно завершил работу (с кодом выхода 255)\footnote{Прим. перев.:
|
||||
Впоследствии, про просьбам пользователей, считавших, что слово <<maintenance>>
|
||||
недостаточно точно отражает ситуацию, оно было заменено на <<failed>>.}.
|
||||
|
||||
В последующих версиях systemd, мы планируем добавить возможность вызова в
|
||||
таких ситуациях ABRT (Automated Bug Report Tool), но для этого необходима
|
||||
@@ -237,7 +245,7 @@ CGI-программы, а демон cron~--- команды, предписа
|
||||
к разработке демонов). Также, не~будем забывать, что процесс легко может
|
||||
изменить свое имя посредством +PR_SETNAME+, или задав значение
|
||||
+argv[0]+, что также усложняет процесс его опознания\footnote{Прим.
|
||||
перев.: стоит отметить, что перечисленные ситуации могут возникнуть не~только
|
||||
перев.: Стоит отметить, что перечисленные ситуации могут возникнуть не~только
|
||||
вследствие ошибок в коде и/или конфигурации программ, но и в результате злого
|
||||
умысла. Например, очень часто встречается ситуация, когда установленный на
|
||||
взломанном сервере процесс-бэкдор маскируется под нормального демона, меняя
|
||||
@@ -579,7 +587,8 @@ systemd именует группы в соответствии с назван
|
||||
службам, но и процессов, запущенных в рамках пользовательских сеансов. В
|
||||
последующих статьях мы обсудим этот вопрос более подробно.
|
||||
|
||||
\section{HOW-TO: преобразование SysV init-скрипта в systemd service-файл}
|
||||
\section{Преобразование SysV init-скрипта в systemd service-файл}
|
||||
\label{sec:convert}
|
||||
|
||||
Традиционно, службы Unix и Linux (демоны) запускаются через SysV init-скрипты.
|
||||
Эти скрипты пишутся на языке Bourne Shell (+/bin/sh+), располагаются в
|
||||
@@ -657,9 +666,11 @@ Bug Reporting Tool, службы, занимающейся сбором crash du
|
||||
socket-активацию, рассмотрены в статье
|
||||
\href{http://0pointer.de/blog/projects/systemd.html}{Rethinking
|
||||
PID~1}, в которой systemd был впервые представлен широкой
|
||||
публике. Ее русский перевод можно прочитать здесь:
|
||||
публике\footnote{Прим. перев.: Ее русский перевод (сделанный
|
||||
совершенно независимо от данного документа, совсем другим
|
||||
человеком) можно прочитать здесь:
|
||||
\href{http://tux-the-penguin.blogspot.com/2010/09/systemd.html}{часть~1},
|
||||
\href{http://tux-the-penguin.blogspot.com/2010/09/systemd-ii.html}{часть~2}.
|
||||
\href{http://tux-the-penguin.blogspot.com/2010/09/systemd-ii.html}{часть~2}.}.
|
||||
Возвращаясь к нашему примеру: в данном случае ценной
|
||||
информацией о зависимостях является только строка
|
||||
+Required-Start: $syslog+, сообщающая, что для работы
|
||||
@@ -755,14 +766,14 @@ systemd считает службу запущенной с момента за
|
||||
играет важную роль при выполнении команды +systemctl enable+, задавая, в каких
|
||||
условиях должен активироваться устанавливаемый юнит. В нашем примере, служба
|
||||
abrtd будет активироваться при переходе в состояние +multi-user.target+,
|
||||
т.е., при каждой нормальной загрузке\footnote{Обратите внимание, что режим
|
||||
графической загрузки в systemd (+graphical.target+, аналог runlevel 5
|
||||
в SysV) является надстройкой над режимом многопользовательской консольной
|
||||
загрузки (+multi-user.target+, аналог runlevel 3 в SysV). Таким
|
||||
образом, все службы, запускаемые в режиме +multi-user.target+, будут
|
||||
также запускаться и в режиме +graphical.target+.} (к <<ненормальным>>
|
||||
можно отнести, например, загрузки в режиме +emergency.target+, который
|
||||
является аналогом первого уровня исполнения в классической SysV).
|
||||
т.е., при каждой нормальной\footnote{Прим. перев.: К <<ненормальным>> загрузкам
|
||||
можно отнести, например, загрузки в режимах +emergency.target+ или
|
||||
+rescue.target+ (является аналогом первого уровня исполнения в классической
|
||||
SysV).} загрузке\footnote{Обратите внимание, что режим графической загрузки в
|
||||
systemd (+graphical.target+, аналог runlevel 5 в SysV) является надстройкой над
|
||||
режимом многопользовательской консольной загрузки (+multi-user.target+, аналог
|
||||
runlevel 3 в SysV). Таким образом, все службы, запускаемые в режиме
|
||||
+multi-user.target+, будут также запускаться и в режиме +graphical.target+.}.
|
||||
|
||||
Вот и все. Мы получили минимальный рабочий service-файл systemd. Чтобы
|
||||
проверить его работоспособность, скопируем его в
|
||||
@@ -861,7 +872,7 @@ service-файлы. Но, к счастью, <<проблемных>> скрип
|
||||
Впрочем, этот метод не~вполне корректен, так как он действует не~только на
|
||||
самого демона, но и на другие процессы с тем же именем. Иногда подобное
|
||||
поведение может привести к неприятным последствиям. Более правильным будет
|
||||
использование pid-файла: +kill $(cat /var/run/syslogd.pid)$+. Вот, вроде
|
||||
использование pid-файла: \verb+kill $(cat /var/run/syslogd.pid)+. Вот, вроде
|
||||
бы, и все, что вам нужно\ldots{} Или мы упускаем еще что-то?
|
||||
|
||||
Действительно, мы забываем одну простую вещь: существуют службы, такие, как
|
||||
@@ -899,15 +910,17 @@ Apache, crond, atd, которые по роду служебной деятел
|
||||
как бы она ни пыталась сбежать из-под нашего контроля при помощи двойного
|
||||
форка или
|
||||
\href{http://ru.wikipedia.org/wiki/Fork-%D0%B1%D0%BE%D0%BC%D0%B1%D0%B0}{форк-бомбардировки}%
|
||||
\footnote{Прим. перев.: стоит особо отметить, что использование контрольных
|
||||
\footnote{Прим. перев.: Стоит особо отметить, что использование контрольных
|
||||
групп не~только упрощает процесс уничтожения форк-бомб, но и значительно
|
||||
уменьшает ущерб от работающей форк-бомбы. Так как systemd автоматически
|
||||
помещает каждую службу и каждый пользовательский сеанс в свою контрольную
|
||||
группу по ресурсу процессорного времени, запуск форк-бомбы одним
|
||||
пользователем или службой не~создаст значительных проблем с отзывчивостью
|
||||
системы у других пользователей и служб. Таким образом, в качестве основной
|
||||
угрозы форк-бомбардировки остаются лишь возможности исчерпания памяти и
|
||||
идентификаторов процессов (PID).}.
|
||||
уменьшает ущерб от работающей форк-бомбы. Так как systemd автоматически помещает
|
||||
каждую службу и каждый пользовательский сеанс в свою контрольную группу по
|
||||
ресурсу процессорного времени, запуск форк-бомбы одним пользователем или службой
|
||||
не~создаст значительных проблем с отзывчивостью системы у других пользователей и
|
||||
служб. Таким образом, в качестве основной угрозы форк-бомбардировки остаются
|
||||
лишь возможности исчерпания памяти и идентификаторов процессов (PID). Впрочем, и
|
||||
их тоже можно легко устранить: достаточно задать соответствующие лимиты в
|
||||
конфигурационном файле службы (см.
|
||||
\href{http://www.0pointer.de/public/systemd-man/systemd.exec.html}{systemd.exec(5)}).}.
|
||||
|
||||
В некоторый случах возникает необходимость отправить сигнал именно основному
|
||||
процессу службы. Например, используя +SIGHUP+, мы можем заставить демона
|
||||
@@ -1042,7 +1055,13 @@ systemd и на этот случай есть простое решение, и
|
||||
\end{Verbatim}
|
||||
|
||||
Итак, блокировка сводится к созданию символьной ссылки
|
||||
с именем соответствующей службы, указывающей на +/dev/null+.
|
||||
с именем соответствующей службы, указывающей на
|
||||
+/dev/null+\footnote{Прим. перев.: Впоследствии в программу
|
||||
+systemctl+ была добавлена поддержка команд +mask+ и +unmask+,
|
||||
упрощающих процесс блокирования и разблокирования юнитов.
|
||||
Например, для блокирования службы +ntpd.service+ теперь
|
||||
достаточно команды +systemctl mask ntpd.service+. Фактически
|
||||
она сделает то же самое, что и приведенная выше команда +ln+.}.
|
||||
После такой операции служба не~может быть запущена ни~вручную,
|
||||
ни~автоматически. Символьная ссылка создается в каталоге
|
||||
+/etc/systemd/system/+, а ее имя должно соответствовать имени
|
||||
@@ -1071,7 +1090,7 @@ systemd и на этот случай есть простое решение, и
|
||||
файлом из пакета).
|
||||
|
||||
Стоит отметить, что блокировка службы, как и ее отключение,
|
||||
является перманентной мерой\footnote{Прим. перев.: подробно
|
||||
является перманентной мерой\footnote{Прим. перев.: Подробно
|
||||
описав принцип работы блокировки службы (юнита), автор забывает
|
||||
привести практические примеры ситуаций, когда эта возможность
|
||||
оказывается полезной.
|
||||
@@ -1084,21 +1103,14 @@ systemd и на этот случай есть простое решение, и
|
||||
ошибочно указано +systemctl restart+ (+service restart+), что
|
||||
является прямым указанием на запуск службы, если она еще
|
||||
не~запущена. Вследствие таких ошибок, отключенная служба может
|
||||
<<ожить>> в самый неподходящий момент.
|
||||
|
||||
Другой пример~---
|
||||
ограничение возможностей непривилегированного пользователя при
|
||||
управлении системой. Даже если такому пользователю делегировано
|
||||
(через механизмы sudo или PolicyKit) право на использование
|
||||
+systemctl+, это еще не~означает, что он сможет запустить
|
||||
заблокированную службу~--- права на выполнение +rm+ (удаление
|
||||
блокирующей символьной ссылки) выдаются отдельно.}.
|
||||
<<ожить>> в самый неподходящий момент.}.
|
||||
\end{itemize}
|
||||
|
||||
После прочтения изложенного выше, у читателя может возникнуть вопрос: как
|
||||
отменить произведенные изменения? Что ж, ничего сложного тут нет:
|
||||
+systemctl start+ отменяет действия +systemctl stop+, +systemctl enable+
|
||||
отменяет действие +systemctl disable+, а +rm+ отменяет действие +ln+.
|
||||
отменяет действие +systemctl disable+, а +rm+ отменяет действие
|
||||
+ln+.
|
||||
|
||||
\section{Смена корня}
|
||||
|
||||
@@ -1138,8 +1150,8 @@ systemd и на этот случай есть простое решение, и
|
||||
init, многие параметры среды выполнения (в частности, лимиты на системные
|
||||
ресурсы, переменные окружения, и т.п.) наследуются от оболочки, из которой был
|
||||
запущен init-скрипт. При использовании systemd ситуация меняется радикально:
|
||||
пользователь просто уведомляет init-демона о необходимости запустить ту или иную
|
||||
службу, и тот запускает демона в чистом, созданном <<с нуля>> и тщательно
|
||||
пользователь просто уведомляет процесс init о необходимости запустить ту или
|
||||
иную службу, и тот запускает демона в чистом, созданном <<с нуля>> и тщательно
|
||||
настроенном окружении, параметры которого никак не~зависят от настроек среды, из
|
||||
которой была отдана команда. Такой подход полностью отменяет традиционный метод
|
||||
запуска демонов в chroot-окружениях: теперь демон порождается процессом init
|
||||
@@ -1167,19 +1179,19 @@ chroot в самой программе. Прежде всего, коррект
|
||||
chroot-окружения требует глубокого понимания принципов работы программы.
|
||||
Например, нужно точно знать, какие каталоги нужно bind-монтировать из основной
|
||||
системы, чтобы обеспечить все необходимые для работы программы каналы связи. С
|
||||
учетом вышесказанного, эффективная chroot-защита обеспечивается в том случае,
|
||||
когда она реализована в коде самого демона. Именно разработчик лучше других
|
||||
знает (\emph{обязан} знать), как правильно сконфигурировать chroot-окружение, и
|
||||
какой минимальный набор файлов, каталогов и файловых систем необходим внутри
|
||||
него для нормальной работы демона. Уже сейчас существуют демоны, имеющие
|
||||
встроенную поддержку chroot. К сожалению, в системе Fedora, установленной с
|
||||
параметрами по умолчанию, таких демонов всего два:
|
||||
учетом вышесказанного, эффективная chroot-защита обеспечивается только в том
|
||||
случае, когда она реализована в коде самого демона. Именно разработчик лучше
|
||||
других знает (\emph{обязан} знать), как правильно сконфигурировать
|
||||
chroot-окружение, и какой минимальный набор файлов, каталогов и файловых систем
|
||||
необходим внутри него для нормальной работы демона. Уже сейчас существуют
|
||||
демоны, имеющие встроенную поддержку chroot. К сожалению, в системе Fedora,
|
||||
установленной с параметрами по умолчанию, таких демонов всего два:
|
||||
\href{http://avahi.org/}{Avahi} и RealtimeKit. Оба они написаны одним очень
|
||||
хитрым человеком ;-) (Вы можете собственноручно убедиться в этом, выполнив
|
||||
команду +ls -l /proc/*/root+.)
|
||||
|
||||
Возвращаясь к теме нашего обсуждения: разумеется, systemd позволяет помещать
|
||||
выбранных демонов в chroot, и управлять ими точно так же, как и другими.
|
||||
выбранных демонов в chroot, и управлять ими точно так же, как и остальными.
|
||||
Достаточно лишь указать параметр +RootDirectory=+ в соответствующем
|
||||
service-файле. Например:
|
||||
\begin{Verbatim}
|
||||
@@ -1246,7 +1258,7 @@ InaccessibleDirectories=/home
|
||||
руководства}.)
|
||||
|
||||
Фактически, FSNS по множеству параметров превосходят +chroot()+. Скорее всего,
|
||||
Avahi и ReltimeKit в ближайшем будущем перейдут с +chroot()+ к использованию
|
||||
Avahi и RealtimeKit в ближайшем будущем перейдут от +chroot()+ к использованию
|
||||
FSNS.
|
||||
|
||||
Итак, мы рассмотрели вопросы использования chroot для обеспечения безопасности.
|
||||
@@ -1261,7 +1273,7 @@ chroot-окружения, по сути, весьма примитивны: о
|
||||
отличается лишь содержимое файловой системы, все остальное у них общее.
|
||||
Например, если вы обновляете дистрибутив, установленный в chroot-окружении, и
|
||||
пост-установочный скрипт пакета отправляет +SIGTERM+ процессу init для его
|
||||
перезапуска\footnote{Прим. перев.: во избежание путаницы отметим, что перезапуск
|
||||
перезапуска\footnote{Прим. перев.: Во избежание путаницы отметим, что перезапуск
|
||||
процесса init (PID~1) <<на лету>> при получении +SIGTERM+ поддерживается только
|
||||
в systemd, в классическом SysV init такой возможности нет.}, на него среагирует
|
||||
именно хост-система! Кроме того, хост и chroot'нутая система будут иметь общую
|
||||
@@ -1282,7 +1294,7 @@ systemd имеет целый ряд возможностей, полезных
|
||||
Таким образом, пакетные скрипты смогут включить/отключить запуск <<своих>> служб
|
||||
при загрузке (или в других ситуациях), однако команды наподобие
|
||||
+systemctl restart+ (обычно выполняется при обновлении пакета) не~дадут никакого
|
||||
эффекта внутри chroot-окружения\footnote{Прим. перев.: автор забывает отметить
|
||||
эффекта внутри chroot-окружения\footnote{Прим. перев.: Автор забывает отметить
|
||||
не~вполне очевидный момент: такое поведение +systemctl+ проявляется только в
|
||||
<<мертвых>> окружениях, т.е. в тех, где не~запущен процесс init, и
|
||||
соответственно отсутствуют управляющие сокеты в +/run/systemd+. Такая ситуация
|
||||
@@ -1298,7 +1310,7 @@ debootstrap/febootstrap. В этом случае возможности +system
|
||||
+chroot(1)+~--- она не~только подменяет корневой каталог, но и создает отдельные
|
||||
пространства имен для дерева файловых систем (FSNS) и для идентификаторов
|
||||
процессов (PID NS), предоставляя легковесную реализацию системного
|
||||
контейнера\footnote{Прим. перев.: используемые в +systemd-nspawn+ механизмы
|
||||
контейнера\footnote{Прим. перев.: Используемые в +systemd-nspawn+ механизмы
|
||||
ядра Linux, такие, как FS NS и PID NS, также лежат в основе
|
||||
\href{http://lxc.sourceforge.net/}{LXC}, системы контейнерной изоляции для
|
||||
Linux, которая позиционируется как современная альтернатива классическому
|
||||
@@ -1317,7 +1329,7 @@ Linux, которая позиционируется как современна
|
||||
ему в штатном режиме. Также, в отличие от +chroot(1)+, внутри окружения
|
||||
будут автоматически смонтированы +/proc+ и +/sys+.
|
||||
|
||||
Следующий пример иллюстрирует возможность запустить Debian в на Fedora-хосте
|
||||
Следующий пример иллюстрирует возможность запустить Debian на Fedora-хосте
|
||||
всего тремя командами:
|
||||
\begin{Verbatim}
|
||||
# yum install debootstrap
|
||||
@@ -1336,15 +1348,18 @@ Linux, которая позиционируется как современна
|
||||
После быстрой загрузки вы получите приглашение оболочки, запущенной внутри
|
||||
полноценной ОС, функционирующей в контейнере. Изнутри контейнера невозможно
|
||||
увидеть процессы, которые находятся вне его. Контейнер сможет пользоваться сетью
|
||||
хоста, однако не~имеет возможности изменить ее настройки (это может привести к
|
||||
серии ошибок в процессе загрузки гостевой ОС, но ни~одна из этих ошибок
|
||||
не~должна быть критической). Контейнер получает доступ к +/sys+ и +/proc/sys+,
|
||||
однако, во избежание вмешательства контейнера в конфигурацию ядра и аппаратного
|
||||
обеспечения хоста, эти каталоги будут смонтированы только для чтения. Обратите
|
||||
внимание, что эта защита блокирует лишь \emph{случайные}, \emph{непредвиденные}
|
||||
попытки изменения параметров. При необходимости, процесс внутри контейнера,
|
||||
обладающий достаточными полномочиями, сможет перемонтировать эти файловые
|
||||
системы в режиме чтения-записи.
|
||||
хоста\footnote{Прим. перев.: Впоследствии в +systemd-nspawn+ была добавлена
|
||||
опция +--private-network+, позволяющая изолировать систему внутри контейнера от
|
||||
сети хоста: такая система будет видеть только свой собственный интерфейс
|
||||
обратной петли.}, однако не~имеет возможности изменить ее настройки (это может
|
||||
привести к серии ошибок в процессе загрузки гостевой ОС, но ни~одна из этих
|
||||
ошибок не~должна быть критической). Контейнер получает доступ к +/sys+ и
|
||||
+/proc/sys+, однако, во избежание вмешательства контейнера в конфигурацию ядра и
|
||||
аппаратного обеспечения хоста, эти каталоги будут смонтированы только для
|
||||
чтения. Обратите внимание, что эта защита блокирует лишь \emph{случайные},
|
||||
\emph{непредвиденные} попытки изменения параметров. При необходимости, процесс
|
||||
внутри контейнера, обладающий достаточными полномочиями, сможет перемонтировать
|
||||
эти файловые системы в режиме чтения-записи.
|
||||
|
||||
Итак, что же такого хорошего в +systemd-nspawn+?
|
||||
\begin{enumerate}
|
||||
@@ -1494,41 +1509,40 @@ $ systemd-analyze blame
|
||||
|
||||
Но почему эта служба вообще присутствует в нашем процессе загрузки? На самом
|
||||
деле, мы можем прекрасно обойтись и без нее. Она нужна лишь как часть
|
||||
используемой в Fedora схемы инициализации устройств хранения, а именно,
|
||||
набора скриптов, выполняющих настройку LVM, RAID и multipath-устройств. На
|
||||
сегодняшний день, реализация этих систем не~поддерживает собственного механизма
|
||||
поиска и опроса устройств, и поэтому их запуск должен производиться только после
|
||||
того, как эта работа уже проделана~--- тогда они могут просто последовательно
|
||||
используемой в Fedora схемы инициализации устройств хранения, а именно, набора
|
||||
скриптов, выполняющих настройку LVM, RAID и multipath-устройств. На сегодняшний
|
||||
день, реализация этих систем не~поддерживает собственного механизма поиска и
|
||||
опроса устройств, и поэтому их запуск должен производиться только после того,
|
||||
как эта работа уже проделана~--- тогда они могут просто последовательно
|
||||
перебирать все диски. Однако, такой подход противоречит одному из
|
||||
фундаментальных требований к современным компьютерам:
|
||||
возможности подключать и отключать оборудование в любой момент, как при
|
||||
выключенном компьютере, так и при включенном. Для некоторых
|
||||
технологий невозможно точно определить момент завершения формирования списка устройств
|
||||
(например, это характерно для USB и iSCSI), и поэтому процесс опроса
|
||||
таких устройств обязательно должен включать некоторую фиксированную задержку,
|
||||
гарантирующую, что все подключенные устройства успеют отозваться. С точки зрения
|
||||
скорости загрузки это, безусловно, негативный эффект: соответствующие скрипты
|
||||
заставляют нас ожидать завершения опроса устройств, хотя большинство этих
|
||||
устройств не~являются необходимыми на данной стадии загрузки. В частности, в
|
||||
рассматриваемой нами системе LVM, RAID и multipath вообще
|
||||
не~используются!\footnote{Наиболее правильным решением в данном случае будет
|
||||
ожидание событий подключения устройств (реализованное через libudev или
|
||||
аналогичную технологию) с соответствующей обработкой каждого такого события~---
|
||||
подобный подход позволит нам продолжить загрузку, как только будут готовы все
|
||||
устройства, необходимые для ее продолжения. Для того, чтобы загрузка была
|
||||
быстрой, мы должны ожидать завершения инициализации только тех устройств, которые
|
||||
\emph{действительно} необходимы для ее продолжения на данной стадии. Ожидать
|
||||
остальные устройства в данном случае смысла нет. Кроме того, стоит отметить, что
|
||||
в числе программ, не~приспособленных для работы с динамически меняющимся
|
||||
оборудованием и построенных в предположении о неизменности списка устройств,
|
||||
кроме служб хранения, есть и другие подсистемы. Например, в нашем случае работа
|
||||
initrd занимает так много времени главным образом из-за того, что для запуска
|
||||
Plymouth необходимо дождаться завершения инициализации всех устройств
|
||||
видеовывода. По неизвестной причине (во всяком случае, неизвестной для меня)
|
||||
подгрузка модулей для моих видеокарт Intel занимает довольно продолжительное
|
||||
время, что приводит к беспричинной задержке процесса загрузки. (Нет, я протестую
|
||||
не~против опроса устройств, но против необходимости ожидать его завершения, чтобы
|
||||
продолжить загрузку.)}
|
||||
фундаментальных требований к современным компьютерам: возможности подключать и
|
||||
отключать оборудование в любой момент, как при выключенном компьютере, так и при
|
||||
включенном. Для некоторых технологий невозможно точно определить момент
|
||||
завершения формирования списка устройств (например, это характерно для USB и
|
||||
iSCSI), и поэтому процесс опроса таких устройств обязательно должен включать
|
||||
некоторую фиксированную задержку, гарантирующую, что все подключенные устройства
|
||||
успеют отозваться. С точки зрения скорости загрузки это, безусловно, негативный
|
||||
эффект: соответствующие скрипты заставляют нас ожидать завершения опроса
|
||||
устройств, хотя большинство этих устройств не~являются необходимыми на данной
|
||||
стадии загрузки. В частности, в рассматриваемой нами системе LVM, RAID и
|
||||
multipath вообще не~используются!\footnote{Наиболее правильным решением в данном
|
||||
случае будет ожидание событий подключения устройств (реализованное через libudev
|
||||
или аналогичную технологию) с соответствующей обработкой каждого такого
|
||||
события~--- подобный подход позволит нам продолжить загрузку, как только будут
|
||||
готовы все устройства, необходимые для ее продолжения. Для того, чтобы загрузка
|
||||
была быстрой, мы должны ожидать завершения инициализации только тех устройств,
|
||||
которые \emph{действительно} необходимы для ее продолжения на данной стадии.
|
||||
Ожидать остальные устройства в данном случае смысла нет. Кроме того, стоит
|
||||
отметить, что в числе программ, не~приспособленных для работы с динамически
|
||||
меняющимся оборудованием и построенных в предположении о неизменности списка
|
||||
устройств, кроме служб хранения, есть и другие подсистемы. Например, в нашем
|
||||
случае работа initrd занимает так много времени главным образом из-за того, что
|
||||
для запуска Plymouth необходимо дождаться завершения инициализации всех
|
||||
устройств видеовывода. По неизвестной причине (во всяком случае, неизвестной для
|
||||
меня) подгрузка модулей для моих видеокарт Intel занимает довольно
|
||||
продолжительное время, что приводит к беспричинной задержке процесса загрузки.
|
||||
(Нет, я возражаю не~против опроса устройств как такового, но против
|
||||
необходимости ожидать его завершения, чтобы продолжить загрузку.)}
|
||||
|
||||
С учетом вышесказанного, мы можем спокойно исключить +udev-settle.service+ из
|
||||
нашего процесса загрузки, так как мы не~используем ни~LVM, ни~RAID,
|
||||
@@ -1543,8 +1557,8 @@ Plymouth необходимо дождаться завершения иници
|
||||
После перезагрузки мы видим, что загрузка стала на одну секунду быстрее. Но
|
||||
почему выигрыш оказался таким маленьким? Из-за второго осквернителя нашей
|
||||
загрузки~--- cryptsetup. На рассматриваемой нами системе зашифрован раздел
|
||||
+/home+. Специально для тестирования я записал пароль в файл, чтобы исключить
|
||||
влияние скорости ручного набора пароля. К сожалению, для подключения
|
||||
+/home+. Специально для нашего тестирования я записал пароль в файл, чтобы
|
||||
исключить влияние скорости ручного набора пароля. К сожалению, для подключения
|
||||
шифрованного раздела cryptsetup требует более пяти секунд. Будем ленивы, и
|
||||
вместо того, чтобы исправлять ошибку в cryptsetup\footnote{На самом деле, я
|
||||
действительно пытался исправить эту ошибку. Задержки в cryptsetup, по
|
||||
@@ -1561,7 +1575,7 @@ cryptsetup, что снижение этого времени с 1 секунд
|
||||
может продолжить загрузку и приступить к запуску служб. Но первое обращение
|
||||
к +/home+ (в отличие, например, от +/var+), происходит на поздней стадии
|
||||
процесса загрузки (когда пользователь входит в систему). Следовательно, нам
|
||||
нужно сделать так, чтобы этот каталога автоматически монтировался при загрузке,
|
||||
нужно сделать так, чтобы этот каталог автоматически монтировался при загрузке,
|
||||
но процесс загрузки не~ожидал завершения работы +cryptsetup+, +fsck+ и +mount+
|
||||
для этого раздела. Как же сделать точку монтирования доступной, не~ожидая, пока
|
||||
завершится процесс монтирования? Этого можно достичь, воспользовавшись
|
||||
@@ -1731,7 +1745,7 @@ LVM, RAID и multipath). Если они вам не~нужны, вы легко
|
||||
конфигурация общесистемной локали.
|
||||
\item
|
||||
\hreftt{http://0pointer.de/public/systemd-man/modules-load.d.html}{/etc/modules-load.d/*.conf}:
|
||||
каталог\footnote{Прим. перев.: для описания этого и трех
|
||||
каталог\footnote{Прим. перев.: Для описания этого и трех
|
||||
последующих каталогов автор пользуется термином <<drop-in
|
||||
directory>>. Этот термин означает каталог, в который можно
|
||||
поместить множество независимых файлов настроек, и при чтении
|
||||
@@ -1785,8 +1799,8 @@ LVM, RAID и multipath). Если они вам не~нужны, вы легко
|
||||
наличия уникального и постоянного идентификатора компьютера.
|
||||
\item
|
||||
\hreftt{http://0pointer.de/public/systemd-man/machine-info.html}{/etc/machine-info}:
|
||||
новый конфигурационный файл, хранящий информации о полном имени
|
||||
(описательном) хоста (например, <<Компьютер Леннарта>>) и
|
||||
новый конфигурационный файл, хранящий информации о полном
|
||||
(описательном) имени хоста (например, <<Компьютер Леннарта>>) и
|
||||
значке, которым он будет обозначаться в графических оболочках,
|
||||
работающих с сетью (раньше этот значок мог определяться,
|
||||
например, файлом +/etc/favicon.png+). Данный конфигурационный
|
||||
@@ -1803,19 +1817,18 @@ LVM, RAID и multipath). Если они вам не~нужны, вы легко
|
||||
момент эти файлы полностью поддерживаются только дистрибутивами, основанными на
|
||||
systemd, но уже сейчас в их число входят практически все ключевые дистрибутивы,
|
||||
\href{http://www.ubuntu.com/}{за исключением
|
||||
одного}\footnote{Прим. перев.: в конце 2010~года энтузиаст Andrew Edmunds
|
||||
одного}\footnote{Прим. перев.: В конце 2010~года энтузиаст Andrew Edmunds
|
||||
\href{http://cgit.freedesktop.org/systemd/commit/?id=858dae181bb5461201ac1c04732d3ef4c67a0256}{добавил}
|
||||
в systemd базовую поддержку Ubuntu и
|
||||
\href{https://wiki.ubuntu.com/systemd}{подготовил} соответствующие пакеты,
|
||||
однако его инициатива не~встретила поддержки среди менеджеров Canonical. На
|
||||
момент написания этих строк (май 2011 года) проект остается заброшенным уже пять
|
||||
месяцев (с середины декабря 2010~г.).}. В этом есть что-то от <<пролемы курицы и
|
||||
яйца>>: стандарт становится начинает работать как стандарт только тогда, когда
|
||||
ему начинают следовать. В будущем мы намерены аккуратно форсировать процесс
|
||||
перехода на новые конфигурационные файлы: поддержка старых файлов будет удалена
|
||||
из systemd. Разумеется, этот процесс будет идти медленно, шаг за шагом. Но
|
||||
конечной его целью является переход всех дистрибутивов на единый набор базовых
|
||||
конфигурационных файлов.
|
||||
момент написания этих строк проект остается заброшенным с декабря 2010~г.}. В
|
||||
этом есть что-то от <<проблемы курицы и яйца>>: стандарт становится настоящим
|
||||
стандартом только тогда, когда ему начинают следовать. В будущем мы намерены
|
||||
аккуратно форсировать процесс перехода на новые конфигурационные файлы:
|
||||
поддержка старых файлов будет удалена из systemd. Разумеется, этот процесс будет
|
||||
идти медленно, шаг за шагом. Но конечной его целью является переход всех
|
||||
дистрибутивов на единый набор базовых конфигурационных файлов.
|
||||
|
||||
Многие из этих файлов используются не~только программами для настройки системы,
|
||||
но и апстримными проектами. Например, мы предлагаем проектам Mono, Java, WINE и
|
||||
@@ -1841,7 +1854,7 @@ shell-скриптов, выполняющих тривиальные задач
|
||||
выбрали то, что должно устроить большинство людей. Форматы конфигурационных
|
||||
файлов максимально просты, и их можно легко читать и записывать даже из
|
||||
shell-скриптов. Да, +/etc/bikeshed.conf+ могло бы быть неплохим именем
|
||||
для файла конфигурации!\footnote{Прим. перев.: здесь автор намекает на
|
||||
для файла конфигурации!\footnote{Прим. перев.: Здесь автор намекает на
|
||||
\href{http://en.wikipedia.org/wiki/Parkinson's_Law_of_Triviality}{Паркинсоновский
|
||||
Закон Тривиальности}, который гласит, что самые жаркие споры возникают вокруг
|
||||
наиболее простых вопросов. В частности, в качестве примера Паркинсон приводит
|
||||
@@ -1881,7 +1894,7 @@ shed)~--- если первое из этих решений принимает
|
||||
зоопарк их различных реализаций в разных дистрибутивах.
|
||||
|
||||
Назначение этих каталогов определено весьма расплывчато. Абсолютное большинство
|
||||
находящихся в них файлов являются включаемыми\footnote{Прим. перев.: здесь автор
|
||||
находящихся в них файлов являются включаемыми\footnote{Прим. перев.: Здесь автор
|
||||
использует термин sourcable, происходящий от bash'овской директивы +source+,
|
||||
обеспечивающей включение в скрипт кода из внешнего файла. В классическом POSIX
|
||||
shell это соответствует оператору-точке <<+.+>>. В отличие от прямого запуска
|
||||
@@ -1921,7 +1934,7 @@ init-скрипта, или даже сами не~являющиеся скри
|
||||
\item Режим остановки для демона.
|
||||
\item Общесистемные настройки, например, системная локаль, часовой пояс,
|
||||
параметры клавиатуры для консоли.
|
||||
\item Избыточные информация о системных настройках, например, указание,
|
||||
\item Избыточная информация о системных настройках, например, указание,
|
||||
установлены ли аппаратные часы по Гринвичу или по местному
|
||||
времени.
|
||||
\item Списки правил брандмауэра, не~являются скриптами (!).
|
||||
@@ -2050,11 +2063,13 @@ systemd?
|
||||
Одно из немногих исключений из этого правила~--- к сожалению, в
|
||||
настоящее время не~поддерживается автоматическая загрузка
|
||||
модулей на основании информации о возможностях процессора,
|
||||
однако это будет исправлено в ближайшем будущем. В случае, если
|
||||
нужный вам модуль ядра все же не~может быть подгружен
|
||||
автоматически, все равно существует гораздо более удобные методы
|
||||
указать его принудительную подгрузку~--- например, создав
|
||||
соответствующий файл в каталоге
|
||||
однако это будет исправлено в ближайшем будущем\footnote{Прим.
|
||||
перев.: Патчи от разработчиков systemd уже приняты в ядро, и
|
||||
соответствующая функция поддерживается Linux, начиная с версии
|
||||
3.3.}. В случае, если нужный вам модуль ядра все же не~может
|
||||
быть подгружен автоматически, все равно существует гораздо более
|
||||
удобные методы указать его принудительную подгрузку~---
|
||||
например, путем создания соответствующего файла в каталоге
|
||||
\hreftt{http://0pointer.de/public/systemd-man/modules-load.d.html}{/etc/modules-load.d/}
|
||||
(стандартный метод настройки принудительной подгрузки модулей).
|
||||
\item И наконец, хотелось бы отметить, что каталог +/etc+ определен как
|
||||
@@ -2085,16 +2100,16 @@ systemd?
|
||||
\item Найдите для них более подходящее место. Например, в случае с
|
||||
некоторыми общесистемными настройками (такими, как локаль или
|
||||
часовой пояс), мы надеемся аккуратно подтолкнуть дистрибутивы в
|
||||
правильно направлении (см. предыдущий эпизод).
|
||||
правильно направлении (см. предыдущую главу).
|
||||
\item Добавьте их поддержку в штатную систему настройки демона через
|
||||
собственные файлы конфигурации. К счастью, большинство служб,
|
||||
работающих в Linux, являются свободным программным обеспечением,
|
||||
так что сделать это довольно просто.
|
||||
\end{itemize}
|
||||
|
||||
Существует лишь одна причина поддерживать эти файлы еще некоторое
|
||||
Существует лишь одна причина поддерживать файлы +/etc/sysconfig+ еще некоторое
|
||||
время: необходимо обеспечить совместимость при обновлении. Тем не~менее, как
|
||||
минимум в новых пакетах, от этих файлов лучше отказаться.
|
||||
минимум в новых пакетах, от таких файлов лучше отказаться.
|
||||
|
||||
Если требование совместимости критично, вы можете задействовать эти
|
||||
конфигурационные файлы даже в том случае, если настраиваете службы через
|
||||
@@ -2111,6 +2126,7 @@ systemd?
|
||||
вместо бинарника демона.
|
||||
|
||||
\section{Экземпляры служб}
|
||||
\label{sec:instances}
|
||||
|
||||
Большинство служб в Linux/Unix являются одиночными (singleton): в каждый момент
|
||||
времени на данном хосте работает только один экземпляр службы. В качестве
|
||||
@@ -2126,22 +2142,29 @@ systemd?
|
||||
нескольких экземплярах (по крайней мере, в мире systemd)~--- \emph{fsck},
|
||||
программа проверки файловой системы, которая запускается по одному экземпляру
|
||||
на каждое блочное устройство, требующее такой проверки. И наконец, стоит
|
||||
упомянуть службы с активацией в стиле inetd~--- через сокет, по одному
|
||||
экземпляру на каждое соединение. В этой статье я попытаюсь рассказать, как в
|
||||
systemd реализовано управление <<многоэкземплярными>> службами, и какие выгоды
|
||||
системный администратор может извлечь из этой возможности.
|
||||
упомянуть службы с активацией в стиле inetd~--- при обращении через сокет, по
|
||||
одному экземпляру на каждое соединение. В этой статье я попытаюсь рассказать,
|
||||
как в systemd реализовано управление <<многоэкземплярными>> службами, и какие
|
||||
выгоды системный администратор может извлечь из этой возможности.
|
||||
|
||||
Если вы читали предыдущие статьи из этого цикла, вы, скорее всего, уже знаете,
|
||||
что службы systemd именуются по схеме \emph{foobar}+.service+, где
|
||||
\emph{foobar}~--- строка, идентифицирующая службу (проще говоря, ее имя), а
|
||||
+.service+~--- суффикс, присутствующий в именах всех файлов конфигурации служб.
|
||||
Сами эти файлы могут находиться в каталогах +/etc/systemd/systemd+ и
|
||||
+/lib/systemd/system+ (а также, возможно, и в других). Для служб, работающих в
|
||||
нескольких экземплярах, эта схема становится немного сложнее:
|
||||
\emph{foobar}+@+\emph{quux}+.service+, где \emph{foobar}~--- имя службы,
|
||||
общее для всех экземпляров, а \emph{quux}~--- идентификатор конкретного
|
||||
экземпляра. Например, +serial-gett@ttyS2.service+~--- это служба getty для
|
||||
COM-порта, запущенная на +ttyS2+.
|
||||
+/lib/systemd/system+ (а также, возможно, и в других\footnote{Прим. перев.:
|
||||
Перечень каталогов, в которых выполняется поиск общесистемных юнит-файлов,
|
||||
приведен на странице руководства
|
||||
\href{http://www.0pointer.de/public/systemd-man/systemd.html}{systemd(1)}
|
||||
(раздел <<System unit directories>>). Указанные выше каталоги
|
||||
+/etc/systemd/systemd+ и +/lib/systemd/system+ соответствуют значениям по
|
||||
умолчанию для упомянутых там переменных pkg-config +systemdsystemconfdir+ и
|
||||
+systemdsystemunitdir+ соответственно.}). Для служб, работающих в нескольких
|
||||
экземплярах, эта схема становится немного сложнее:
|
||||
\emph{foobar}+@+\emph{quux}+.service+, где \emph{foobar}~--- имя службы, общее
|
||||
для всех экземпляров, а \emph{quux}~--- идентификатор конкретного экземпляра.
|
||||
Например, +serial-gett@ttyS2.service+~--- это служба getty для COM-порта,
|
||||
запущенная на +ttyS2+.
|
||||
|
||||
При необходимости, экземпляры служб можно легко создать динамически. Скажем, вы
|
||||
можете, безо всяких дополнительных настроек, запустить новый экземпляр getty на
|
||||
@@ -2179,6 +2202,397 @@ RestartSec=0
|
||||
\href{http://cgit.freedesktop.org/systemd/plain/units/serial-getty@.service.m4}{полную
|
||||
версию}.)
|
||||
|
||||
Этот файл похож на обычный файл конфигурации юнита, с единственным отличием: в
|
||||
нем используются спецификаторы \%I и \%i. В момент загрузки юнита, systemd
|
||||
заменяет эти спецификаторы на идентификатор экземпляра службы. В нашем случае,
|
||||
при обращении к экземпляру +serial-getty@ttyUSB0.service+, они заменяются на
|
||||
<<+ttyUSB0+>>. Результат такой замены можно проверить, например, запросив
|
||||
состояние для этой службы:
|
||||
\begin{Verbatim}[commandchars=\\\{\}]
|
||||
$ 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
|
||||
\mytextSFii{} 5443 /sbin/agetty -s ttyUSB0 115200,38400,9600
|
||||
\end{Verbatim}
|
||||
Собственно, это и есть ключевая идея организации экземпляров служб. Как видите,
|
||||
systemd предоставляет простой в использовании механизм шаблонов, позволяющих
|
||||
динамически создавать экземпляры служб. Добавим несколько дополнительных
|
||||
замечаний, позволяющих эффективно использовать этот механизм:
|
||||
|
||||
Вы можете создавать дополнительные экземпляры таких служб, просто добавляя
|
||||
символьные ссылки в каталоги +*.wants/+. Например, чтобы обеспечить запуск getty
|
||||
на ttyUSB0 при каждой загрузке, достаточно создать такую ссылку:
|
||||
\begin{Verbatim}[commandchars=\\\{\}]
|
||||
# ln -s /lib/systemd/system/serial-getty@.service \tbs{}
|
||||
/etc/systemd/system/getty.target.wants/serial-getty@ttyUSB0.service
|
||||
\end{Verbatim}
|
||||
При этом файл конфигурации, на который указывает ссылка (в нашем случае
|
||||
+serial-getty@.service+), будет вызван с тем именем экземпляра, которое указанно
|
||||
в названии этой ссылки (в нашем случае~--- +ttyUSB0+).
|
||||
|
||||
Вы не~сможете обратиться к юниту-шаблону без указания идентификатора экземпляра.
|
||||
В частности, команда +systemctl start serial-getty@.service+ завершится ошибкой.
|
||||
|
||||
Иногда возникает необходимость отказаться от использования общего шаблона
|
||||
для конкретного экземпляра (т.е. конфигурация данного экземпляра настолько
|
||||
сильно отличается от конфигурации остальных экземпляров данной службы, что
|
||||
механизм шаблонов оказывается неэффективен). Специально для таких случаев, в
|
||||
systemd и заложен предварительный поиск файла с именем, точно соответствующим
|
||||
указанному (прежде чем использовать общий шаблон). Таким образом, вы можете
|
||||
поместить файл с именем, точно соответствующим полному титулу экземпляра, в
|
||||
каталог +/etc/systemd/system/+~--- и содержимое этого файла, при обращении
|
||||
к выбранному экземпляру, полностью перекроет все настройки, сделанные в общем
|
||||
шаблоне.
|
||||
|
||||
В приведенном выше файле, в некоторых местах используется спецификатор +%I+, а
|
||||
в других~--- +%i+. У вас может возникнуть закономерный вопрос~--- чем они
|
||||
отличаются? +%i+ всегда точно соответствует идентификатору экземпляра, в то
|
||||
время, как +%I+ соответствует неэкранированной (unescaped) версии этого
|
||||
идентификатора. Когда идентификатор не~содержит спецсимволов (например,
|
||||
+ttyUSB0+), результат в обоих случаях одинаковый. Но имена устройств, например,
|
||||
содержат слеши (<</>>), которые не~могут присутствовать в имени юнита (и в имени
|
||||
файла на Unix). Поэтому, перед использованием такого имени в качестве
|
||||
идентификатора устройства, оно должно быть экранировано~--- <</>> заменяются на
|
||||
<<->>, а большинство других специальных символов (включая <<->>) заменяются
|
||||
последовательностями вида +\xAB+, где AB~--- ASCII-код символа, записанный в
|
||||
шестнадцатеричной системе счисления\footnote{Согласен, этот алгоритм дает на
|
||||
выходе не~очень читабельный результат. Но этим грешат практически все алгоритмы
|
||||
экранирования. В данном случае, был использован механизм экранирования из udev,
|
||||
с одним изменением. В конце концов, нам нужно было выбрать что-то. Если вы
|
||||
собираетесь комментировать наш алгоритм экранирования~--- пожалуйста, оставьте
|
||||
свой адрес, чтобы я мог приехать к вам и раскрасить ваш сарай для велосипедов в
|
||||
синий с желтыми полосами. Спасибо!}. Например, чтобы обратиться к
|
||||
последовательному 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+) и для формирования читабельных строк описания. Рассмотрим
|
||||
работу этих принципов на примере нашего юнит-файла\footnote{Прим. перев.: как
|
||||
видно из нижеприведенного примера, в командной строке +systemctl+ необходимо
|
||||
указывать экранированное имя юнита, что создает определенные трудности даже при
|
||||
наличии в оболочке <<умного>> автодополнения. Однако, на момент написания этих
|
||||
строк, в TODO проекта содержится пункт о добавлении в systemctl поддержки
|
||||
неэкранированных имен юнитов.}:
|
||||
\begin{landscape}
|
||||
\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
|
||||
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
|
||||
|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}
|
||||
Как видите, в качестве идентификатора экземпляра используется экранированная
|
||||
версия, в то время как в строке описания и в командной строке +getty+
|
||||
фигурирует неэкранированный вариант, как и предполагалось.
|
||||
|
||||
(Небольшое замечание: помимо +%i+ и +%I+, существует еще несколько
|
||||
спецификаторов, и большинство из них доступно и в обычных файлах конфигурации
|
||||
юнитов, а не~только в шаблонах. Подробности можно посмотреть на
|
||||
\href{http://0pointer.de/public/systemd-man/systemd.unit.html}{странице
|
||||
руководства}, содержащей полный перечень этих спецификаторов с краткими
|
||||
пояснениями.)
|
||||
|
||||
\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-активации.
|
||||
Заметим, что явно указывать опцию +StandardOutput=+ в данном случае
|
||||
необязательно~--- она автоматически устанавливается в то же значение, что и
|
||||
+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
|
||||
|
||||
Reference in New Issue
Block a user