Compare commits

...

5 Commits
v13.2 ... v14.2

Author SHA1 Message Date
nnz1024
20db2f8715 Version v14.2 (2013-01-24 23:03) [AUTO] 2017-08-17 23:05:40 +03:00
nnz1024
93782fa4ec Version v14.1 (2013-01-21 04:31) [AUTO] 2017-08-17 23:05:40 +03:00
nnz1024
57b51c4e30 Version v14.0 (2013-01-20 06:01) [AUTO] 2017-08-17 23:05:40 +03:00
nnz1024
97fae68e02 Version v13.4 (2013-01-20 04:30) [AUTO] 2017-08-17 23:05:40 +03:00
nnz1024
5fb4692385 Version v13.3 (2012-12-04 21:06) [AUTO] 2017-08-17 23:05:40 +03:00

799
s4a.tex
View File

@@ -1117,6 +1117,7 @@ Apache, crond, atd, которые по роду служебной деятел
+ln+.
\section{Смена корня}
\label{sec:chroots}
Практически все администраторы и разработчики рано или поздно встречаются с
\href{http://linux.die.net/man/1/chroot}{chroot-окружениями}. Системный вызов
@@ -2311,6 +2312,7 @@ serial-getty@serial-by\x2dpath-pci\x2d0000:00:1d.0\x2dusb\x2d0:1.4:1.1\x2dport0.
пояснениями.)
\section{Службы с активацией в стиле inetd}
\label{sec:inetd}
В одной из предыдущих глав (гл.~\ref{sec:convert}) я рассказывал, как можно
преобразовать SysV init-скрипт в юнит-файл systemd. В этой главе мы рассмотрим,
@@ -2612,6 +2614,7 @@ systemd. Но, конечно, будет лучше, если этим займ
приложения, или сопровождающие вашего дистрибутива.
\section{К вопросу о безопасности}
\label{sec:security}
Одно из важнейших достоинств Unix-систем~--- концепция разделения привилегий
между различными компонентами ОС. В частности, службы обычно работают от имени
@@ -3337,42 +3340,44 @@ StartLimitAction=reboot-force
для краткости изложения, при переводе используется не~вполне корректный, но
хорошо знакомый администраторам жаргонизм <<последовательная консоль>>. Также
отметим, что в данном документе термины <<консоль>> и <<терминал>> используются
как синонимы.} достаточно добавить параметр ядра \verb+console=ttyS0+, и systemd
автоматически запустит getty на этом терминале.}
как синонимы.} достаточно указать в загрузчике параметр ядра
\verb+console=ttyS0+, и systemd автоматически запустит getty на этом терминале.}
Физический последовательный порт
\href{https://ru.wikipedia.org/wiki/RS-232}{RS-232}, хотя уже и стал редкостью
на современных настольных компьютерах, тем не~менее, продолжает играть
важную роль на современных серверах и встраиваемых системах. Он предоставляет
простой и надежный доступ к управлению системой, даже когда сеть упала, а
основной интерфейс управления завис. Кроме того, эмуляция последовательной
консоли часто используется при управлении виртуальными машинами.
на современных настольных компьютерах, тем не~менее, продолжает играть важную
роль как на серверах, так и во встраиваемых системах. Он предоставляет простой и
надежный доступ к управлению системой, даже когда сеть упала, а основной
интерфейс управления завис. Кроме того, эмуляция последовательной консоли часто
используется при управлении виртуальными машинами.
Разумеется, в Linux уже давно реализована поддержка работы с последовательными
консолями, однако при разработке
\href{http://www.freedesktop.org/wiki/Software/systemd}{systemd} мы постарались
консолями но, при разработке
\href{http://www.freedesktop.org/wiki/Software/systemd}{systemd}, мы постарались
сделать работу с ними еще проще. В этой статье я хочу рассказать о том, как в
systemd реализован запуск \href{https://ru.wikipedia.org/wiki/Getty}{getty} на
терминалах различных типов.
Для начала, хотелось бы отметить следующий момент: в большинстве случаев, чтобы
Для начала, хотелось бы отметить один важный момент: в большинстве случаев, чтобы
получить приглашение к логину на последовательном терминале, вам не~нужно
совершать никаких дополнительных действий: systemd сам проверит настройки ядра,
определит их них используемую ядром консоль, и автоматически запустит на ней
getty. Таким образом, вам достаточно лишь правильно указать ядру соответствующую
консоль (например, добавив к параметрам ядра в загрузчик +console=ttyS0+).
Тем не~менее, для общего образования мы все же рассмотрим некоторые тонкости
запуска getty в systemd. Эта задача решается двумя шаблонами
Тем не~менее, для общего образования все же стоит рассмотреть некоторые
тонкости запуска getty в systemd. Эта задача решается двумя шаблонами
юнитов\footnote{Прим. перев.: Принципы работы с шаблонами и экземплярами служб
изложены в главе~\ref{sec:instances}. Для лучшего понимания нижеприведенного
материала, рекомендуется перечитать эту главу, если вы ее подзабыли.}:
материала, рекомендуется перечитать эту главу, если вы успели ее подзабыть.}:
\begin{itemize}
\item +getty@.service+ отвечает за
\href{https://ru.wikipedia.org/wiki/%D0%92%D0%B8%D1%80%D1%82%D1%83%D0%B0%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F_%D0%BA%D0%BE%D0%BD%D1%81%D0%BE%D0%BB%D1%8C}%
{виртуальные консоли} (+/dev/tty1+ и т.д.)~--- их можно увидеть
безо всякого дополнительного оборудования, просто переключившись
на них из графического сеанса.
{виртуальные консоли} (virtual terminals, VT, известные в
системе под именами +/dev/tty1+, +/dev/tty2+ и т.д.)~--- те,
которые вы можете увидеть безо всякого дополнительного
оборудования, просто переключившись на них из графического
сеанса.
\item +serial-getty@.service+ обеспечивает поддержку всех прочих
разновидностей терминалов, в том числе, подключаемых к
@@ -3380,7 +3385,7 @@ getty. Таким образом, вам достаточно лишь прав
ряд отличий от +getty@.service+, в частности, переменная \verb+$TERM+
в нем устанавливается в значение +vt102+ (должно хорошо работать
на большинстве физических терминалов), а не~+linux+ (которое
работаеть правильно только на виртуальных консолях), а также
работает правильно только на виртуальных консолях), а также
пропущены настройки, касающиеся очистки буфера прокрутки (и
поэтому имеющие смысл только на VT).
\end{itemize}
@@ -3399,18 +3404,18 @@ getty. Таким образом, вам достаточно лишь прав
как вы переключитесь на вторую виртуальную консоль. Отказавшись от
обязательного запуска нескольких экземпляров getty, мы сэкономили немного
системных ресурсов, а также сделали загрузку системы чуть-чуть быстрее. При
этом, с точки зрения пользователя, все очень просто: как только он переключается
на виртуальную консоль, на ней запускается getty, которая выводит приглашение к
логину. Пользователь может и не~подозревать о том, что до момента переключения
ничего этого не~было. Тем не~менее, если он войдет в систему и выполнит команду
+ps+, он увидит, что getty запущены только на тех консолях, на которых он уже
побывал.
этом, с точки зрения пользователя, все осталось так же просто: как только он
переключается на виртуальную консоль, на ней запускается getty, которая выводит
приглашение к логину. Пользователь может и не~подозревать о том, что до момента
переключения приглашения не~было. Тем не~менее, если он войдет в систему и
выполнит команду +ps+, он увидит, что getty запущены только на тех консолях, на
которых он уже побывал.
По умолчанию, автоматический запуск getty производится на виртуальных консолях с
первой по шестую (чтобы свести к минимуму отличия от привычной
конфигурации)\footnote{Тем не~менее, это поведение можно легко изменить,
задавая параметр +NAutoVTs=+ в файле
\href{http://www.freedesktop.org/software/systemd/man/logind.conf.html}{logind.conf}.}
\href{http://www.freedesktop.org/software/systemd/man/logind.conf.html}{logind.conf}.}.
Отметим, что автоматический запуск getty на конкретной консоли производится
только при условии, что эта консоль не~занята другой программой. В частности,
при интенсивном использовании механизма
@@ -3429,9 +3434,9 @@ getty. Таким образом, вам достаточно лишь прав
Что касается +tty6+, то она используется исключительно для автоматического
запуска getty, и недоступна другим подсистемам, в частности, графическому
серверу\footnote{При необходимости, вы можете легко поменять резервируемую
консоль, используя параметр +ReserveVT=+ в файле
\href{http://www.freedesktop.org/software/systemd/man/logind.conf.html}{logind.conf}.}
серверу\footnote{При необходимости, вы можете легко поменять номер резервируемой
консоли (или отключить резервирование), используя параметр +ReserveVT=+ в файле
\href{http://www.freedesktop.org/software/systemd/man/logind.conf.html}{logind.conf}.}.
Мы сделали так специально, чтобы гарантировать возможность входа в систему в
текстовом режиме, даже если графический сервер займет более пяти консолей.
@@ -3446,7 +3451,7 @@ getty. Таким образом, вам достаточно лишь прав
Консолью ядра~--- это та консоль, на которую выводятся сообщения ядра. Обычно
она настраивается в загрузчике, путем добавления к параметрам ядра аргумента
наподобие +console=ttyS0+\footnote{Подробнее об этой опции см. в файле
\href{https://www.kernel.org/doc/Documentation/kernel-parameters.txt}{kernel-parameters.txt}.}
\href{https://www.kernel.org/doc/Documentation/kernel-parameters.txt}{kernel-parameters.txt}.}.
Таким образом, если пользователь перенаправил вывод ядра на последовательную
консоль, то по завершении загрузки он увидит на этой консоли приглашение для
логина\footnote{Отметим, что getty, а точнее, +agetty+ на такой консоли
@@ -3459,7 +3464,7 @@ getty. Таким образом, вам достаточно лишь прав
\href{http://www.freedesktop.org/wiki/Software/systemd/Generators}{программой-генератором}~---
\href{http://www.freedesktop.org/software/systemd/man/systemd-getty-generator.html}{systemd-getty-generator}.
Генераторы запускаются в самом начале загрузки и автоматически настраивают
различные службы в зависимости от различных факторов.
различные службы в зависимости от соответствующих факторов.
В большинстве случаев, вышеописанного механизма автоматической настройки должно
быть достаточно, чтобы получить приглашение логина там, где нужно~--- без
@@ -3483,9 +3488,9 @@ daemon-reload}.}:
указанных последовательных портов при всех последующих загрузках.
В некоторых ситуациях может возникнуть необходимость в тонкой настройке
параметров getty (например, настроенная для ядра символьная скорость непригодна
для интерактивного сеанса). Тогда просто скопируйте штатный шаблон юнита в
каталог +/etc/systemd/system+ и отредактируйте полученную копию:
параметров getty (например, заданная для вывода сообщений ядра символьная
скорость непригодна для интерактивного сеанса). Тогда просто скопируйте штатный
шаблон юнита в каталог +/etc/systemd/system+ и отредактируйте полученную копию:
\begin{Verbatim}
# cp /usr/lib/systemd/system/serial-getty@.service /etc/systemd/system/serial-getty@ttyS2.service
# vi /etc/systemd/system/serial-getty@ttyS2.service
@@ -3496,7 +3501,7 @@ daemon-reload}.}:
\end{Verbatim}
В приведенном примере создает файл настроек, определяющий запуск getty на порту
+ttyS2+ (это определяется именем, под которым мы скопировали файл~---
+serial-getty@ttyS2.service+). Все изменения настроек, сделанные в этом файле,
+serial-getty@ttyS2.service+). Все изменения настроек, сделанные в данном файле,
будут распространяться только на этот порт.
Собственно, это все, что я хотел рассказать о последовательных портах,
@@ -3523,7 +3528,9 @@ Journal был включен в Fedora начиная с F17. В Fedora~18 jour
удобный механизм работы с системным журналом. Однако, и в~F17, и в~F18 journal
по умолчанию сохраняет информацию только в небольшой кольцевой буфер в каталоге
+/run/log/journal+. Как и все содержимое каталога +/run+, эта информация
теряется при перезагрузке. Такой подход сильно ограничивает использование
теряется при перезагрузке\footnote{Прим. перев.: Разумеется, это никак
не~относится к традиционному демону системного лога, даже если он работает
поверх journal.}. Такой подход сильно ограничивает использование
полезных возможностей journal, однако вполне достаточен для вывода актуальных
сообщений от служб в +systemctl status+. Начиная с Fedora~19, мы собираемся
включить сохранение логов на диск, в каталог +/var/log/journal+. При этом,
@@ -3544,8 +3551,8 @@ Journal был включен в Fedora начиная с F17. В Fedora~18 jour
\begin{Verbatim}
# mkdir -p /var/log/journal
\end{Verbatim}
После этого рекомендуется перезагрузить систему, чтобы заполнить журнал овыми
записями.
После этого рекомендуется перезагрузить систему, чтобы заполнить журнал новыми
записями.
Так как теперь у вас есть journal, syslog вам больше не~нужен (кроме ситуаций,
когда вам совершенно необходимо иметь +/var/log/messages+ в текстовом виде), и
@@ -3557,8 +3564,8 @@ Journal был включен в Fedora начиная с F17. В Fedora~18 jour
\subsection{Основы}
Итак, приступим. Нижеприведенный текст демонстрирует возможности systemd~195,
входящего в Fedora~18\footnote{Обновление со 195-й версией systemd в настоящее
время находится
входящего в Fedora~18\footnote{Обновление со 195-й версией systemd на момент
написания этих строк находится
\href{https://admin.fedoraproject.org/updates/FEDORA-2012-16709/systemd-195-1.fc18}{на
тестировании} и вскоре будет включено в состав Fedora~18.}, так что, если
некоторые из описанных трюков не~сработают в F17~--- пожалуйста, дождитесь F18.
@@ -3571,14 +3578,22 @@ Journal был включен в Fedora начиная с F17. В Fedora~18 jour
\end{Verbatim}
Если вы выполнили эту команду с полномочиями root, вы увидите все
журнальные сообщения, включая исходящие как от системных компонентов, так и от
залогиненных пользователей. Вывод этой команды форматируется в стиле
+/var/log/messages+, однако в нем добавлены кое-какие улучшения:
залогиненных пользователей\footnote{Прим. перев.: А если вы выполнили эту
команду от имени непривилегированного пользователя, не~входящего в группу
+adm+, и при этом не~включили сохранение логов на диск, то вы не~увидите
ничего~--- без специальных полномочий пользователь может просматривать только
собственный лог, а он по умолчанию ведется только если логи записываются на
диск.}. Вывод этой команды форматируется в стиле
+/var/log/messages+, но при этом добавлены кое-какие улучшения:
\begin{itemize}
\item Строки с приоритетом error и выше подсвечены красным.
\item Строки с приоритетом notice и warning выделены жирным шрифтом.
\item Все отметки времени сформированы с учетом вашего часового пояса.
\item Для навигации по тексту используется просмотрщик (pager), по
умолчанию +less+.
умолчанию +less+\footnote{Прим. перев.: В инструментах systemd,
включая journalctl, просмотрщик включается только при прямом
выводе на экран, и отключается при перенаправлении вывода в файл
или передаче его по каналу (shell pipe).}.
\item Выводятся \emph{все} доступные данные, включая информацию из
файлов, прошедших ротацию (rotated logs).
\item Загрузка системы отмечается специальной строкой, отделяющей
@@ -3587,7 +3602,8 @@ Journal был включен в Fedora начиная с F17. В Fedora~18 jour
Отметим, что в данной статье не~приводятся примеры такого вывода~--- прежде
всего, для краткости изложения, но также и для того, чтобы дать вам повод
поскорее попробовать Fedora~18 с systemd~195. Надеюсь, вы поймете суть и так.
поскорее попробовать Fedora~18 с systemd~195. Надеюсь, отсутствие таких примеров
не~помешает вам уловить суть.
\subsection{Контроль доступа}
@@ -3620,10 +3636,10 @@ $ journalctl
\subsection{Отслеживание логов в реальном времени}
Когда вы запускаете программу +journalctl+ без параметров, она выводит все
сообщения, сгенерированные на текущий момент. Однако, иногда бывает полезно
отслеживать их появление в режиме реального времени. В классической реализации
syslog это осуществлялось командой +tail -f /var/log/messages+. В journal ее
аналог выглядит так:
сообщения, сгенерированные на текущий момент, и возвращает управление оболочке.
Однако, иногда бывает полезно отслеживать их появление в режиме реального
времени. В классической реализации syslog это осуществлялось командой
+tail -f /var/log/messages+. В journal ее аналог выглядит так:
\begin{Verbatim}
$ journalctl -f
\end{Verbatim}
@@ -3669,18 +3685,18 @@ $ journalctl -u httpd --since=00:00 --until=9:30
\begin{Verbatim}
$ journalctl /dev/sdc
\end{Verbatim}
Кошмар, ошибка ввода-вывода!\footnote{Ну ладно, признаюсь, здесь я немножко
Кошмар, ошибка ввода-вывода\footnote{Ну ладно, признаюсь, здесь я немножко
считерил. Индексирование сообщений ядра по блочным устройствам пока что
не~принято в апстрим, но Ганс
\href{http://www.spinics.net/lists/linux-scsi/msg62499.html}{проделал огромную
работу}, чтобы реализовать эту функциональность, и я надеюсь, что к релизу F18
все будет.} Нужно срочно заменить диск, пока не~начались более серьезные
проблемы. Ладно, пошли дальше. Что у нас там случилось с процессом vpnc?
все будет.}! Нужно срочно заменить диск, пока не~начались более серьезные
проблемы. Ладно, пойдем дальше. Что у нас там случилось с процессом vpnc?
\begin{Verbatim}
$ journalctl /usr/sbin/vpnc
\end{Verbatim}
Хм, ничего подозрительного. Но, кажется, проблема где-то во взаимодействии между
+vpnc+ и +dhclient+. Посмотрим объединенный и отсортированный по времени списов
+vpnc+ и +dhclient+. Посмотрим объединенный и отсортированный по времени список
сообщений от этих процессов:
\begin{Verbatim}
$ journalctl /usr/sbin/vpnc /usr/sbin/dhclient
@@ -3735,7 +3751,7 @@ Tue, 2012-10-23 23:51:38 CEST [s=ac9e9c423355411d87bf0ba1a9b424e8;i=4301;b=5335e
\href{http://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html}%
{странице руководства}.
И база данных Journal индексируется по \emph{всем} этим полям! И мы можем
База данных Journal индексируется по \emph{всем} этим полям! И мы можем
использовать любое из них в качестве критерия выборки:
\begin{Verbatim}
$ journalctl _UID=70
@@ -3746,18 +3762,18 @@ $ journalctl _UID=70
\begin{Verbatim}
$ journalctl _UID=70 _UID=71
\end{Verbatim}
Указание двух значений для одного и того же поля эквивалентно логическому ИЛИ.
Таким образом, будут выведены записи как от процессов с UID 70, так и от
Указание нескольких значений для одного и того же поля эквивалентно логическому
ИЛИ. Таким образом, будут выведены записи как от процессов с UID 70, так и от
процессов с UID 71.
\begin{Verbatim}
$ journalctl _HOSTNAME=epsilon _COMM=avahi-daemon
\end{Verbatim}
А указание двух \emph{различных} полей дает эффект логического И. В результате,
будут выведены записи только от процесса +avahi-daemon+, работающего на хосте с
именем +epsilon+.
А вот указание нескольких \emph{различных} полей дает эффект логического И. В
результате, будут выведены записи только от процесса +avahi-daemon+, работающего
на хосте с именем +epsilon+.
Но мы этим не~ограничимся! Мы же суровые компьютерщики, мы хотим использовать
сложные логические выражения!
Но мы этим не~ограничимся! Мы же суровые компьютерщики, нам нужны сложные
логические выражения!
\begin{Verbatim}
$ journalctl _HOSTNAME=theta _UID=70 + _HOSTNAME=epsilon _COMM=avahi-daemon
\end{Verbatim}
@@ -3773,8 +3789,8 @@ $ journalctl _HOSTNAME=theta _UID=70 + _HOSTNAME=epsilon _COMM=avahi-daemon
\subsection{И немного магии}
Уже неплохо, правда? Но есть один недостаток~--- мы же не~сможем запомнить все
возможные значения все полей журнала! Для этого была бы нужна очень хорошая
Уже неплохо, правда? Но есть одна проблема~--- мы же не~сможем запомнить все
возможные значения всех полей журнала! Для этого была бы нужна очень хорошая
память. Но +journalctl+ вновь приходит к нам на помощь:
\begin{Verbatim}
$ journalctl -F _SYSTEMD_UNIT
@@ -3829,7 +3845,7 @@ SELinux ;-) Разумеется, такое дополнение работае
Например, о том, что +journalctl+ может выводить данные в формате JSON, или в
формате +/var/log/messages+, но с относительными метками времени, как в dmesg.
\section{Управление ресурсами}
\section{Управление ресурсами с помощью cgroups}
Важную роль в современных компьютерных системах играют механизмы управления
использованием ресурсов: когда вы запускаете на одной системе несколько
@@ -3837,7 +3853,7 @@ SELinux ;-) Разумеется, такое дополнение работае
в соответствии с некоторыми правилами. В частности, это особенно актуально на
маломощных встраиваемых и мобильных системах, обладающих очень скудными
ресурсами. Но та же задача актуальна и для очень мощных вычислительных
кластеров, которые располагают огромными ресурсами, но при это несут и огромную
кластеров, которые располагают огромными ресурсами, но при этом несут и огромную
вычислительную нагрузку.
Исторически, в Linux поддерживался только одна схема управления ресурсами: все
@@ -3851,7 +3867,7 @@ CGI-процессов при прочих равных получает гор
syslog, у которой не~так много процессов.
В процессе проектирования архитектуры systemd, мы практически сразу поняли, что
управление ресурсов должно быть одной из его базовых функций, заложенных в
управление ресурсов должно быть одной из базовых функций, заложенных в
основы его структуры. В современной системе~--- неважно, серверной или
встраиваемой~--- контроль использования процессора, памяти и ввода-вывода для
различных служб нельзя добавлять задним числом. Такая функциональность должна
@@ -3866,16 +3882,35 @@ Resource Limits}.
администраторам.
В свое время я
\href{http://0pointer.de/blog/projects/cgroups-vs-cgroups.html}{пояснял}, что
контрольные группы Linux (cgroups) могут работать и как механизм группировки и
отслеживания процессов, и как инструмент управления использованием ресурсов. Для
функционирования systemd необходим только первый из этих режимов, а второй
опционален. И именно этот опциональный второй режим дает вам возможность
распределять ресурсы между службами. (А сейчас очень рекомендую вам, прежде чем
продолжать чтение этой статьи, ознакомиться с
\href{https://en.wikipedia.org/wiki/Cgroups}{базовой информацией о cgroups}.
Хотя дальнейшие рассуждения и не~будут затрагивать низкоуровневые аспекты, все
же будет лучше, если у вас сформируется некоторое представление о них.)
\href{http://0pointer.de/blog/projects/cgroups-vs-cgroups.html}{пояснял}%
\footnote{Прим. перев.: В указанном документе автор рассказывает, что
контрольные группы Linux состоят из двух сущностей: \textbf{(A)} механизма
иерархической группировки и маркировки процессов, и \textbf{(B)} механизма,
позволяющего распределять ресурсы между полученными группами. Для работы (B)
необходимо (A), но не~наоборот~--- (A) может прекрасно работать без (B). Для
нормально функционирования systemd (A) \emph{необходим}, а (B) опционален (он
лишь обеспечивает работу некоторых настроек). Вы можете собрать ядро только с
необходимой для (A) опцией +CONFIG_CGROUPS=y+, отключив все связанные с (B)
опции (такие как {\tiny +CONFIG_CGROUP_FREEZER=y+, +CONFIG_CGROUP_DEVICE=y+,
+CONFIG_CGROUP_CPUACCT=y+, +CONFIG_CGROUP_MEM_RES_CTLR=y+,
+CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y+, +CONFIG_CGROUP_MEM_RES_CTLR_KMEM=y+,
+CONFIG_CGROUP_PERF=y+, +CONFIG_CGROUP_SCHED=y+, +CONFIG_BLK_CGROUP=y+,
+CONFIG_NET_CLS_CGROUP=y+, +CONFIG_NET_PRIO_CGROUP=y+}), и systemd будет
нормально работать на такой системе (за исключением того, что связанные с этими
контроллерами настройки не~будут срабатывать). Однако, если собрать ядро без
+CONFIG_CGROUPS=y+, функциональность systemd будет сильно ограничена. При этом,
автор особо подчеркивает, что все негативные эффекты влияния контрольных групп
на производительность обусловлены именно (B), в то время как (A) на
производительность практически не~влияет.}, что контрольные группы Linux
(cgroups) могут работать и как механизм группировки и отслеживания процессов, и
как инструмент управления использованием ресурсов. Для функционирования systemd
необходим только первый из этих режимов, а второй опционален. И именно этот
опциональный второй режим дает вам возможность распределять ресурсы между
службами. (А сейчас очень рекомендую вам, прежде чем продолжать чтение этой
статьи, ознакомиться с \href{https://en.wikipedia.org/wiki/Cgroups}{базовой
информацией о cgroups}. Хотя дальнейшие рассуждения и не~будут затрагивать
низкоуровневые аспекты, все же будет лучше, если у вас сформируется некоторое
представление о них.)
Основными контроллерами cgroups, отвечающими за управление ресурсами, являются
\href{http://www.kernel.org/doc/Documentation/scheduler/sched-design-CFS.txt}{cpu},
@@ -3886,6 +3921,624 @@ Resource Limits}.
systemd предоставляет ряд высокоуровневых настроек, позволяющих использовать эти
контроллеры, не~вникая в технические детали их работы.
\subsection{Процессор}
Если в ядре включен контроллер +cpu+, systemd по умолчанию создает контрольную
группу по этому ресурсу для каждой службы. Даже без каких-либо дополнительных
настроек это дает положительных эффект: на системе под управлением systemd все
службы получают равные доли процессорного времени, независимо от количества
процессов, запущенных в рамках службы. Например, на вашем веб-сервере MySQL с
несколькими рабочими процессами получит такую же долю процессорного времени,
как и Apache, даже если тот запустил 1000 CGI-процессов. Разумеется, такое
поведение при необходимости можно легко отключить~--- см. опцию
\hreftt{http://0pointer.de/public/systemd-man/systemd.conf.html}{DefaultControllers=}
в файле +/etc/systemd/system.conf+.
Если \emph{равномерное} распределение процессорного времени между службами вас
не~устраивает, и вы хотите выделить определенным службам больше или меньше
времени~--- используйте опцию
\hreftt{http://0pointer.de/public/systemd-man/systemd.exec.html}{CPUShares=} в
конфигурационном файле службы. По умолчанию это значение равно 1024. Увеличивая
это число, вы даете службе больше процессорного времени, уменьшая~---
соответственно, меньше.
Рассмотрим небольшой практический пример. Допустим, вам нужно увеличить
для службы Apache относительную долю потребления процессора до 1500. Для этого
создаем файл <<ручных>> настроек +/etc/systemd/system/httpd.service+, который
включает в себя все те же опции, что и файл настроек по умолчанию
+/usr/lib/systemd/system/httpd.service+, отличаясь от него только значением
+CPUShares=+:
\begin{Verbatim}
.include /usr/lib/systemd/system/httpd.service
[Service]
CPUShares=1500
\end{Verbatim}
Первая строка обеспечивает включение в нашу конфигурацию файла с настройками по
умолчанию, сделанными разработчиками Apache или его сопровождающими в вашем
дистрибутиве (если это включение не~указать явно, данный файл будет проигнорирован).
Далее, мы указываем тот параметр, который хотим изменить. Сохраняем файл,
приказываем systemd перечитать конфигурацию, и перезапускаем Apache, чтобы
настройки вступили в силу\footnote{Прим. перев.: К сожалению, в настоящее время
systemd не~поддерживает изменение параметров контрольных групп без перезапуска
службы. Но вы можете узнать контрольную группу службы командой наподобие
+systemctl show -p ControlGroup avahi-daemon.service+, и выполнить настройки
любым удобным для вас способом, например, через запись значений в псевдофайлы
cgroupfs. Разумеется, при следующем запуске службы к ней будут применены
параметры, указанные в конфигурационном файле.}:
\begin{Verbatim}
systemctl daemon-reload
systemctl restart httpd.service
\end{Verbatim}
Готово!
Обратите внимание, что явное указание значения +CPUShares=+ в конфигурации
службы заставит systemd создать для нее контрольную группу в иерархии контроллера
+cpu+, даже если этот контроллер не~указан в +DefaultControllers=+ (см. выше).
\subsection{Отслеживание использования ресурсов}
Для того, чтобы правильно распределять ресурсы между службами, неплохо бы знать
реальные потребности этих служб. Чтобы упростить для вас отслеживание
потребления ресурсов службами, мы подготовили утилиту
\href{http://www.freedesktop.org/software/systemd/man/systemd-cgtop.html}{systemd-cgtop},
которая находит все имеющиеся в системе контрольные группы, определяет для
каждой из них количество потребляемых ресурсов (процессорное время, память и
ввод-вывод) и выводит эти данные в динамически обновляемой сводной таблице, по аналогии
с программой \href{http://linux.die.net/man/1/top}{top}. Используя вводимое
systemd распределение служб по контрольным группам, эта утилита выводит для
служб те же сведения, которые top выводит для отдельных процессов.
К сожалению, по умолчанию +cgtop+ может раздельно отслеживать для каждой службы
только потребление процессорного времени, а сведения по использованию памяти и
ввода-вывода доступны только для всей системы в целом. Это ограничение возникает
из-за того, что в конфигурации по умолчанию контрольные группы для служб
создаются только в иерархии контроллера +cpu+, но не~+memory+ и~+blkio+. Без
создания групп в иерархии этих контроллеров невозможно отследить использование
ресурса по службам. Самый простой способ обойти это ограничение~--- приказать
systemd создавать соответствующие группы, добавив +memory+ и +blkio+ в перечень
+DefaultControllers=+ в файле +system.conf+.
\subsection{Память}
Используя опции +MemoryLimit=+ и +MemorySoftLimit=+, вы можете ограничивать
суммарное потребление оперативной памяти всеми процессами службы. В них
указывается предел потребления памяти в байтах\footnote{Прим. перев.: Разница
между +MemorySoftLimit=+ и +MemoryLimit=+ состоит в том, что первый предел можно
превышать, если в системе еще есть достаточное количество свободной памяти.
Второй из этих пределов превышать нельзя, независимо от наличия свободной
памяти. Подробнее см. раздел <<Soft limits>> в
\href{http://www.kernel.org/doc/Documentation/cgroups/memory.txt}{файле
документации}.}. При этом поддерживаются суффиксы K, M, G и T, обозначающие
соответственно, килобайт, мегабайт, гигабайт и терабайт (по основанию 1024).
\begin{Verbatim}
.include /usr/lib/systemd/system/httpd.service
[Service]
MemoryLimit=1G
\end{Verbatim}
По аналогии с +CPUShares=+, явное указание этих опций заставит systemd создать
для службы контрольную группу в иерархии контроллера +memory+, даже если он
не~был указан в +DefaultControllers=+.
\subsection{Ввод-вывод}
Для контроля пропускной полосы ввода-вывода с блочных устройств, доступно
несколько настроек. Первая из них~--- +BlockIOWeight=+, задающая \emph{долю} полосы
ввода-вывода для указанной службы. Принцип похож на +CPUShares=+ (см. выше), однако
здесь величина относительной доли ограничена значениями от 10 до 1000. По
умолчанию, она равна 1000. Уменьшить долю для службы Apache можно так:
\begin{Verbatim}
.include /usr/lib/systemd/system/httpd.service
[Service]
BlockIOWeight=500
\end{Verbatim}
При необходимости, вы можете задать такое значение отдельно для каждого
устройства:
\begin{Verbatim}
.include /usr/lib/systemd/system/httpd.service
[Service]
BlockIOWeight=/dev/disk/by-id/ata-SAMSUNG_MMCRE28G8MXP-0VBL1_DC06K01009SE009B5252 750
\end{Verbatim}
При этом, точное название устройства знать не~обязательно~--- достаточно указать
интересующий вас каталог:
\begin{Verbatim}
.include /usr/lib/systemd/system/httpd.service
[Service]
BlockIOWeight=/home/lennart 750
\end{Verbatim}
Если заданный вами путь не~указывает на файл устройства, systemd автоматически
определит, на каком устройстве расположен указанный файл/каталог, и выставит для
этого устройства соответствующую настройку.
Вы можете добавить несколько таких строк, задавая долю пропускной полосы
отдельно для различных устройств, и при этом также допускается указать <<общее>>
значение (как в первом примере), которое будет использовано для всех остальных
устройств.
В качестве альтернативы относительной доле пропускной полосы, вы также можете
ограничивать абсолютную долю, используя настройки +BlockIOReadBandwidth=+ и
+BlockIOWriteBandwidth=+. В них нужно указать устройство или любой находящийся
на нем файл/каталог, а также предельную скорость чтения/записи в байтах в
секунду:
\begin{Verbatim}
.include /usr/lib/systemd/system/httpd.service
[Service]
BlockIOReadBandwith=/var/log 5M
\end{Verbatim}
В результате, для данной службы скорость чтения с устройства, содержащего
каталог +/var/log+, будет ограничена величиной 5 мегабайт в секунду.
По аналогии с вышеописанными +CPUShares=+ и +MemoryLimit=+, явное указание любой
из приведенных настроек пропускной полосы заставит systemd создать для службы
контрольную группу в иерархии контроллера +blkio+.
\subsection{Прочие параметры}
Вышеописанные опции покрывают лишь малую толику настроек, поддерживаемых
различными контроллерами Linux cgroups. Мы добавили высокоуровневый интерфейс
только к тем настройкам, которые кажутся нам наиболее важным для большинства
пользователей. Из соображений удобства мы добавили механизмы, обеспечивающие
поддержку крупных единиц измерения (килобайты, мегабайты и т.д.) и
автоматическое определение блочных устройств по указанному файлу/каталогу.
В некоторых случаях описанных высокоуровневых настроек может оказаться
недостаточно~--- допустим, вам нужно задать низкоуровневую настройку cgroups,
для которой мы (пока) не~добавили высокоуровневого аналога. На этот случай мы
предусмотрели универсальных механизм задания таких опций в конфигурационных
файлах юнитов. Рассмотрим, например, задание для службы параметра
\emph{swappiness} (относительная интенсивность использования подкачки для
процессов службы). В systemd нет высокоуровневой настройки для этого значения.
Однако вы можете задать его, используя низкоуровневую настройку
+ControlGroupAttribute=+:
\begin{Verbatim}
.include /usr/lib/systemd/system/httpd.service
[Service]
ControlGroupAttribute=memory.swappiness 70
\end{Verbatim}
Как обычно, явное указание настройки, относящейся к какому-либо контроллеру (в
нашем случае +memory+) приведет к автоматическому созданию группы в иерархии
данного контроллера.
В дальнейшем, возможно, мы расширим возможности высокоуровневой настройки
различных параметров контрольных групп. Если вы часто пользуетесь какими-то из
них и полагаете, что для них можно добавить соответствующие опции~---
не~стесняйтесь обращаться к нам. А лучше всего~--- присылайте сразу патч!
\begin{caveat}
Обратите внимание, что использование некоторых контроллеров может сильно
сказаться на производительности системы. Это та цена, которую приходится
платить за контроль над ресурсами. Использование таких контроллеров
может ощутимо замедлить некоторые операции. В частности, весьма
нелестная в этом плане репутация закрепилась за контроллером +memory+
(хотя, не~исключено, что эта проблема уже исправлена в свежих выпусках
ядра).
\end{caveat}
Для углубленного изучения темы, затронутой в этой статье, вы можете обратиться к
документации по
\href{http://0pointer.de/public/systemd-man/systemd.exec.html}{поддерживаемым
настройкам юнитов}, а также по контроллерам
\href{http://www.kernel.org/doc/Documentation/scheduler/sched-design-CFS.txt}{cpu},
\href{http://www.kernel.org/doc/Documentation/cgroups/memory.txt}{memory} и
\href{http://www.kernel.org/doc/Documentation/cgroups/blkio-controller.txt}{blkio}.
Стоит подчеркнуть, что мы сейчас обсуждали распределение ресурсов \emph{между
службами}. В дополнение к этим современным механизмам, systemd также
поддерживает и традиционные настройки, касающиеся распределения ресурсов
\emph{между отдельными процессами}. Хотя такие настройки обычно наследуются
порожденными процессами, они, тем не~менее, все равно ограничивают ресурсы
на уровне отдельных процессов. В частности, к ним относятся +IOSchedulingClass=+,
+IOSchedulingPriority=+, +CPUSchedulingPolicy=+, +CPUSchedulingPriority=+,
+CPUAffinity=+, +LimitCPU=+ и т.п. Для их работы не~требуют контроллеры cgroups,
и они не~так сильно ухудшают производительность. Возможно, мы рассмотрим их в
последующих статьях.
\section{Проверка на виртуальность}
Еще в начале разработки systemd, мы внимательно изучали существовавшие на тот
момент init-скрипты, выделяя наиболее типичные для них операции. Среди прочих, в
составленный нами список попала и такая функция, как определение виртуализации:
некоторые скрипты проверяли, запускаются они в виртуальном окружении (например,
KVM, VMWare, LXC и т.д.) или на полноценной, физической системе. Часть этих
скриптов отказывалась работать на виртуальных системах (например, службы
управления устройствами совершенно излишни в виртуальных контейнерах, не~имеющих
доступа к устройствам), другие же, наоборот, запускались только в определенных
виртуальных окружениях (например, всевозможные <<guest additions>>,
рекомендуемые к запуску на гостевых системах VMWare и VirtualBox). По-хорошему,
в некоторых ситуациях было бы более правильно проверять некоторые другие
условия, а не~пытаться явно определить наличие виртуализации. Тем не~менее,
всесторонне изучив вопрос, мы пришли к выводу, что во многих случаях
возможность явной проверки такого условия при запуске служб была бы очень
кстати. В результате, мы добавили поддержку соответствующей опции настройки
юнитов~---
\hreftt{http://www.freedesktop.org/software/systemd/man/systemd.unit.html}{ConditionVirtualization};
кроме того, мы создали небольшую утилиту, которую можно вызывать из
скриптов~---
\hreftt{http://www.freedesktop.org/software/systemd/man/systemd-detect-virt.html}{systemd-detect-virt(1)};
и наконец, мы предоставили простой интерфейс для шины D-Bus, позволяющий
получить информацию о виртуализации даже непривилегированным программам.
Определить, запущен код на виртуальной системе, или на физической, на самом деле
\href{http://cgit.freedesktop.org/systemd/systemd/tree/src/shared/virt.c#n30}{не~так
уж и сложно}. В зависимости от того, какие именно механизмы виртуализации вы
хотите определить, основная работа сводится к выполнению инструкции CPUID и,
возможно, проверке некоторых файлов в +/sys+ и +/proc+. Основная трудность
здесь~--- точно знать строки, которые нужно искать. Список таких строк
необходимо поддерживать в актуальном состоянии. В настоящий момент, systemd
определяет следующие механизмы виртуализации:
\begin{itemize}
\item Полная виртуализация (т.е. виртуальные машины):
\begin{itemize}
\item qemu
\item kvm
\item vmware
\item microsoft
\item oracle
\item xen
\item bochs
\end{itemize}
\item Виртуализация на уровне ОС (т.е. контейнеры):
\begin{itemize}
\item chroot
\item openvz
\item lxc
\item lxc-libvirt
\item \hyperref[sec:chroots]{systemd-nspawn}
\end{itemize}
\end{itemize}
Рассмотрим, как можно использовать эту функциональность.
\subsection{Условия на запуск юнитов}
При помощи опции
\hreftt{http://www.freedesktop.org/software/systemd/man/systemd.unit.html}{ConditionVirtualization},
добавленной в секцию +[Unit]+ файла конфигурации юнита, вы можете обеспечить
запуск (или наоборот, отмену запуска) данного юнита в зависимости от того,
работает ли он на виртуальной системе, или нет. В случае утвердительного ответа,
также можно уточнить, какая система виртуализации при этом используется.
Например:
\begin{Verbatim}
[Unit]
Name=My Foobar Service (runs only only on guests)
ConditionVirtualization=yes
[Service]
ExecStart=/usr/bin/foobard
\end{Verbatim}
Помимо <<+yes+>> или <<+no+>>, вы также можете указать идентификатор конкретной
системы виртуализации (согласно списку выше, например, <<+kvm+>>, <<+vmware+>> и
т.д.), либо <<+container+>> или <<+vm+>> (что позволит отличить виртуализацию на
уровне ОС от полной виртуализации). Кроме того, вы можете добавить перед
значением восклицательный знак, и результат проверки будет инвертирован (юнит
запустится только в том случае, если указанная технология
\emph{не}~используется). Подробности вы можете узнать на
\href{http://www.freedesktop.org/software/systemd/man/systemd.unit.html}{странице
руководства}.
\subsection{В скриптах}
В скриптах оболочки вы можете выполнить аналогичные проверки при помощи утилиты
\hreftt{http://www.freedesktop.org/software/systemd/man/systemd-detect-virt.html}{systemd-detect-virt(1)}.
Например:
\begin{Verbatim}
if systemd-detect-virt -q ; then
echo "Virtualization is used:" `systemd-detect-virt`
else
echo "No virtualization is used."
fi
\end{Verbatim}
Эта утилита возвращает код 0 (успех), обнаружив виртуализацию, или ненулевое
значение, если виртуализация не~выявлена. Кроме того, она выводит идентификатор
обнаруженной системы виртуализации (согласно списку выше), если это не~было
запрещено опцией +-q+. Кроме того, опции +-c+ и +-v+ позволяют ограничить
проверки только механизмами виртуализации на уровне ОС, либо полной
виртуализации, соответственно. Подробности см. на
\href{http://www.freedesktop.org/software/systemd/man/systemd-detect-virt.html}{странице
руководства}.
\subsection{В программах}
Информация о виртуализации также представлена на системной шине:
\begin{Verbatim}
$ gdbus call --system --dest org.freedesktop.systemd1 --object-path /org/freedesktop/systemd1 \
> --method org.freedesktop.DBus.Properties.Get org.freedesktop.systemd1.Manager Virtualization
(<'systemd-nspawn'>,)
\end{Verbatim}
Если виртуализация не~выявлена, это свойство содержит пустую строку. Обратите
внимание, что некоторые контейнерные системы не~могут быть обнаружены напрямую
из непривилегированного кода. Именно поэтому мы не~стали создавать библиотеку, а
воспользовались шиной D-Bus, которая позволяет корректно решить проблему
привилегий.
Стоит отметить, что все эти инструменты определяют только <<самый внутренний>>
из задействованных механизмов виртуализации. Если вы используете несколько
систем, вложенных друг в друга, вышеописанные инструменты обнаружат только ту, в
которой они непосредственно запущены. В частности, если они работают в
контейнере, находящемся внутри виртуальной машины, они увидят только контейнер.
\section{Сокет-активация служб и контейнеров}
\href{http://0pointer.de/blog/projects/socket-activation.html}{Сокет}-%
\href{http://0pointer.de/blog/projects/socket-activation2.html}{активация}~---
это одна из наиболее интересных возможностей systemd. Еще в
\href{http://0pointer.de/blog/projects/systemd.html}{первом анонсе} нашего
проекта мы рассказывали о том, как этот механизм улучшает
параллелизацию и отказоустойчивость использующих сокеты служб, а также упрощает
формирование зависимостей между службами при загрузке. В данной статье я
продолжу рассказ о его возможностях~--- на этот раз речь пойдет об увеличении
числа служб и контейнеров, работающих на одной и той же системе, без повышения
потребления ресурсов. Другими словами: как можно увеличить количество клиентских
сайтов на хостинговых серверах без затрат на новое оборудование.
\subsection{Сокет-активация сетевых служб}
Начнем с небольшого отступления. Итак, что же такое сокет-активация, и как она
работает? На самом деле все довольно просто. systemd создает <<слушающие>>
сокеты (не~обязательно IP) от имени вашей службы (которая пока не~запущена) и
ожидает входящие соединения. Как только в сокет поступает первый запрос, systemd
запускает службу и передает ей полученные данные. После обработки запроса, в
зависимости от реализации службы, она может продолжать работу, ожидая новых
соединений, или завершиться, переложив эту задачу обратно на systemd (который
вновь запустит ее при поступлении очередного запроса). При этом, со стороны
клиента невозможно отличить, когда служба запущена, а когда~--- нет. Сокет
постоянно остается открытым для входящих соединений, и все они обрабатываются
быстро и корректно.
Такая конфигурация позволяет снизить потребление ресурсов: службы работают и
потребляют ресурсы только тогда, когда это действительно необходимо. Многие
интернет-сайты и службы могут использовать это с выгодой для себя. Например,
хостеры веб-сайтов знают, что из огромного количества существующих в Интернете
сайтов лишь малая часть получает непрерывный поток запросов. Большинство же
сайтов, хотя и должны постоянно оставаться доступными, получают запросы очень
редко. Используя сокет-активацию, вы можете воспользоваться этим: разместив
множество таких сайтов на одной системе и активируя их службы только при
необходимости, вы получаете возможность <<оверкоммита>>: ваша система
будет обслуживать сайтов больше, чем формально позволяют ее ресурсы. Разумеется,
увлекаться оверкоммитом не~стоит, иначе в моменты пиковой нагрузки ресурсов
может действительно не~хватить.
С помощью systemd вы без труда можете организовать такую схему. Множество
современных сетевых служб уже поддерживают сокет-активацию <<из коробки>> (а в
те, которые пока не~поддерживают, ее
\href{http://0pointer.de/blog/projects/socket-activation.html}{не~так уж} и
\href{http://0pointer.de/blog/projects/socket-activation2.html}{сложно}
добавить). Реализованный в systemd механизм управления
\hyperref[sec:instances]{экземплярами служб} позволяет подготовить
универсальные шаблоны конфигурации служб, и в соответствии с ними для каждого
сайта будет запускаться свой экземпляр службы. Кроме того, не~стоит забывать,
что systemd предоставляет \hyperref[sec:security]{богатый арсенал}
механизмов обеспечения безопасности и разграничения доступа, который позволит
изолировать клиентские сайты друг от друга (например, службы каждого клиента
будут видеть только его собственный домашний каталог, в то время как каталоги
всех остальных пользователей будут им недоступны). Итак, в конечном итоге вы
получаете надежную и масштабируемую серверную систему, на сравнительно небольших
ресурсах которой функционирует множество безопасно изолированных друг от друга
служб~--- и все это реализовано штатными возможностями вашей ОС.
Подобные конфигурации уже используются на рабочих серверах ряда компаний. В
частности, специалисты из \href{https://www.getpantheon.com/}{Pantheon}
используют такую схему для обслуживания масштабируемой инфраструктуры множества
сайтов на базе Drupal. (Стоит упомянуть, что заслуга ее внедрения в Pantheon
принадлежит Дэвиду Штрауссу. Дэвид, ты крут!)
\subsection{Сокет-активация контейнеров}
Все вышеописанные технологии уже реализованы в ранее вышедших версиях systemd.
Если ваш дистрибутив поддерживает systemd, вы можете воспользоваться этими
механизмами прямо сейчас. А теперь сделаем шаг вперед: начиная с
systemd 197 (и, соответственно, с Fedora~19), мы добавили поддержку
сокет-активации \emph{виртуальных контейнеров} с полноценными ОС внутри. И
я считаю это действительно важным достижением.
Сокет-активация контейнеров работает следующим образом. Изначально, systemd
хост-системы слушает порты от имени контейнера (например, порт SSH, порт
веб-сервера и порт сервера СУБД). При поступлении на любой из этих портов
входящего запроса, systemd запускает контейнер и передает ему все его сокеты.
Внутри контейнера, еще один systemd (init гостевой системы) принимает эти
сокеты, после чего вступает в работу вышеописанная схема обычной сокет-активации
служб. При этом, SSH-сервер, веб-сервер и СУБД-сервер <<видят>> только ОС
контейнера~--- хотя они были активированы сокетами, созданными на хосте! Для
клиента все эти тонкости скрыты. Таким образом, мы получаем виртуальный
контейнер с собственной ОС, активируемый при поступлении входящего сетевого
соединения, причем совершенно прозрачно для клиента\footnote{Кстати говоря,
\href{https://plus.google.com/115547683951727699051/posts/cVrLAJ8HYaP}{это еще
один аргумент} в пользу важности быстрой загрузки для серверных систем.}.
Внутри контейнера функционирует полноценная ОС, причем ее дистрибутив
не~обязательно совпадает с дистрибутивом хост-системы. Например, вы можете
установить на хосте Fedora, и запускать на нем несколько контейнеров с Debian.
Контейнеры имеют собственные init-системы, собственные SSH-серверы, собственные
списки процессов и т.д., но при этом пользуются некоторыми механизмами ОС хоста
(например, управлением памятью).
К настоящему моменту сокет-активация контейнеров поддерживается лишь встроенным
в systemd простейшим контейнерным менеджером~---
\hyperref[sec:chroots]{systemd-nspawn}. Мы надеемся, что соответствующая
возможность вскоре появится и в
\href{http://libvirt.org/drvlxc.html}{libvirt-lxc}. А пока рассмотрим
использование этого механизма на примере systemd-nspawn.
Начнем с установки файлов ОС контейнера в выбранный каталог. Детальное
рассмотрение этого вопроса выходит далеко за рамки нашего обсуждения, и
при этом детально рассмотрено во многих статьях и руководствах. Поэтому
ограничусь лишь несколькими наиболее важными замечаниями. В частности, команда
для установки Fedora будет выглядеть следующим образом:
\begin{Verbatim}
$ yum --releasever=19 --nogpg --installroot=/srv/mycontainer/ --disablerepo='*' \
> --enablerepo=fedora install systemd passwd yum fedora-release vim-minimal
\end{Verbatim}
а для Debian~---
\begin{Verbatim}
$ debootstrap --arch=amd64 unstable /srv/mycontainer/
\end{Verbatim}
Также см. последние абзацы страницы руководства
\href{http://www.freedesktop.org/software/systemd/man/systemd-nspawn.html}{systemd-nspawn(1)}.
Стоит отметить, что в настоящее время реализация системного аудита в Linux
не~поддерживает виртуальные контейнеры, и ее включение в ядре хоста вызовет
множество ошибок при попытке запустить контейнер. Отключить ее можно добавлением
+audit=0+ в строку параметров загрузки ядра хоста.
Разумеется, внутри контейнера должен должен быть установлен systemd версии
не~ниже 197. Установить его и произвести другие необходимые настройки можно при
помощи того же \hyperref[sec:chroots]{systemd-nspawn} (пока что в режиме аналога
+chroot+, т.е. без параметра +-b+). После этого можно попробовать загрузить ОС
контейнера, используя systemd-nspawn уже с параметром +-b+.
Итак, ваш контейнер нормально загружается и работает. Подготовим для него
service-файл, при помощи которого systemd сможет запускать и останавливать
виртуальное окружение. Для этого, создадим на хост системе файл
+/etc/systemd/system/mycontainer.service+ со следующим содержанием:
\begin{Verbatim}
[Unit]
Description=My little container
[Service]
ExecStart=/usr/bin/systemd-nspawn -jbD /srv/mycontainer 3
KillMode=process
\end{Verbatim}
Теперь мы можем запускать и останавливать эту службу командами +systemctl start+
и +stop+. Однако, пока что мы не~можем войти в эту систему\footnote{Прим.
перев.: Ручной запуск на хосте соответствующей команды +systemd-nspawn -b+ во
время работы такой службы просто создаст \emph{еще один контейнер}. Хотя
корневой каталог у них один и тот же, пространства имен (например, списки
процессов) у каждого будут свои. Впрочем, данная задача может быть решена
утилитой +nsenter+, которая должна войти в следующий релиз util-linux
(предположительно, 2.23).}. Настроим на контейнере SSH-сервер, причем таким
образом, что подключение к его порту активировало весь контейнер, а затем
активировало сервер, работающий внутри. Начнем с того, что прикажем хосту
слушать порт SSH для контейнера. Для этого создадим на хосте файл
+/etc/systemd/system/mycontainer.socket+:
\begin{Verbatim}
[Unit]
Description=The SSH socket of my little container
[Socket]
ListenStream=23
\end{Verbatim}
После того, как мы запустим этот юнит командой +systemctl start+, systemd будет
слушать 23-й TCP-порт хоста. В примере выбран именной 23-й, потому что 22-й
скорее всего окажется занят SSH-сервером самого хоста. nspawn виртуализует
списки процессов и точек монтирования, но не~сетевые стеки, поэтому порты хоста
и гостей не~должны конфликтовать\footnote{Прим. перев.: Ограниченные возможности
виртуализации сети в +systemd-nspawn+ значительно затрудняют использование
описываемой технологии. Ее практическое применение будет иметь смысл после
реализации соответствующей поддержки в lxc-libvirt, либо расширения возможностей
nspawn. Впрочем, опытные администраторы легко могут найти обходные пути,
например: присваивание хосту дополнительного IP-адреса (безо всякой
виртуализации, командой +ip addr add+) и привязка слушающих сокетов к конкретным
адресам (в параметре +ListenStream=+ сокет-файлов или в директиве
+ListenAddress+ файла +sshd_config+).}.
Пока что systemd, работающий внутри контейнера, не~знает, что делать с тем
сокетами, которые ему передает systemd хоста. Если вы попробуете подключиться к
порту 23, контейнер запустится, но сетевое соединение немедленно будет закрыто,
так как этому сокету не~соответствует пока никакой сервер. Давайте это исправим!
Настройка сокет-активации службы SSH была детально рассмотрена
\hyperref[sec:inetd]{в одной из предыдущих статей}, поэтому ограничусь
приведением содержимого необходимых для этого конфигурационных файлов. Файл
настроек для сокета
(+/srv/mycontainer/etc/systemd/system/sshd.socket+)\footnote{Прим. перев.:
Обратите внимание на разницу между файлами конфигурации сокетов на хосте и
в контейнере. Внутри контейнера используются параметры +Accept=yes+ и
+StandardInput=socket+, которые не~задействованы на хосте. Это обусловлено тем
фактом, что внутри контейнера сокет-активация производится в стиле inetd (служба
получает только один сокет, причем замкнутый на ее потоки STDIN и STDOUT), в то
время как на стороне хоста используется родной стиль активации systemd, при
котором запущенному процессу (в данном случае +systemd-nspawn+) просто
передаются открытые файловые дескрипторы (+fd+) всех сокетов. Одно из достоинств
такого подхода~--- возможность передавать сразу несколько сокетов, что позволяет
активировать внутри контейнера несколько серверов.}:
\begin{Verbatim}
[Unit]
Description=SSH Socket for Per-Connection Servers
[Socket]
ListenStream=23
Accept=yes
\end{Verbatim}
Соответствующий ему файл конфигурации службы \\
(+/srv/mycontainer/etc/systemd/system/sshd@.service+):
\begin{Verbatim}
[Unit]
Description=SSH Per-Connection Server for %I
[Service]
ExecStart=-/usr/sbin/sshd -i
StandardInput=socket
\end{Verbatim}
После чего, добавим наш сокет в зависимости к цели +sockets.target+, чтобы
systemd создавал его автоматически при загрузке контейнера\footnote{Прим.
перев.: Возиться вручную с командой +ln+ здесь совершенно необязательно. Можно
просто добавить в файл +sshd.socket+ секцию +[Install]+, содержащую параметр
+WantedBy=sockets.target+, после чего добавление правильного симлинка будет
выполняться куда более очевидной командой
+systemctl --root=/srv/mycontainer enable sshd.socket+.}:
\begin{Verbatim}
$ chroot /srv/mycontainer ln -s /etc/systemd/system/sshd.socket \
> /etc/systemd/system/sockets.target.wants/
\end{Verbatim}
Собственно, все. После того, как мы запустим на хосте юнит +mycontainer.socket+,
systemd начнет прослушивать TCP-порт 23. При подключении к этому порту, systemd
запустит контейнер и передаст сокет ему. Внутри контейнера свой systemd, в
соответствии с файлом +sshd.socket+, примет этот сокет и запустит для нашего
соединения экземпляр +sshd@.service+, что позволит нам залогиниться в контейнер
по SSH.
Если нам нужно запуститьунтри контейнера другие службы с сокет-активацией, мы
можем добавить в +mycontainer.socket+ дополнительные сокеты. Все они будут
прослушиваться, обращение к любому из них приведет к активации контейнера, и все
они будут переданы контейнеру при его активации. Внутри контейнера они будут
обработаны соответствии с настройками имеющихся там сокет-юнитов. Те сокеты, для
которых соответствующих юнитов не~найдется, будут закрыты, а те сокеты, которые
будут настроены для прослушивания внутри контейнера, но не~получены от хоста,
будут активированы и доступны изнутри контейнера (а если это сетевые или
файловые unix-сокеты, то и извне).
Итак, давайте отступим чуть назад и полюбуемся на результаты наших трудов. Что
мы получили в итоге? Возможность настраивать на одном хосте множество
контейнеров с полноценными ОС внутри, причем контейнеры запускаются только по
запросу, что позволяет снизить потребление системных ресурсов и, соответственно,
увеличить количество контейнеров (по сравнению с принудительной их активацией
при загрузке хоста).
Разумеется, описанный подход работает только для контейнерной виртуализации, и
неприменим к полной, т.е. может быть использован только с технологиями наподобие
libvirt-lxc или nspawn, но не~c qemu/kvm или xen.
Если вы будете администрировать несколько таких контейнеров, вас наверняка
порадует одна из возможностей journal: при запуске на хосте утилиты +journalctl+
с ключом +-m+, она автоматически обнаружит журналы гостевых контейнеров и
объединит их вывод с выводом журнала хоста\footnote{Прим. перев.: Этот трюк
работает благодаря опции +-j+, которую мы передаем программе +systemd-nspawn+
при запуске контейнера (см. файл юнита выше). В соответствии с ней, в каталоге
+/var/log/journal+ хоста создается символьная ссылку на соответствующий каталог
гостя. При этом, так как сообщения от гостей имеют другие machine ID, journalctl
хоста не~выводит их, если явно не~указать +-m+. Подробности см. на страницах
руководства
\href{http://www.freedesktop.org/software/systemd/man/systemd-nspawn.html}{systemd-nspawn(1)}
и
\href{http://www.freedesktop.org/software/systemd/man/journalctl.html}{journalctl(1)}.}.
Ловко, не~правда ли?
\end{document}
vim:ft=tex:tw=80:spell:spelllang=ru