Compare commits

...

6 Commits
v2.0 ... v5.2

Author SHA1 Message Date
nnz1024
56e266c439 Version v5.2 (2011-05-20 19:49) [AUTO] 2017-08-17 23:05:37 +03:00
nnz1024
d7012437df Version v5.1 (2011-05-14 18:48) [AUTO] 2017-08-17 23:05:37 +03:00
nnz1024
c24887c795 Version v5.0 (2011-05-14 17:53) [AUTO] 2017-08-17 23:05:37 +03:00
nnz1024
15e9a095a1 Version v4.0 (2011-04-29 20:05) [AUTO] 2017-08-17 23:05:37 +03:00
nnz1024
01f2aafc02 Version v3.0 (2011-03-22 17:42) [AUTO] 2017-08-17 23:05:37 +03:00
nnz1024
71b6656ebb Version v2.1 (2011-01-11 14:55) [AUTO] 2017-08-17 23:05:37 +03:00

633
s4a.tex
View File

@@ -391,8 +391,8 @@ $ ps xawf -eo pid,user,cgroup,args
Обратите внимание на третий столбец, показывающий имя контрольной группы,
которое systemd присваивает каждому процессу. Например, процесс +udev+
находится в группе +name=systemd:/systemd-1/sysinit.service+. В этой группе
находятся процессы, порожденные службой +sysinit.service+, которая запускается
находится в группе +name=systemd:/systemd-1/sysinit.service+. В эту группу
входят процессы, порожденные службой +sysinit.service+, которая запускается
на ранней стадии загрузки.
Вы можете очень сильно упростить себе работу, если назначите для
@@ -836,7 +836,7 @@ systemd запустит процесс abrtd, который уже не~буд
падения. Или, например, добавив +OOMScoreAdjust=-500+, мы попросим ядро сберечь
эту службу, даже если OOM Killer выйдет на тропу войны. А если мы добавим
строчку +CPUSchedulingPolicy=idle+, процесс abrtd будет работать только в те
моменты, когда система больше ничем не~занята, что позволит не создавать помех
моменты, когда система больше ничем не~занята, что позволит не~создавать помех
для процессов, активно использующих CPU.
За более подробным описанием всех опций настройки, вы можете обратиться к
@@ -859,7 +859,7 @@ service-файлы. Но, к счастью, <<проблемных>> скрип
Впрочем, этот метод не~вполне корректен, так как он действует не~только на
самого демона, но и на другие процессы с тем же именем. Иногда подобное
поведение может привести к неприятным последствиям. Более правильным будет
использование pid-файла: +kill $(cat /var/run/syslogd.pid)+. Вот, вроде
использование pid-файла: +kill $(cat /var/run/syslogd.pid)$+. Вот, вроде
бы, и все, что вам нужно\ldots{} Или мы упускаем еще что-то?
Действительно, мы забываем одну простую вещь: существуют службы, такие, как
@@ -946,4 +946,629 @@ systemd и на этот случай есть простое решение, и
инструментов, позволяющих корректно отправить сигнал службе в целом, а
не~отдельному процессу.
\section{Три уровня выключения}
В \href{http://www.freedesktop.org/wiki/Software/systemd}{systemd} существует
три уровня (разновидности) действий, направленных на прекращение работы службы
(или любого другого юнита):
\begin{itemize}
\fvset{gobble=3}
\item Вы можете \emph{остановить} службу, то есть прекратить
выполнение уже запущенных процессов службы. При этом
сохраняется возможность ее последующего запуска, как ручного
(через команду +systemctl start+), так и автоматического (при
загрузке системы, при поступлении запроса через сокет или
системную шину, при срабатывании таймера, при подключении
соответствующего оборудования и т.д.). Таким образом,
остановка службы является временной мерой, не~дающей никаких
гарантий на будущее.
В качестве примера рассмотрим остановку службы NTPd
(отвечающей за синхронизацию времени по сети):
\begin{Verbatim}
systemctl stop ntpd.service
\end{Verbatim}
Аналогом этой команды в классическом SysV init является
\begin{Verbatim}
service ntpd stop
\end{Verbatim}
Заметим, что в Fedora~15, использующей в качестве системы
инициализации systemd, в целях обеспечения обратной
совместимости допускается использование классических
SysV-команд, и systemd будет корректно воспринимать их.
В~частности, вторая приведенная здесь команда будет эквивалентна
первой.
\item Вы можете \emph{отключить} службу, то есть отсоединить ее от всех
триггеров активации. В результате служба уже не~будет
автоматически запускаться ни~при загрузке системы, ни~при
обращении к сокету или адресу на шине, ни~при подключении
оборудования, и т.д. Но при этом сохраняется возможность
<<ручного>> запуска службы (командой +systemctl start+).
Обратите внимание, что при отключении уже запущенной службы, ее
выполнение в текущем сеансе не~останавливается~--- это нужно
сделать отдельно, иначе процессы службы будут работать до
момента выключения системы (но при следующем включении,
разумеется, уже не~запустятся).
Рассмотрим отключение службы на примере все того же NTPd:
\begin{Verbatim}
systemctl disable ntpd.service
\end{Verbatim}
В классических SysV-системах аналогичная команда будет иметь
вид
\begin{Verbatim}
chkconfig ntpd off
\end{Verbatim}
Как и в предыдущем случае, в Fedora~15 вторая из этих команд
будет действовать аналогично первой.
Довольно часто приходится сочетать действия отключения и
остановки службы~--- такая комбинированная операция
гарантирует, что уже исполняющиеся процессы службы будут
прекращены, и служба больше не~будет запускаться автоматически
(но может быть запущена вручную):
\begin{Verbatim}
systemctl disable ntpd.service
systemctl stop ntpd.service
\end{Verbatim}
Подобное сочетание команд используется,
например, при деинсталляции пакетов в Fedora.
Обратите внимание, что отключение службы является перманентной
мерой, и действует вплоть до явной отмены соответствующей
командой. Перезагрузка системы не~отменяет отключения службы.
\item Вы можете \emph{заблокировать} (замаскировать) службу. Действие
этой операции аналогично отключению, но дает более сильный
эффект. Если при отключении отменяется только возможность
автоматического запуска службы, но сохраняется возможность
ручного запуска, то при блокировке исключаются обе эти
возможности. Отметим, что использование данной опции при
непонимании принципов ее работы может привести к трудно
диагностируемым ошибкам.
Тем не~менее, рассмотрим пример блокировки все той же службы NTPd:
\begin{Verbatim}
ln -s /dev/null /etc/systemd/system/ntpd.service
systemctl daemon-reload
\end{Verbatim}
Итак, блокировка сводится к созданию символьной ссылки
с именем соответствующей службы, указывающей на +/dev/null+.
После такой операции служба не~может быть запущена ни~вручную,
ни~автоматически. Символьная ссылка создается в каталоге
+/etc/systemd/system/+, а ее имя должно соответствовать имени
файла описания службы из каталога +/lib/systemd/system/+ (в
нашем случае +ntpd.service+).
Заметим, что systemd читает файлы конфигурации из обоих этих
каталогов, но файлы из +/etc+ (управляемые системным
администратором) имеют приоритет над файлами из +/lib+ (которые
управляются пакетным менеджером). Таким образом, созание
символьной ссылки (или обычного файла)
+/etc/systemd/system/ntpd.service+ предотвращает чтение
штатного файла конфигурации +/lib/systemd/system/ntpd.service+.
В выводе +systemctl status+ заблокированные службы отмечаются
словом +masked+. Попытка запустить такие службы командой
+systemctl start+ завершится ошибкой.
В рамках классического SysV init, штатная реализация такой
возможности отсутствует. Похожий эффект может быть
достигнут с помощью <<костылей>>, например, путем добавления
команды +exit 0+ в начало init-скрипта. Однако, подобные решения
имеют ряд недостатков, например, потенциальная возможность
конфликтов с пакетным менеджером (при очередном обновлении
исправленный скрипт может быть просто затерт соответствующим
файлом из пакета).
Стоит отметить, что блокировка службы, как и ее отключение,
является перманентной мерой\footnote{Прим. перев.: подробно
описав принцип работы блокировки службы (юнита), автор забывает
привести практические примеры ситуаций, когда эта возможность
оказывается полезной.
В частности, иногда бывает необходимо
полностью предотвратить запуск службы в любой ситуации. При этом
не~стоит забывать, что в post-install скриптах пакетного
менеджера или, скажем, в~заданиях cron, вместо
+systemctl try-restart+ (+service condrestart+) может быть
ошибочно указано +systemctl restart+ (+service restart+), что
является прямым указанием на запуск службы, если она еще
не~запущена. Вследствие таких ошибок, отключенная служба может
<<ожить>> в самый неподходящий момент.
Другой пример~---
ограничение возможностей непривилегированного пользователя при
управлении системой. Даже если такому пользователю делегировано
(через механизмы sudo или PolicyKit) право на использование
+systemctl+, это еще не~означает, что он сможет запустить
заблокированную службу~--- права на выполнение +rm+ (удаление
блокирующей символьной ссылки) выдаются отдельно}.
\end{itemize}
После прочтения изложенного выше, у читателя может возникнуть вопрос: как
отменить произведенные изменения? Что ж, ничего сложного тут нет:
+systemctl start+ отменяет действия +systemctl stop+, +systemctl enable+
отменяет действие +systemctl disable+, а +rm+ отменяет действие +ln+.
\section{Смена корня}
Практически все администраторы и разработчики рано или поздно встречаются с
\href{http://linux.die.net/man/1/chroot}{chroot-окружениями}. Системный вызов
+chroot()+ позволяет задать для определенного процесса (и его потомков) каталог,
который они будут рассматривать как корневой +/+, тем самым ограничивая для них
область видимости иерархии файловой системы отдельным поддеревом. Большинство
применений chroot-окружений можно отнести к двум классам задач:
\begin{enumerate}
\item Обеспечение безопасности. Потенциально уязвимый демон chroot'ится
в отдельный каталог, и даже в случае успешной атаки, взломщик
увидит лишь содержимое этого каталога, а не~всю файловую
систему~--- он окажется в ловушке chroot'а.
\item Подготовка и управление образом операционной системы при отладке,
тестировании, компиляции, установке или восстановлении. При этом
вся иерархия файловых систем гостевой ОС монтируется или
создается в каталоге системы-хоста, и при запуске оболочки (или
любого другого приложения) внутри этой иерархии, их корень
сдвигается в этот каталог. Система, которую <<видят>> такие
программы, может сильно отличаться от ОС хоста. Например, это
может быть другой дистрибутив, или даже другая аппаратная
архитектура (запуск i386-гостя на x86\_64-хосте). Гостевая ОС
не~может увидеть полной иерархии ОС хоста.
\end{enumerate}
В системах, использующих классический SysV init, использовать chroot-окружения
сравнительно несложно. Например, чтобы запустить выбранного демона внутри
иерархии гостевой ОС, достаточно смонтировать внутри этой иерархии +/proc+,
+/sys+ и остальные API ФС, воспользоваться программой +chroot(1)+ для входа в
окружение, и выполнить соответствующий init-скрипт, запустив +/sbin/service+
внутри окружения.
Но в системах, использующих systemd, уже не~все так просто. Одно из важнейших
достоинств systemd состоит в том, что параметры среды, в которой запускаются
демоны, никак не~зависит от метода их запуска. В системах, использующих SysV
init, многие параметры среды выполнения (в частности, лимиты на системные
ресурсы, переменные окружения, и т.п.) наследуются от оболочки, из которой был
запущен init-скрипт. При использовании systemd ситуация меняется радикально:
пользователь просто уведомляет init-демона о необходимости запустить ту или иную
службу, и тот запускает демона в чистом, созданном <<с нуля>> и тщательно
настроенном окружении, параметры которого никак не~зависят от настроек среды, из
которой была отдана команда. Такой подход полностью отменяет традиционный метод
запуска демонов в chroot-окружениях: теперь демон порождается процессом init
(PID~1) и наследует корневой каталог от него, вне зависимости от того, находился
ли пользователь, отдавший команду на запуск, в chroot-окружении, или нет. Кроме
того, стоит особо отметить, что взаимодействие управляющих программ с systemd
происходит через сокеты, находящиеся в каталоге +/run/systemd+, так что
программы, запущенные в chroot-окружении, просто не~смогут взаимодействовать с
init-подсистемой (и это, в общем, неплохо, а если такое ограничение будет
создавать проблемы, его можно легко обойти, используя bind-монтирование).
В свете вышесказанного, возникает вопрос: как правильно использовать
chroot-окружения в системах на основе systemd? Что ж, постараемся дать подробный
и всесторонний ответ на этот вопрос.
Для начала рассмотрим первое из перечисленных выше применений chroot: изоляция в
целях безопасности. Прежде всего, стоит заметить, что защита, предоставляемая
chroot'ом, весьма эфемерна и ненадежна, так как chroot не~является <<дорогой с
односторонним движением>>. Выйти из chroot-окружения сравнительно несложно, и
соответствующее предупреждение даже
\href{http://linux.die.net/man/2/chroot}{присутствует на странице руководства}.
Действительно эффективной защиты можно достичь, только сочетая chroot с другими
методиками. В большинстве случаев, это возможно только при наличии поддержки
chroot в самой программе. Прежде всего, корректное конфигурирование
chroot-окружения требует глубокого понимания принципов работы программы.
Например, нужно точно знать, какие сокеты использует программа, чтобы обеспечить
bind-монтирование соответствующих каталогов. С учетом вышесказанного,
эффективный chroot-защита обеспечивается в том случае, когда она реализована в
коде самого демона. Именно разработчик лучше других знает (\emph{обязан знать}),
как правильно сконфигурировать chroot-окружение, и какой минимальный набор
файлов, каталогов и файловых систем необходим внутри для нормальной работы
демона. Уже сейчас существуют демоны, имеющие встроенную поддержку chroot.
К сожалению, в системе Fedora, установленной с параметрами по умолчанию, таких
демонов всего два: \href{http://avahi.org/}{Avahi} и RealtimeKit. Оба они
написаны одним очень хитрым человеком ;-) (Вы можете собственноручно
убедится в этом, выполнив команду +ls -l /proc/*/root+.)
Возвращаясь к тема нашего обсуждения: разумеется, systemd позволяет помещать
выбранных демонов в chroot, и управлять ими точно так же, как и другими.
Достаточно лишь указать параметр +RootDirectory=+ в соответствующем
service-файле. Например:
\begin{Verbatim}
[Unit]
Description=A chroot()ed Service
[Service]
RootDirectory=/srv/chroot/foobar
ExecStartPre=/usr/local/bin/setup-foobar-chroot.sh
ExecStart=/usr/bin/foobard
RootDirectoryStartOnly=yes
\end{Verbatim}
Рассмотрим этот пример подробнее. Параметр +RootDirectory=+ задает каталог, в
который производится chroot перед запуском исполняемого файла, заданного
параметром +ExecStart=+. Заметим, что путь к этому файлу должен быть указан
относительно каталога chroot (так что, в нашем случае, с точки зрения основной
системы, на исполнение будет запущен файл +/srv/chroot/foobar/usr/bin/foobard+).
Перед запуском демона будет вызван сценарий оболочки +setup-foobar-chroot.sh+,
который должен обеспечить подготовку chroot-окружения к запуску демона
(например, смонтировать в нем +/proc+ и/или другие файловые системы, необходимые
для работы демона). Указав +RootDirectoryStartOnly=yes+, мы задаем, что
+chroot()+ будет выполняться только перед выполнением файла из +ExecStart=+, а
команды из других директив, в частности, +ExecStartPre=+, будут иметь полный
доступ к иерархии файловых систем ОС (иначе наш скрипт просто не~сможет
выполнить bind-монтирование нужных каталогов). Более подробную информацию по
опциям конфигурации вы можете получить на
\href{http://0pointer.de/public/systemd-man/systemd.service.html}{страницах}
\href{http://0pointer.de/public/systemd-man/systemd.exec.html}{руководства}.
Поместив приведенный выше текст примера в файл
+/etc/systemd/system/foobar.service+, вы сможете запустить chroot'нутого демона
командой +systemctl start foobar.service+. Информацию о его текущем состоянии
можно получить с помощью команды +systemctl status foobar.service+. Команды
управления и мониторинга службы не~зависят от того, запущена ли она в chroot'е,
или нет. Этим systemd отличается от класического SysV init.
Новые ядра Linux поддерживают возможность создания независимых пространств имен
файловых систем (в дальнейшем FSNS, от <<file system namespaces>>). По
функциональности этот механизм аналогичен +chroot()+, однако предоставляет
гораздо более широкие возможности, и в нем отсутствуют проблемы с безопасностью,
характерные для chroot. systemd позволяет использовать при конфигурировании
юнитов некоторые возможности, предоставляемые FSNS. В частности, использование
FSNS часто является гораздо более простой и удобной альтернативой созданию
полноценных chroot-окружений. Используя директивы +ReadOnlyDirectories=+,
+InaccessibleDirectories=+, вы можете задать ограничения по использованию
иерархии файловых систем для заданной службы: ее корнем будет системный корневой
каталог, однако указанные в этих директивах подкаталоги будут доступны только
для чтения или вообще недоступны для нее. Например:
\begin{Verbatim}
[Unit]
Description=A Service With No Access to /home
[Service]
ExecStart=/usr/bin/foobard
InaccessibleDirectories=/home
\end{Verbatim}
Такая служба будет иметь доступ ко всей иерархии файловых систем ОС, с
единственным исключением~--- она не~будет видеть каталог +/home+, что позволит
защитить данные пользователей от потенциальных хакеров. (Подробнее об этих
опциях можно почитать на
\href{http://0pointer.de/public/systemd-man/systemd.exec.html}{странице
руководства}).
Фактически, FSNS по множеству параметров превосходят +chroot()+. Скорее всего,
Avahi и ReltimeKit в ближайшем будущем перейдут с +chroot()+ к использованию
FSNS.
Итак, мы рассмотрели вопросы использования chroot для обеспечения безопасности.
Переходим ко второму пункту: Подготовка и управление образом операционной
системы при отладке, тестировании, компиляции, установке или восстановлении.
chroot-окружения, по сути, весьма примитивно: они изолируют только иерархии
файловых систем. Даже после chroot'а в определенный подкаталог, процесс
по-прежнему имеет полный доступ к системным вызовам, может убить любой процесс
из основной системы, и т.п. Вследствие этого, запуск полноценной ОС (или ее
части) внутри chroot'а несет угрозу для хост-системы: у гостя и хоста отличаются
лишь содержимое файловой системы, все остальное у них общее. Например, если вы
обновляете дистрибутив, установленный в chroot-окружении, и пост-установочный
скрипт пакета отправляет +SIGTERM+ процессу init для его
перезапуска\footnote{Прим. перев.: Во избежание путаницы отметим, что перезапуск
процесса init (PID~1) <<на лету>> при получении +SIGTERM+ поддерживается только
в systemd, в классическом SysV init такой возможности нет}, на него среагирует
именно хост-система! Кроме того, хост и chroot'нутая система будут иметь общую
разделяемую память SysV (SysV shared memory), общие сокеты из абстрактных
пространств имен (abstract namespace sockets) и другие элементы IPC. Для
отладки, тестирования, компиляции, установки и восстановлении ОС не~требуется
абсолютно безопасная изоляция, однако нужна защита от \emph{случайного}
воздействия на ОС хоста изнутри chroot-окружения, иначе вы можете получить целый
букет проблем, как минимум, от пост-инсталляционных скриптов при установке
пакетов в chroot-окружении.
systemd имеет целый ряд возможностей, полезных для работы с chroot-системами:
Прежде всего, управляющая программа +systemctl+ автоматически определяет, что
она запущена в chroot-системе. В такой ситуации будут работать только команды
+systemctl enable+ и +systemctl disable+, во всех остальных случаях +systemctl+
просто не~будет ничего делать, возвращая код успешного завершения операции.
Таким образом, пакетные скрипты смогут включить/отключить запуск <<своих>> служб
при загрузке (или в других ситуациях), однако команды наподобие
+systemctl restart+ (обычно выполняется при обновлении пакета) не~дадут никакого
эффекта внутри chroot-окружения\footnote{Прим. перев.: автор забывает отметить
не~вполне очевидный момент: такое поведение +systemctl+ проявляется только в
<<мертвых>> окружениях, т.е. в тех, где не~запущен процесс init, и
соответственно отсутствуют управляющие сокеты в +/run/systemd+. Такая ситуация
возникает, например, при установке системы в chroot через
debootstrap/febootstrap. В этом случае возможности +systemctl+ ограничиваются
операциями с символьными ссылками, определяющими триггеры активации юнитов, т.е.
выполнением действий +enable+ и +disable+, не~требующих непосредственного
взаимодействия с процессом init}.
Однако, куда более интересные возможности предоставляет программа
\href{http://0pointer.de/public/systemd-man/systemd-nspawn.html}{systemd-nspawn},
входящая в стандартный комплект поставки systemd. По сути, это улучшенный аналог
+chroot(1)+~--- она не~только подменяет корневой каталог, но и создает отдельные
пространства имен для дерева файловых систем (FSNS) и для идентификаторов
процессов (PID NS), предоставляя легковесную реализацию системного
контейнера\footnote{Прим. перев.: Используемые в +systemd-nspawn+ механизмы
ядра Linux, такие, как FS NS и PID NS, также лежат в основе
\href{http://lxc.sourceforge.net/}{LXC}, системы контейнерной изоляции для
Linux, которая позиционируется как современная альтернатива классическому
\href{http://wiki.openvz.org/Main_Page}{OpenVZ}. Стоит отметить, что LXC
ориентирована прежде всего на создание независимых виртуальных окружений,
с поддержкой раздельных сетевых стеков, ограничением на ресурсы, сохранением
настроек и т.п., в то время как +systemd-nspawn+ является лишь более удобной и
эффективной заменой команды +chroot(1)+, предназначенной прежде всего для
развертывания, восстановления, сборки и тестирования операционных систем. Далее
автор разъясняет свою точку зрения на этот вопрос}.
+systemd-nspawn+ проста в использовании как +chroot(1)+, однако изоляция
от хост-системы является более полной и безопасной. Всего одной командой
вы можете загрузить внутри контейнера \emph{полноценную} ОС (на базе systemd
или SysV init). Благодаря использованию независимых пространств идентификаторов
процессов, процесс init внутри контейнера получит PID~1, что позволит работать
ему в штатном режиме. Также, в отличие от +chroot(1)+, внутри окружения
будут автоматически смонтированы +/proc+ и +/sys+.
Следующий пример иллюстрирует возможность запустить Debian в на Fedora-хосте
всего тремя командами:
\begin{Verbatim}
# yum install debootstrap
# debootstrap --arch=amd64 unstable debian-tree/
# systemd-nspawn -D debian-tree/
\end{Verbatim}
Вторая из этих команд обеспечивает развертывание в подкаталоге +./debian-tree/+
файловой структуры дистрибутива Debian, после чего третья команда запускает
внутри полученной системы процесс командной оболочки. Если вы хотите запустить
внутри контейнера полноценную ОС, воспользуйтесь командой
\begin{Verbatim}
# systemd-nspawn -D debian-tree/ /sbin/init
\end{Verbatim}
После быстрой загрузки вы получите приглашение оболочки, запущенной внутри
полноценной ОС, функционирующей в контейнере. Изнутри контейнера невозможно
увидеть процессы, которые находятся вне его. Контейнер сможет пользоваться сетью
хоста, однако не~имеет возможности изменить ее настройки (это может привести к
серии ошибок в процессе загрузки гостевой ОС, но ни~одна из этих ошибок
не~должна быть критической). Контейнер получает доступ к +/sys+ и +/proc/sys+,
однако, во избежание вмешательства контейнера в конфигурацию ядра и аппаратного
обеспечения хоста, эти каталоги будут смонтированы только для чтения. Обратите
внимание, что эта защита блокирует лишь \emph{случайные}, \emph{непредвиденные}
попытки изменения параметров. При необходимости, процесс внутри контейнера,
обладающий достаточными полномочиями, сможет перемонтировать эти файловые
системы в режиме чтения-записи.
Итак, что же такого хорошего в +systemd-nspawn+?
\begin{enumerate}
\item Использовать эту утилиту очень просто. Вам даже не~нужно вручную монтировать
внутри окружения +/proc+ и +/sys+~--- она сделает это за вас, а
ядро автоматически отмонтирует их, когда последний процесс
контейнера завершится.
\item Обеспечивается надежная изоляция, предотвращающая случайные
изменения параметров ОС хоста изнутри контейнера.
\item Теперь вы можете загрузить внутри контейнера полноценную ОС, а
не~одну-единственную оболочку.
\item Эта утилита очень компактна и присутствует везде, где установлен
systemd. Она не~требует специальной установки и настройки.
\end{enumerate}
systemd уже подготовлен для работы внутри таких контейнеров. Например, когда
подается команда на выключение системы внутри контейнера, systemd на последнем
шаге вызывает не~+reboot()+, а просто +exit()+.
Стоит отметить, что +systemd-nspawn+ все же не~является полноценной системой
контейнерной виртуализации/изоляции~--- если нужно именно это, воспользуйтесь
\href{ http://lxc.sourceforge.net/}{LXC}. Этот проект использует те же самые
механизмы ядра, но предоставляет куда более широкие возможности, включая
виртуализацию сети. Если вам угодно, +systemd-nspawn+ как реализация контейнера
похожа на GNOME~3~--- компактна и проста в использовании, опций для настройки
очень мало. В то время как LXC больше похож на KDE: опций для настройки больше,
чем строк кода. Я создал +systemd-nspawn+ специально для тестирования, отладки,
сборки, восстановления. Именно для этих задач вам стоит ее использовать~--- она
неплохо с ними справляется, куда лучше, чем +chroot(1)+.
Что ж, пора заканчивать. Итак:
\begin{enumerate}
\item Использование +chroot()+ для обеспечения безопасности дает
наилучший результат, когда оно реализовано непосредственно в
коде самой программы.
\item +ReadOnlyDirectories=+ и +InaccessibleDirectories=+ могут быть
удобной альтернативой созданию полноценных chroot-окружений.
\item Если вам нужно поместить в chroot-окружение какую-либо службу,
воспользуйтесь опцией +RootDirectory=+.
\item +systemd-nspawn+~--- очень неплохая штука.
\item chroot'ы убоги, FSNS~---
\href{http://ru.wikipedia.org/wiki/Leet}{1337}.
\end{enumerate}
И все это уже сейчас доступно в Fedora~15.
\section{Поиск виновных}
Fedora~15\footnote{Величайший в истории релиз свободной ОС}
является первым релизом Fedora, использующим systemd в качестве системы
инициализации по умолчанию. Основной нашей целью при работе над выпуском F15
является обеспечение полной взаимной интеграции и корректной работы всех
компонентов. При подготовке следующего релиза, F16, мы сконцентрируемся на
дальнейшей полировке и ускорении системы. Для этого мы подготовили ряд
инструментов (доступных уже в F15), которые должны помочь нам в поиске проблем,
связанных с процессом загрузки. В этой статье я попытаюсь рассказать о том, как
найти виновников медленной загрузки вашей системы, и о том, что с ними делать
дальше.
Первый инструмент, который мы можем вам предложить, очень прост: по завершении
загрузки, systemd регистрирует в системном журнале информацию о суммарном
времени загрузки:
\begin{Verbatim}
systemd[1]: Startup finished in 2s 65ms 924us (kernel) + 2s 828ms 195us (initrd)
+ 11s 900ms 471us (userspace) = 16s 794ms 590us.
\end{Verbatim}
Эта запись означает следующее: на инициализацию ядра (до момента запуска initrd,
т.е. dracut) ушло 2 секунды. Далее, чуть менее трех секунд работал initrd. И
наконец, почти 12 секунд было потрачено systemd на запуск программ из
пространства пользователя. Итоговое время, начиная с того момента, как загрузчик
передал управление коду ядра, до того момента, как systemd завершил все
операции, связанные с загрузкой системы, составило почти 17 секунд. Казалось
бы, смысл этого числа вполне очевиден\ldots{} Однако не~стоит делать поспешных
выводов. Прежде всего, сюда не~входит время, затраченное на
инициализацию вашего сеанса в GNOME, так как эта задача уже выходит за рамки
задач процесса init. Кроме того, в этом показателе учитывается только время
работы systemd, хотя часто бывает так, что некоторые демоны продолжают
\emph{свою} работу по инициализации уже после того, как секундомер остановлен.
Проще говоря: приведенные числа позволяют лишь оценить общую скорость
загрузки, однако они не~являются точной характеристикой длительности процесса.
Кроме того, эта информация носит поверхностный характер: она не~сообщает, какие
именно системные компоненты заставляют systemd ждать так долго. Чтобы исправить
это упущение, мы ввели команду +systemd-analyze blame+:
\begin{Verbatim}
$ systemd-analyze blame
6207ms udev-settle.service
5228ms cryptsetup@luks\x2d9899b85d\x2df790\x2d4d2a\x2da650\x2d8b7d2fb92cc3.service
735ms NetworkManager.service
642ms avahi-daemon.service
600ms abrtd.service
517ms rtkit-daemon.service
478ms fedora-storage-init.service
396ms dbus.service
390ms rpcidmapd.service
346ms systemd-tmpfiles-setup.service
322ms fedora-sysinit-unhack.service
316ms cups.service
310ms console-kit-log-system-start.service
309ms libvirtd.service
303ms rpcbind.service
298ms ksmtuned.service
288ms lvm2-monitor.service
281ms rpcgssd.service
277ms sshd.service
276ms livesys.service
267ms iscsid.service
236ms mdmonitor.service
234ms nfslock.service
223ms ksm.service
218ms mcelog.service
...
\end{Verbatim}
Она выводит список юнитов systemd, активированных при загрузке, с указанием
времени инициализации для каждого из них. Список отсортирован по убыванию этого
времени, поэтому наибольший интерес для нас представляют первые строчки. В нашем
случае это +udev-settle.service+ и
+cryptsetup@luks\x2d9899b85d\x2df790\x2d4d2a\x2da650\x2d8b7d2fb92cc3.service+,
инициализация которых занимает более одной секунды. Стоит отметить, что к
анализу вывода команды +systemd-analyze blame+ тоже следует подходить с
осторожностью: она не~поясняет, \emph{почему} тот или иной юнит тратит
столько-то времени, она лишь констатирует факт, что время было затрачено. Кроме
того, не~стоит забывать, что юниты могут запускаться параллельно. В частности,
если две службы были запущены одновременно, то время их инициализации будет
значительно меньше, чем сумма времен инициализации каждой из них.
Рассмотрим повнимательнее первого осквернителя нашей загрузки: службу
+udev-settle.service+. Почему ей требуется так много времени для запуска, и что
мы можем с этим сделать? Эта служба выполняет очень простую задачу: она ожидает,
пока udev завершит опрос устройств, после чего завершается. Опрос же устройств
может занимать довольно много времени. Например, в нашем случае опрос устройств
занимает более 6~секунд из-за встроенного в компьютер 3G-модема, в котором
отсутствует SIM-карта. Этот модем очень долго отвечает на запросы udev. Опрос
устройств является частью схемы, обеспечивающей работу ModemManager'а и
позволяющей NetworkManager'у упростить для вас настройку 3G. Казалось бы,
очевидно, что виновником задержки является именно ModemManager, так как опрос
устройств для него занимает слишком много времени. Но такое обвинение будет
заведомо ошибочным. Дело в том, что опрос устройств очень часто оказывается довольно
длительной процедурой. Медленный опрос 3G-устройств для ModemManager является
частным случаем, отражающим это общее правило. Хорошая система опроса устройств
обязательно должна учитывать тот факт, что операция опроса любого из устройств
может затянуться надолго. Истинной причиной наших проблем является необходимость
ожидать завершения опроса устройств, т.е., наличие службы
+udev-settle.service+ как обязательной части нашего процесса загрузки.
Но почему эта служба вообще присутствует в нашем процессе загрузки? На самом
деле, мы можем прекрасно обойтись и без нее. Она нужна лишь как часть
используемой в Fedora схемы инициализации устройств хранения, а именно,
набора скриптов, выполняющих настройку LVM, RAID и multipath-устройств. На
сегодняшний день, реализация этих систем не~поддерживает собственного механизма
поиска и опроса устройств, и поэтому их запуск должен производиться только после
того, как эта работа уже проделана~--- тогда они могут просто последовательно
перебирать все диски. Однако, такой подход противоречит одному из
фундаментальных требований к современным компьютерам:
возможности подключать и отключать оборудование в любой момент, как при
выключенном компьютере, так и при включенном. Для некоторых
технологий невозможно точно определить момент завершения формирования списка устройств
(например, это характерно для USB и iSCSI), и поэтому процесс опроса
таких устройств обязательно должен включать некоторую фиксированную задержку,
гарантирующую, что все подключенные устройства успеют отозваться. С точки зрения
скорости загрузки это, безусловно, негативный эффект: соответствующие скрипты
заставляют нас ожидать завершения опроса устройств, хотя большинство этих
устройств не~являются необходимыми на данной стадии загрузки. В частности, в
рассматриваемой нами системе LVM, RAID и multipath вообще
не~используются!\footnote{Наиболее правильным решением в данном случае будет
ожидание событий подключения устройств (реализованное через libudev или
аналогичную технологию) с соответствующей обработкой каждого такого события~---
подобный подход позволит нам продолжить загрузку, как только будут готовы все
устройства, необходимые для ее продолжения. Для того, чтобы загрузка была
быстрой, мы должны ожидать завершения инициализации только тех устройств, которые
\emph{действительно} необходимы для ее продолжения на данной стадии. Ожидать
остальные устройства в данном случае смысла нет. Кроме того, стоит отметить, что
в числе программ, не~приспособленных для работы с динамически меняющимся
оборудованием и построенных в предположении о неизменности списка устройств,
кроме служб хранения, есть и другие подсистемы. Например, в нашем случае работа
initrd занимает так много времени главным образом из-за того, что для запуска
Plymouth необходимо дождаться завершения инициализации всех устройств
видеовывода. По неизвестной причине (во всяком случае, неизвестной для меня)
подгрузка модулей для моих видеокарт Intel занимает довольно продолжительное
время, что приводит к беспричинной задержке процесса загрузки. (Нет, я протестую
не~против опроса устройств, но против необходимости ожидать его завершения, чтобы
продолжить загрузку.)}
С учетом вышесказанного, мы можем спокойно исключить +udev-settle.service+ из
нашего процесса загрузки, так как мы не~используем ни~LVM, ни~RAID,
ни~multipath. Замаскируем эти службы, чтобы увеличить скорость загрузки:
\begin{Verbatim}
# ln -s /dev/null /etc/systemd/system/udev-settle.service
# ln -s /dev/null /etc/systemd/system/fedora-wait-storage.service
# ln -s /dev/null /etc/systemd/system/fedora-storage-init.service
# systemctl daemon-reload
\end{Verbatim}
После перезагрузки мы видим, что загрузка стала на одну секунду быстрее. Но
почему выигрыш оказался таким маленьким? Из-за второго осквернителя нашей
загрузки~--- cryptsetup. На рассматриваемой нами системе зашифрован раздел
+/home+. Специально для тестирования я записал пароль в файл, чтобы исключить
влияние скорости ручного набора пароля. К сожалению, для подключения
шифрованного раздела cryptsetup требует более пяти секунд. Будем ленивы, и
вместо того, чтобы исправлять ошибку в cryptsetup\footnote{На самом деле, я
действительно пытался исправить эту ошибку. Задержки в cryptsetup, по
моим наблюдениям, обусловлены, главным образом, слишком большим значением по
умолчанию для опции +--iter-time+. Я попытался доказать сопровождающим пакета
cryptsetup, что снижение этого времени с 1 секунды до 100~миллисекунд
не~приведет к трагическим последствиям для безопасности, однако они мне
не~поверили.}, попробуем найти обходной путь\footnote{Вообще-то я предпочитаю
решать проблемы, а не~искать пути для их обхода, однако в данном конкретном
случае возникает отличная возможность продемонстрировать одну интересную
возможность systemd\ldots{}}. Во время загрузки systemd должен дождаться, пока
все файловые системы, перечисленные в +/etc/fstab+ (кроме помеченных как
+noauto+) будут обнаружены, проверены и смонтированы. Только после этого systemd
может продолжить загрузку и приступить к запуску служб. Но первое обращение
к +/home+ (в отличие, например, от +/var+), происходит на поздней стадии
процесса загрузки (когда пользователь входит в систему). Следовательно, нам
нужно сделать так, чтобы этот каталога автоматически монтировался при загрузке,
но процесс загрузки не~ожидал завершения работы +cryptsetup+, +fsck+ и +mount+
для этого раздела. Как же сделать точку монтирования доступной, не~ожидая, пока
завершится процесс монтирования? Это можно сделать, воспользовавшись магической
силой systemd~--- просто добавим опцию монтирования +comment=systemd.automount+
в +/etc/fstab+. После этого, systemd будет создавать в +/home+ точку
автоматического монтирования, и при первом же обращении к этому каталогу
systemd выполнит подготовку устройства, проверку и монтирование файловой
системы.
\end{document}
vim:ft=tex:tw=80:spell:spelllang=ru