Compare commits

...

6 Commits
v13.4 ... v15.0

Author SHA1 Message Date
nnz1024
5ffca4f1be Version v15.0 (2013-04-01 00:24) [AUTO] 2017-08-17 23:05:40 +03:00
nnz1024
751d5d1bae Version v14.4 (2013-03-08 22:32) [AUTO] 2017-08-17 23:05:40 +03:00
nnz1024
237904f0df Version v14.3 (2013-01-25 00:28) [AUTO] 2017-08-17 23:05:40 +03:00
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

802
s4a.tex
View File

@@ -14,7 +14,7 @@
\hypersetup{pdftitle={systemd для администраторов},%
pdfauthor={Lennart Poettering, Sergey Ptashnick}}
% Не засоряем оглавление подразделами
\setcounter{tocdepth}{1}
%\setcounter{tocdepth}{1} % А почему бы и нет?
% Несколько сокращений
\newcommand{\sectiona}[1]{\section*{#1}\addcontentsline{toc}{section}{#1}}
\newcommand{\hreftt}[2]{\href{#1}{\texttt{#2}}}
@@ -48,7 +48,8 @@ pdfauthor={Lennart Poettering, Sergey Ptashnick}}
\href{http://creativecommons.org/licenses/by-sa/3.0/legalcode}{CC-BY-SA 3.0
Unported}}
\maketitle
\tableofcontents%\newpage
\tableofcontents
\newpage
\sectiona{Предисловие автора}
Многие из вас, наверное, уже знают, что
\href{http://www.freedesktop.org/wiki/Software/systemd}{systemd}~--- это новая
@@ -67,7 +68,7 @@ Unported}}
Большинство этих возможностей можно описать легко и просто, и подобные статьи
должны быть интересны довольно широкой аудитории. Однако, время от времени мы
будем рассматривать ключевые новшества systemd, что может потребовать несколько
более подробного изложения.
более подробного изложения.
\begin{flushright}
Lennart Poettering, 23 августа 2010~г.
\end{flushright}
@@ -419,7 +420,12 @@ alias psc='ps xawf -eo pid,user,cgroup,args'
Альтернативный способ получить ту же информацию~--- воспользоваться утилитой
+systemd-cgls+, входящей в комплект поставки systemd. Она отображает иерархию
контрольных групп в виде псевдографической диаграммы-дерева:
контрольных групп в виде псевдографической диаграммы-дерева\footnote{Прим.
перев.: Стоит заметить, что в нижеприведенном листинге используется
ASCII-псевдографика. Между тем, уже довольно давно отображение иерархии
процессов в +systemd-cgls+ и +systemctl status+ производится в более
выразительной ├юникодной └псевдографике. ASCII-вариант выводится только в том
случае, если консоль не~поддерживает Unicode.}:
\begin{landscape}
\begin{Verbatim}[fontsize=\small]
@@ -1117,6 +1123,7 @@ Apache, crond, atd, которые по роду служебной деятел
+ln+.
\section{Смена корня}
\label{sec:chroots}
Практически все администраторы и разработчики рано или поздно встречаются с
\href{http://linux.die.net/man/1/chroot}{chroot-окружениями}. Системный вызов
@@ -2126,7 +2133,7 @@ systemd?
позволяющей прочитать из файла набор переменных окружения, который будет
установлен при запуске службы. Если же для задания настроек вам необходим
полноценный язык программирования~--- ничто не~мешает им воспользоваться.
Например, вы можете создайть в +/usr/lib/<your package>/+ простой скрипт,
Например, вы можете создать в +/usr/lib/<your package>/+ простой скрипт,
который включает соответствующие файлы, а затем запускает бинарник демона через
+exec+. После чего достаточно просто указать этот скрипт в опции +ExecStart=+
вместо бинарника демона.
@@ -2311,6 +2318,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 +2620,7 @@ systemd. Но, конечно, будет лучше, если этим займ
приложения, или сопровождающие вашего дистрибутива.
\section{К вопросу о безопасности}
\label{sec:security}
Одно из важнейших достоинств Unix-систем~--- концепция разделения привилегий
между различными компонентами ОС. В частности, службы обычно работают от имени
@@ -3129,7 +3138,7 @@ URI, ссылающиеся на документацию, формируютс
Да, кстати: если вас интересует общий обзор процесса загрузки systemd, то вам
стоит обратить внимание на
\href{http://www.freedesktop.org/software/systemd/man/bootup.html}{новую
страницу руководства}, где представлена псевгдорафическая потоковая диаграмма,
страницу руководства}, где представлена псевдографическая потоковая диаграмма,
описывающая процесс загрузки и роль ключевых юнитов.
\section{Сторожевые таймеры}
@@ -3453,7 +3462,7 @@ getty. Таким образом, вам достаточно лишь прав
консоль, то по завершении загрузки он увидит на этой консоли приглашение для
логина\footnote{Отметим, что getty, а точнее, +agetty+ на такой консоли
вызывается с параметром +-s+, и поэтому не~изменяет настроек символьной
скорость (baud rate)~--- сохраняется то значение, которое было указано в строке
скорости (baud rate)~--- сохраняется то значение, которое было указано в строке
параметров ядра.}. Кроме того, systemd выполняет поиск консолей, предоставляемых
системами виртуализации, и запускает +serial-getty@.service+ на первой из этих
консолей (+/dev/hvc0+, +/dev/xvc0+ или +/dev/hvsi0+). Такое поведение
@@ -3625,7 +3634,10 @@ Journal был включен в Fedora начиная с F17. В Fedora~18 jour
большинства лог-файлов. При этом авторы четко разделяют полномочия групп +adm+ и
+wheel+: если последняя используется для предоставления прав \emph{изменять}
что-либо в системе, то первая дает возможность лишь \emph{просматривать}
системную информацию.}:
системную информацию. Начиная с версии systemd 198, группа-владелец файлов
журнала изменена с +adm+ на +systemd-journal+, однако штатный алгоритм установки
systemd все равно выдает права на их чтение группам +adm+ и +wheel+ (через
ACL).}:
\begin{Verbatim}
$ journalctl
\end{Verbatim}
@@ -3801,7 +3813,7 @@ $ journalctl -F _SYSTEMD_UNIT
однако, начиная с релиза systemd 196, аналогичная функциональность доступна и
для zsh.}! Это же просто прекрасно~--- вы можете просмотреть перечень значений
поля и выбрать из него нужно прямо при вводе выражения. Возьмем для примера
метки SELinux. Помнится, имя поле начиналось с букв SE\ldots{}
метки SELinux. Помнится, имя поля начиналось с букв SE\ldots{}
\begin{Verbatim}[commandchars=\\\{\}]
$ journalctl _SE\textbf{<TAB>}
\end{Verbatim}
@@ -3953,16 +3965,32 @@ CPUShares=1500
\end{Verbatim}
Первая строка обеспечивает включение в нашу конфигурацию файла с настройками по
умолчанию, сделанными разработчиками Apache или его сопровождающими в вашем
дистрибутиве (если это включение не~указать явно, данный файл будет проигнорирован).
Далее, мы указываем тот параметр, который хотим изменить. Сохраняем файл,
приказываем systemd перечитать конфигурацию, и перезапускаем Apache, чтобы
настройки вступили в силу\footnote{Прим. перев.: К сожалению, в настоящее время
systemd не~поддерживает изменение параметров контрольных групп без перезапуска
службы. Но вы можете узнать контрольную группу службы командой наподобие
+systemctl show -p ControlGroup avahi-daemon.service+, и выполнить настройки
любым удобным для вас способом, например, через запись значений в псевдофайлы
cgroupfs. Разумеется, при следующем запуске службы к ней будут применены
параметры, указанные в конфигурационном файле.}:
дистрибутиве (если это включение не~указать явно, данный файл будет
проигнорирован). Далее, мы указываем тот параметр, который хотим
изменить\footnote{Прим. перев.: В новых версиях systemd, начиная со 198, можно
поступить проще: вместо того, чтобы создавать свой юнит-файл в каталоге +/etc+ и
включать в него содержимое штатного (из +/usr+), достаточно просто создать
каталог +/etc/systemd/system/httpd.service.d/+ и поместить в него файл
+my_resource_limit.conf+ (суффикс +.conf+ обязателен), в котором указываются
только те настройки, которые необходимо изменить (последние две строки
вышеприведенного листинга). Это никак не~отменяет основного конфигурационного
файла юнита (который может находиться в +/usr+ или +/etc+), однако настройки из
+.d+-каталогов имеют более высокий приоритет и могут перекрывать настройки
основного файла. Все вышесказанное относится и к другим примерам из этого
раздела.}. Сохраняем файл, приказываем systemd перечитать конфигурацию, и
перезапускаем Apache, чтобы настройки вступили в силу\footnote{Прим. перев.:
systemd версий до 197 включительно, не~поддерживает изменение параметров
контрольных групп без перезапуска службы. Но вы можете узнать контрольную группу
службы командой наподобие +systemctl show -p ControlGroup avahi-daemon.service+,
и выполнить настройки любым удобным для вас способом, например, через запись
значений в псевдофайлы cgroupfs (разумеется, при следующем запуске службы к ней
будут применены параметры, указанные в конфигурационном файле). Начиная с
systemd 198, в программу +systemctl+ добавлена поддержка команд
+set-cgroup-attr+, +unset-cgroup-attr+ и +get-cgroup-attr+, позволяющих
манипулировать практически всеми параметрами контрольных групп. При этом
поддерживается сохранение установленных таким образом параметров в виде
вспомогательных конфигурационных файлов в +.d+-каталогах (см. примечание
выше).}:
\begin{Verbatim}
systemctl daemon-reload
systemctl restart httpd.service
@@ -4139,6 +4167,742 @@ ControlGroupAttribute=memory.swappiness 70
и они не~так сильно ухудшают производительность. Возможно, мы рассмотрим их в
последующих статьях.
\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 (который
вновь запустит ее при поступлении очередного запроса). При этом, со стороны
клиента невозможно отличить, когда служба запущена, а когда~--- нет. Сокет
постоянно остается открытым для входящих соединений, и все они обрабатываются
быстро и корректно.
Такая конфигурация позволяет снизить потребление системных ресурсов: службы
работают и потребляют ресурсы только тогда, когда это действительно необходимо.
Многие интернет-сайты и службы могут использовать это с выгодой для себя.
Например, хостеры веб-сайтов знают, что из огромного количества существующих в
Интернете сайтов лишь малая часть получает непрерывный поток запросов.
Большинство же сайтов, хотя и должны постоянно оставаться доступными, получают
запросы очень редко. Используя сокет-активацию, вы можете воспользоваться этим:
разместив множество таких сайтов на одной системе и активируя их службы только
при необходимости, вы получаете возможность <<оверкоммита>>: ваша система будет
обслуживать сайтов больше, чем формально позволяют ее ресурсы\footnote{Прим.
перев.: Стоит отметить, что подобная схема работает только при условии, что для
каждого клиентского сайта запускаются отдельные процессы служб, хотя это и
происходит в рамках одного хоста. Не~очень распространенный в отчественном
хостинге вариант: обычно следующей опцией после shared-хостинга (одна служба на
всех клиентов) идет VPS (каждому клиенту по виртуальному хосту).}. Разумеется,
увлекаться оверкоммитом не~стоит, иначе в моменты пиковой нагрузки ресурсов
может действительно не~хватить.
С помощью 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]{внушительный арсенал}
механизмов обеспечения безопасности и разграничения доступа, который позволит
изолировать клиентские сайты друг от друга (например, службы каждого клиента
будут видеть только его собственный домашний каталог, в то время как каталоги
всех остальных пользователей будут им недоступны). Итак, в конечном итоге вы
получаете надежную и масштабируемую серверную систему, на сравнительно небольших
ресурсах которой функционирует множество безопасно изолированных друг от друга
служб~--- и все это реализовано штатными возможностями вашей ОС\footnote{Прим.
перев.: В качестве практического примера использования сокет-активации служб
systemd в промышленных серверных платформах, можно предложить
\href{http://savanne.be/articles/deploying-node-js-with-systemd/}{вот эту
статью}, наглядно описывающую применение этой и других технологий (мониторинг,
использование Journal, ограничение ресурсов и доступа) на примере Node.js.}.
Подобные конфигурации уже используются на рабочих серверах ряда компаний. В
частности, специалисты из \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+ дополнительные сокеты. Все они будут
прослушиваться, обращение к любому из них приведет к активации контейнера, и все
эти сокеты будут переданы контейнеру при его активации. Внутри контейнера они
будут обработаны соответствии с настройками имеющихся там сокет-юнитов. Те
сокеты, для которых соответствующих юнитов не~найдется, будут
закрыты\footnote{Прим. перев.: Стоит особо отметить, что описанная технология
работает только для служб, поддерживающих сокет-активацию в режимах inetd (все
классические inetd-службы, кроме встроенных) или systemd (зависят от библиотеки
+libsystemd-daemon.so+, либо содержат в исходниках заголовочный файл
+sd-daemon.h+). Службы, которые сами открывают себе слушающий сокет и
не~содержат кода для приема уже открытого сокета, так активировать нельзя.}, а
те сокеты, которые будут настроены для прослушивания внутри контейнера, но
не~получены от хоста, будут активированы и доступны изнутри контейнера (а если
это сетевые или файловые 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)}.}.
Ловко, не~правда ли?
Необходимый минимум технологий для сокет-активации контейнеров, присутствует в
systemd, начиная с версии 197. Тем не~менее, наша работа в этой области еще
не~закончена, и в ближайшее время мы планируем доработать некоторые моменты.
Например, сейчас, даже если все серверные службы внутри контейнера
закончили обработку запросов и завершились, контейнер все равно продолжает
функционировать и потреблять ресурсы хоста. Мы просто обязаны реализовать
возможность автоматического завершения работы гостевой системы в такой ситуации.
Причем у нас уже есть готовые наработки в этой области~--- мы можем
задействовать уже существующую инфраструктуру, обеспечивающую автоматическое
засыпание/выключение ноутбука при отсутствии активных задач и пользователей.
Впрочем, пора закругляться, а то статья получается чересчур длинной. Надеюсь,
что вы смогли продраться через все эти длинные и скучные рассуждения о
виртуализации, сокетах, службах, различных ОС и прочем колдунстве. Также
надеюсь, что эта статья станет хорошей отправной точкой при конфигурировании
мощных и хорошо масштабируемых серверных систем. За дополнительной информацией
обращайтесь к документации или приходите на наш IRC-канал. Спасибо за внимание!
\appendix
\section{Диагностика проблем при работе с systemd}
\subsection{Диагностика проблем с загрузкой}
Если система зависает во время загрузки, прежде всего нужно разобраться, на
каком этапе возникает проблема~--- до запуска systemd, или после.
Для этого надо удалить из командной стоки ядра параметры +quiet+ и +rhgb+. При
работе systemd на экран выводятся примерно такие сообщения:
\begin{Verbatim}[commandchars=\\\{\}]
Welcome to \textcolor{blue}{Fedora \emph{ВЕРСИЯ} (\emph{имя релиза})}!
Starting \emph{название}...
[ \textcolor{green}{OK} ] Stared \emph{название}...
\end{Verbatim}
(Пример можно посмотреть на
\href{http://freedesktop.org/wiki/Software/systemd/Debugging?action=AttachFile&do=view&target=f17boot.png}{скриншоте}.)
Если у вас есть доступ к оболочке, это значительно упрощает диагностику и
решение проблем. В том случае, когда до приглашения входа в систему дело так и
не~доходит, попробуйте переключиться на другую виртуальную консоль, нажав
CTRL--ALT--F<\emph{цифра}>. Дело в том, что при проблемах, связанных с запуском
X-сервера, может возникать ситуация, когда на первой консоли (+tty1+)
приглашение ко входу отсутствует, но все остальные консоли при этом работают
нормально.
Если ни~на одной из виртуальных консолей приглашение так и не~появилось~---
попробуйте выждать еще \emph{порядка 5 минут}. Только после этого можно
будет утверждать, что процесс загрузка завис окончательно. Если подвисание
обусловлено сбоем при запуске какой-то службы, то после истечения
тайм-аута проблемная служба будет убита, и загрузка может продолжиться. Другой
вариант~--- отсутствует устройство, которое должно быть смонтировано для
нормальной работы системы. В этом случае загрузка будет остановлена, и система
перейдет в \emph{аварийный режим (emergency mode)}.
\subsubsection{Если у вас нет~доступа к оболочке}
Если система не~предоставила вам ни~нормального приглашения, ни~аварийной
оболочки, то для диагностики проблемы нужно выполнить следующие действия:
\begin{itemize}
\item Попытайтесь перезагрузить систему, нажав CTRL--ALT--DEL. Если после
этого перезагрузки не~произойдет~--- обязательно укажите данное
обстоятельство, когда будете писать отчет об ошибке (bugreport).
Чтобы перезагрузить систему, вы можете воспользоваться
\href{http://fedoraproject.org/wiki/QA/Sysrq}{SysRq} или
<<аппаратным>> методом.
\item При следующей загрузке попробуйте воспользоваться некоторыми
нижеописанными стратегиями.
\end{itemize}
\begin{description}
\item[Вывод диагностических сообщений на последовательную консоль]%
\hypertarget{it:serial}{} Если у вас под рукой есть терминал
последовательной консоли, либо дело происходит в виртуальной машине (в
частности, virt-manager позволяет просматривать вывод виртуальной машины
на последовательную консоль: меню Вид (View)~$\Rightarrow$ Текстовые
консоли (Text Consoles)), вы можете попросить systemd выводить на эту
консоль подробную отладочную информацию о ходе загрузки, добавив к
параметрам ядра следующие аргументы:
\begin{Verbatim}
systemd.log_level=debug systemd.log_target=console console=ttyS0,38400
\end{Verbatim}
\item[Загрузка в восстановительном (rescue) или аварийном (emergency)
режимах] Чтобы загрузиться в восстановительном режиме, добавьте к
параметрам ядра +systemd.unit=rescue.target+, или просто +1+. Это режим
эффективен для решения проблем, возникающих на этапе запуска обычных
служб, когда ключевые компоненты системы уже инициализированы. В такой
ситуации, вы можете просто отключить проблемную службу. Если же загрузка
не~доходит даже до восстановительного режима~--- попробуйте менее
требовательный, аварийный режим.
Для загрузки напрямую в режим аварийной оболочки, добавьте к параметрам
ядра +systemd.unit=emergency.target+, или просто +emergency+. Обратите
внимание, что в аварийном режиме корневая система по умолчанию
монтируется в режиме <<только для чтения>>, поэтому перед
восстановительными работами, связанными с записью на диск, необходимо
перемонтировать ее в режиме <<для чтения и записи>>:
\begin{Verbatim}
mount -o remount,rw /
\end{Verbatim}
Как правило, аварийная оболочка используется для исправления
некорректных записей в +/etc/fstab+. После внесения необходимых
изменений, скомандуйте +systemctl daemon-reload+, чтобы systemd увидел
ваши исправления.
Если не~работает даже аварийный режим, попробуйте загрузиться напрямую в
оболочку, добавив к параметрам ядра +init=/bin/sh+. Такая ситуация может
возникнуть вследствие повреждения бинарного файла systemd, либо
библиотек, которые он использует. В этом случае может помочь
переустановка соответствующих пакетов.
Если не~срабатывает даже +init=/bin/sh+, остается лишь попробовать
загрузиться с другого носителя.
\item[Отладочная оболочка]\hypertarget{it:dbgshell}{}
Вы можете включить специальную отладочную оболочку, которая запускается
в отдельной консоли на раннем этапе загрузки и позволяет собрать
необходимую диагностическую информацию, а также провести
восстановительные операции. Для включения отладочной оболочки
скомандуйте
\begin{Verbatim}
systemctl enable debug-shell.service
\end{Verbatim}
\textbf{Совет:} Если вы используете старую версию systemd, в которой еще
не~реализована поддержка отладочной оболочки, вы можете загрузить
соответствующий файл конфигурации юнита из
\href{http://cgit.freedesktop.org/systemd/systemd/plain/units/debug-shell.service.in}{git-репозитария
systemd}. Перед использованием этого файла, замените в нем +@sushell@+
на +/bin/bash+.
\textbf{Совет:} Если вы не~можете воспользоваться командой +systemctl+
(например, загрузились с помощью другой операционной системы), вы можете
выполнить соответствующие действия и напрямую:
\begin{Verbatim}
cd $ПУТЬ_К_ВАШЕМУ_КОРНЮ/etc/systemd/system
mkdir -p sysinit.target.wants
ln -s /lib/systemd/system/debug-shell.service sysinit.target.wants/
\end{Verbatim}
Отладочная оболочка будет запущена с правами +root+ на консоли +tty9+
при следующей загрузке системы. Чтобы переключиться на нее, нажмите
CTRL--ALT--F9. Оболочка запускается на самом раннем этапе загрузки и
позволяет вам проверять состояние служб, читать системные журналы,
выявлять зависшие задачи (командой +systemctl list-jobs+) и т.д.
\textbf{Предупреждение:} Используйте эту оболочку только для отладки!
Не~забудьте отключить ее после того, как разберетесь с проблемами.
Оставлять доступную всем и каждому оболочку с правами +root+, мягко
говоря, небезопасно.
\item[Проверка параметров ядра] Для корректной загрузки системы необходимо,
чтобы каталог +/dev+ был заполнен, хотя бы частично. Убедитесь, что ядро
Linux собрано с опциями +CONFIG_DEVTMPFS+ и +CONFIG_DEVTMPFS_MOUNT+.
Кроме того, для корректной работы systemd рекомендуется включить
поддержку контрольных групп и fanotify (опции +CONFIG_CGROUPS+ и
+CONFIG_FANOTIFY+ соответственно). Отключение этих опций может привести
к появлению сообщений об ошибках вида <<Failed to get D-Bus
connection: No connection to service manager.>> при попытке запуска
+systemctl+.
\end{description}
\subsubsection{Если у вас есть доступ к оболочке}
Если вам все-таки удалось получить доступ к оболочке системы, вы можете
воспользоваться ею для сбора диагностической информации. Загрузите систему со
следующими параметрами ядра:
\begin{Verbatim}
systemd.log_level=debug systemd.log_target=kmsg log_buf_len=1M
\end{Verbatim}
В соответствии с ними, systemd будет выводить максимально подробные сообщения о
процессе загрузки, и направлять их в кольцевой буфер ядра (последний параметр
обеспечивает соответствующее увеличение размера буфера). Дождавшись запуска
оболочки, сохраните полученный журнал:
\begin{Verbatim}
dmesg > dmesg.txt
\end{Verbatim}
Отправляя отчет об ошибке, присоедините к нему полученный файл +dmesg.txt+.
Также, вы можете просмотреть список операций, чтобы выявить зависшие задачи:
\begin{Verbatim}
systemctl list-jobs
\end{Verbatim}
Операции, находящиеся в состоянии <<waiting>>, будут запущены на исполнение
только после того, как завершатся операции, выполняемые в данный момент
(состояние <<running>>).
\subsection{Диагностика проблем с выключением системы}
При зависании системы во время выключения, как и в случае с загрузкой,
рекомендуется подождать \emph{минимум 5 минут}, чтобы отличить полное зависание
системы от временного подвисания из-за проблем с отдельными службами. Также
стоит проверить, реагирует ли система на нажатие CTRL--ALT--DEL.
Если процесс остановки системы (при выключении или перезагрузке) зависает
полностью, прежде всего нужно убедиться, способно ли ядро Linux выключить или
перезагрузить систему. Для этого воспользуйтесь одной из команд:
\begin{Verbatim}
sync && reboot -f
sync && poweroff -f
\end{Verbatim}
Если хотя бы одна из этих команд не~сработает~--- значит, проблема не~в systemd,
а в ядре.
\subsubsection{Система очень долго выключается}
Если ваша система все же может выключиться/перезагрузиться, но этот процесс
длится подозрительно долго, выполните нижеописанные операции:
\begin{itemize}
\item Загрузите систему со следующими параметрами ядра:
\begin{Verbatim}
systemd.log_level=debug systemd.log_target=kmsg log_buf_len=1M enforcing=0
\end{Verbatim}
\item Создайте файл +/lib/systemd/system-shutdown/debug.sh+, добавьте
ему право на запуск и запишите в него следующие строки:
\begin{Verbatim}
#!/bin/sh
mount -o remount,rw /
dmesg > /shutdown-log.txt
mount -o remount,ro /
\end{Verbatim}
\item Перезагрузите систему.
\end{itemize}
После этого вы можете самостоятельно проанализировать файл +/shutdown-log.txt+,
и/или присоединить его к вашему сообщению об ошибке.
\subsubsection{Система не~может выключиться самостоятельно}
Если процесс выключения или перезагрузки вашей системы не~завершается даже через
несколько минут, и вышеописанный метод с +shutdown-log+ не~сработал, вы можете
собрать диагностическую информацию другими методами (которые мы уже
рассматривали применительно к проблемам загрузки):
\begin{itemize}
\item Используйте \hyperlink{it:serial}{последовательную консоль}.
\item Воспользуйтесь \hyperlink{it:dbgshell}{отладочной оболочкой}~---
она полезна не~только на ранних стадиях загрузки, но и на
поздних стадиях остановки системы.
\end{itemize}
\subsection{Просмотр состояния службы и ее журнала}
Когда при запуске службы происходит сбой, systemd выводит весьма абстрактное
сообщение об ошибке:
\begin{Verbatim}
# systemctl start foo.service
Job failed. See system journal and 'systemctl status' for details.
\end{Verbatim}
При этом сама служба может выводить собственное сообщение, но вы (пока что) его
не~видите. Дело в том, что запуск служб происходит не~из вашей оболочки, а из
процесса systemd, и поэтому вывод программы не~привязан к вашей консоли. Тем
не~менее, это вовсе не~означает, что выводимые сообщения теряются. По умолчанию,
потоки STDOUT и STDERR, принадлежащие запускаемым службам, перенаправляются в
системный журнал (journal). Туда же попадают и сообщения, отправляемые с помощью
функции +syslog(3)+. Кроме того, systemd записывает код выхода сбойных
процессов. Посмотреть собранные данные можно, например, так:
\begin{Verbatim}
# systemctl status foo.service
foo.service - mmm service
Loaded: loaded (/etc/systemd/system/foo.service; static)
Active: failed (Result: exit-code) since Fri, 11 May 2012 20:26:23 +0200; 4s ago
Process: 1329 ExecStart=/usr/local/bin/foo (code=exited, status=1/FAILURE)
CGroup: name=systemd:/system/foo.service
May 11 20:26:23 scratch foo[1329]: Failed to parse config
\end{Verbatim}
В нашем примере, служба запустилась как процесс с идентификатором (PID) 1329,
после чего завершилась с кодом выхода 1. Если вы запустили команду
+systemctl status+ от имени пользователя +root+, либо от имени пользователя,
входящего в группу +adm+, вы также увидите последние несколько строчек,
записанные службой в журнал. В нашем примере служба выдала всего одно сообщение
(<<Failed to parse config>>).
Чтобы просмотреть весь журнал целиком, воспользуйтесь командой +journalctl+.
Если одновременно с journal вы используете и классический демон системного лога
(например, rsyslog), то все сообщения из журнала будут переданы также и этому
демону, который запишет их в традиционные лог-файлы (в какие именно~--- зависит
от его настроек, обычно +/var/log/messages+).
\subsection{Подготовка сообщений об ошибках}
Если вы собираетесь отправить сообщение об ошибке в systemd, пожалуйста,
включите в него диагностическую информацию, в частности, содержимое системных
журналов. Журналы должны быть полными (без вырезок), не~заархивированными, с
MIME-типом +text/plain+.
Прежде всего, отправьте сообщение в багтрекер своего дистрибутива. Если же вы
твердо уверены, что проблема именно в апстримном systemd, проверьте сначала
список
\href{https://bugs.freedesktop.org/buglist.cgi?query_format=advanced&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&product=systemd}{уже
известных ошибок}. Если вы не~найдете в нем своего случая~---
\href{https://bugs.freedesktop.org/enter_bug.cgi?product=systemd}{заводите новую
запись}.
\subsubsection{Что нужно включить в сообщение об ошибке}
По возможности, пожалуйста, укажите в самом сообщении, либо присоедините к нему,
следующую информацию:
\begin{itemize}
\item Строку параметров ядра, если она отличается от значения по
умолчанию. Ее можно найти в файле конфигурации загрузчика
(например, +/boot/grub2/grub.cfg+) или в специальном файле
+/proc/cmdline+.
\item Копию файла +/var/log/messages+.
\item Файл +dmesg.txt+, полученный после выполнения команды
+dmesg > dmesg.txt+ (перед ее выполнением лучше загрузиться с
параметрами ядра
+systemd.log_level=debug systemd.log_target=kmsg log_buf_len=1M+.
\item Файл +systemd-dump.txt+, полученный в результате выполнения
команды +systemctl dump > systemd-dump.txt+.
\item Файл +systemd-test.txt+, полученный при помощи команды
+/usr/bin/systemd --test --system --log-level=debug > systemd-test.txt 2>&1+.
\end{itemize}
\end{document}
vim:ft=tex:tw=80:spell:spelllang=ru