6269 lines
543 KiB
TeX
6269 lines
543 KiB
TeX
\documentclass[10pt,oneside,a4paper]{article}
|
||
\usepackage{cmap} % Copy-paste из PDF без проблем с кодировкой
|
||
\usepackage[utf8]{inputenc}
|
||
\usepackage[english,russian]{babel} % Русские переносы и проч.
|
||
\usepackage{graphicx,color,pmboxdraw}
|
||
\usepackage[T1,T2A]{fontenc}
|
||
\usepackage{indentfirst} % Отступ в первом абзаце главы
|
||
\usepackage{fancyvrb} % Продвинутые листинги и in-line commands
|
||
% listings в данной ситуации, IMHO, избыточен
|
||
\usepackage{verbatim} % Окружение comment
|
||
\usepackage{pdflscape} % Внимание! При выводе в DVI выборочный
|
||
% поворот страниц работать не будет, хотя текст будет повернут.
|
||
\usepackage[colorlinks,unicode,urlcolor=blue]{hyperref}
|
||
% Заполняем поля PDF уже со включенной опцией unicode
|
||
\hypersetup{pdftitle={systemd для администраторов},%
|
||
pdfauthor={Lennart Poettering, Sergey Ptashnick}}
|
||
% Не засоряем оглавление подразделами
|
||
%\setcounter{tocdepth}{1} % А почему бы и нет?
|
||
% Несколько сокращений
|
||
\newcommand{\sectiona}[1]{\section*{#1}\addcontentsline{toc}{section}{#1}}
|
||
\newcommand{\hreftt}[2]{\href{#1}{\texttt{#2}}}
|
||
\newenvironment{caveat}[1][]{\smallskip\par\textbf{Предупреждение#1: }}%
|
||
{\smallskip\par}
|
||
\newcommand{\sfnote}[1]{\texorpdfstring{\protect\footnote%
|
||
{Прим. перев.: #1}}{}}
|
||
\newcommand{\qna}[1]{\medskip\par\textbf{Вопрос: #1}\par Ответ:}
|
||
\newcommand\yousaywtf[1]{\emph{#1}}
|
||
\newcommand\yousaywtfsk[1]{\yousaywtf{#1}\medskip\par}
|
||
% Настройка макета страницы
|
||
\setlength{\hoffset}{-1.5cm}
|
||
\addtolength{\textwidth}{2cm}
|
||
\setlength{\voffset}{-2cm}
|
||
\addtolength{\textheight}{3cm}
|
||
\addtolength{\footskip}{5pt}
|
||
% Настройка форматирования in-line commands
|
||
\DefineShortVerb{\+}
|
||
\VerbatimFootnotes
|
||
% И листингов
|
||
\definecolor{gray}{gray}{0.75}
|
||
\fvset{frame=leftline,rulecolor=\color{gray},framerule=1mm}
|
||
\definecolor{dgreen}{rgb}{0,0.6,0}
|
||
% Запрет висячих строк
|
||
\clubpenalty=10000
|
||
\widowpenalty=10000
|
||
|
||
\begin{document}
|
||
\sloppy
|
||
\title{systemd для администраторов}
|
||
\author{Lennart Poettering (автор)\thanks{Первоисточник (на английском
|
||
языке) опубликован на сайте автора: \url{http://0pointer.de/blog/projects}}\\%
|
||
Сергей Пташник (русский перевод)\thanks{Актуальная версия перевода
|
||
доступна на личной странице переводчика:
|
||
\url{http://www2.kangran.su/~nnz/pub/s4a/}}\\%
|
||
\small Данный документ доступен на условиях лицензии
|
||
\href{http://creativecommons.org/licenses/by-sa/3.0/legalcode}{CC-BY-SA 3.0
|
||
Unported}}
|
||
\maketitle
|
||
\tableofcontents
|
||
%\newpage
|
||
\sectiona{Предисловие автора}
|
||
Многие из вас, наверное, уже знают, что
|
||
\href{http://www.freedesktop.org/wiki/Software/systemd}{systemd}~--- это новая
|
||
система инициализации дистрибутива Fedora, начиная с Fedora~14\footnote{Прим.
|
||
перев.: к сожалению, разработчики Fedora приняли решение оставить в Fedora~14 в
|
||
качестве системы инициализации по умолчанию upstart, однако systemd все равно
|
||
включен в этот релиз и может быть использован в качестве альтернативной системы
|
||
инициализации. Окончательный переход на systemd произошел лишь в Fedora~15.}.
|
||
Помимо Fedora, systemd также поддерживает и другие дистрибутивы, в частности,
|
||
\href{http://en.opensuse.org/SDB:Systemd}{OpenSUSE}\footnote{Прим. перев.:
|
||
Сейчас systemd поддерживается практически во всех популярных дистрибутивах для
|
||
настольных систем.}. systemd предоставляет администраторам целый ряд новых
|
||
возможностей, значительно упрощающих процесс обслуживания системы. Эта статья
|
||
является первой в серии публикаций, планируемых в ближайшие месяцы. В каждой из
|
||
этих статей я попытаюсь рассказать об очередной новой возможности systemd.
|
||
Большинство этих возможностей можно описать легко и просто, и подобные статьи
|
||
должны быть интересны довольно широкой аудитории. Однако, время от времени мы
|
||
будем рассматривать ключевые новшества systemd, что может потребовать несколько
|
||
более подробного изложения.
|
||
\begin{flushright}
|
||
Lennart Poettering, 23 августа 2010~г.
|
||
\end{flushright}
|
||
|
||
\section{Контроль процесса загрузки}
|
||
\label{sec:verify}
|
||
|
||
Как правило, во время загрузки Linux по экрану быстро пробегает огромное
|
||
количество различных сообщений. Так как мы интенсивно работаем над
|
||
параллелизацией и ускорением процесса загрузки, с каждой новой версией
|
||
systemd эти сообщения будут пробегать все быстрее и быстрее, вследствие чего,
|
||
читать их будет все труднее. К тому же, многие пользователи применяют
|
||
графические оболочки загрузки (например, Plymouth), полностью скрывающие эти
|
||
сообщения. Тем не~менее, информация, которую они несут, была и остается
|
||
чрезвычайно важной~--- они показывают, успешно ли запустилась каждая служба, или
|
||
попытка ее запуска закончилась ошибкой (зеленое
|
||
\texttt{[~\textcolor{dgreen}{OK}~]} или красное
|
||
\texttt{[~\textcolor{red}{FAILED}~]} соответственно). Итак, с ростом скорости
|
||
загрузки систем, возникает неприятная ситуация: информация о результатах
|
||
запуска служб бывает очень важна, а просматривать ее все тяжелее. systemd
|
||
предлагает выход из этой ситуации: он отслеживает и запоминает факты успешного
|
||
или неудачного запуска служб на этапе загрузки, а также сбои служб во время
|
||
работы. К таким случаям относятся выходы с ненулевым кодом, ошибки
|
||
сегментирования и т.п. Введя +systemctl status+ в своей командной оболочке, вы
|
||
можете ознакомиться с состоянием всех служб, как <<родных>> (native) для
|
||
systemd, так и классических SysV/LSB служб, поддерживаемых в целях
|
||
совместимости:
|
||
|
||
\begin{landscape}
|
||
\begin{Verbatim}[fontsize=\small]
|
||
[root@lambda] ~# systemctl
|
||
UNIT LOAD ACTIVE SUB JOB DESCRIPTION
|
||
dev-hugepages.automount loaded active running Huge Pages File System Automount Point
|
||
dev-mqueue.automount loaded active running POSIX Message Queue File System Automount Point
|
||
proc-sys-fs-binfmt_misc.automount loaded active waiting Arbitrary Executable File Formats File System Automount Point
|
||
sys-kernel-debug.automount loaded active waiting Debug File System Automount Point
|
||
sys-kernel-security.automount loaded active waiting Security File System Automount Point
|
||
sys-devices-pc...0000:02:00.0-net-eth0.device loaded active plugged 82573L Gigabit Ethernet Controller
|
||
[...]
|
||
sys-devices-virtual-tty-tty9.device loaded active plugged /sys/devices/virtual/tty/tty9
|
||
-.mount loaded active mounted /
|
||
boot.mount loaded active mounted /boot
|
||
dev-hugepages.mount loaded active mounted Huge Pages File System
|
||
dev-mqueue.mount loaded active mounted POSIX Message Queue File System
|
||
home.mount loaded active mounted /home
|
||
proc-sys-fs-binfmt_misc.mount loaded active mounted Arbitrary Executable File Formats File System
|
||
abrtd.service loaded active running ABRT Automated Bug Reporting Tool
|
||
accounts-daemon.service loaded active running Accounts Service
|
||
acpid.service loaded active running ACPI Event Daemon
|
||
atd.service loaded active running Execution Queue Daemon
|
||
auditd.service loaded active running Security Auditing Service
|
||
avahi-daemon.service loaded active running Avahi mDNS/DNS-SD Stack
|
||
bluetooth.service loaded active running Bluetooth Manager
|
||
console-kit-daemon.service loaded active running Console Manager
|
||
cpuspeed.service loaded active exited LSB: processor frequency scaling support
|
||
crond.service loaded active running Command Scheduler
|
||
cups.service loaded active running CUPS Printing Service
|
||
dbus.service loaded active running D-Bus System Message Bus
|
||
getty@tty2.service loaded active running Getty on tty2
|
||
getty@tty3.service loaded active running Getty on tty3
|
||
getty@tty4.service loaded active running Getty on tty4
|
||
getty@tty5.service loaded active running Getty on tty5
|
||
getty@tty6.service loaded active running Getty on tty6
|
||
haldaemon.service loaded active running Hardware Manager
|
||
hdapsd@sda.service loaded active running sda shock protection daemon
|
||
irqbalance.service loaded active running LSB: start and stop irqbalance daemon
|
||
iscsi.service loaded active exited LSB: Starts and stops login and scanning of iSCSI devices.
|
||
iscsid.service loaded active exited LSB: Starts and stops login iSCSI daemon.
|
||
livesys-late.service loaded active exited LSB: Late init script for live image.
|
||
livesys.service loaded active exited LSB: Init script for live image.
|
||
lvm2-monitor.service loaded active exited LSB: Monitoring of LVM2 mirrors, snapshots etc. using dmeventd or progress polling
|
||
mdmonitor.service loaded active running LSB: Start and stop the MD software RAID monitor
|
||
modem-manager.service loaded active running Modem Manager
|
||
netfs.service loaded active exited LSB: Mount and unmount network filesystems.
|
||
NetworkManager.service loaded active running Network Manager
|
||
ntpd.service loaded maintenance maintenance Network Time Service
|
||
polkitd.service loaded active running Policy Manager
|
||
prefdm.service loaded active running Display Manager
|
||
rc-local.service loaded active exited /etc/rc.local Compatibility
|
||
rpcbind.service loaded active running RPC Portmapper Service
|
||
rsyslog.service loaded active running System Logging Service
|
||
rtkit-daemon.service loaded active running RealtimeKit Scheduling Policy Service
|
||
sendmail.service loaded active running LSB: start and stop sendmail
|
||
sshd@172.31.0.53:22-172.31.0.4:36368.service loaded active running SSH Per-Connection Server
|
||
sysinit.service loaded active running System Initialization
|
||
systemd-logger.service loaded active running systemd Logging Daemon
|
||
udev-post.service loaded active exited LSB: Moves the generated persistent udev rules to /etc/udev/rules.d
|
||
udisks.service loaded active running Disk Manager
|
||
upowerd.service loaded active running Power Manager
|
||
wpa_supplicant.service loaded active running Wi-Fi Security Service
|
||
avahi-daemon.socket loaded active listening Avahi mDNS/DNS-SD Stack Activation Socket
|
||
cups.socket loaded active listening CUPS Printing Service Sockets
|
||
dbus.socket loaded active running dbus.socket
|
||
rpcbind.socket loaded active listening RPC Portmapper Socket
|
||
sshd.socket loaded active listening sshd.socket
|
||
systemd-initctl.socket loaded active listening systemd /dev/initctl Compatibility Socket
|
||
systemd-logger.socket loaded active running systemd Logging Socket
|
||
systemd-shutdownd.socket loaded active listening systemd Delayed Shutdown Socket
|
||
dev-disk-by\x1...x1db22a\x1d870f1adf2732.swap loaded active active /dev/disk/by-uuid/fd626ef7-34a4-4958-b22a-870f1adf2732
|
||
basic.target loaded active active Basic System
|
||
bluetooth.target loaded active active Bluetooth
|
||
dbus.target loaded active active D-Bus
|
||
getty.target loaded active active Login Prompts
|
||
graphical.target loaded active active Graphical Interface
|
||
local-fs.target loaded active active Local File Systems
|
||
multi-user.target loaded active active Multi-User
|
||
network.target loaded active active Network
|
||
remote-fs.target loaded active active Remote File Systems
|
||
sockets.target loaded active active Sockets
|
||
swap.target loaded active active Swap
|
||
sysinit.target loaded active active System Initialization
|
||
|
||
LOAD = Reflects whether the unit definition was properly loaded.
|
||
ACTIVE = The high-level unit activation state, i.e. generalization of SUB.
|
||
SUB = The low-level unit activation state, values depend on unit type.
|
||
JOB = Pending job for the unit.
|
||
|
||
221 units listed. Pass --all to see inactive units, too.
|
||
[root@lambda] ~#
|
||
\end{Verbatim}
|
||
(Листинг был сокращен за счет удаления строк, не~относящихся к теме статьи.)
|
||
\end{landscape}
|
||
|
||
Обратите внимание на графу ACTIVE, в которой отображается обобщенный статус
|
||
службы (или любого другого юнита systemd: устройства, сокета, точки
|
||
монтирования~--- их мы рассмотрим подробнее в последующих статьях). Основными
|
||
значениями обобщенного статуса являются active (служба выполняется) и inactive
|
||
(служба не~была запущена). Также существуют и другие статусы. Например,
|
||
внимательно посмотрев на листинг выше, вы можете заметить, что служба ntpd
|
||
(сервер точного времени) находится в состоянии, обозначенном как maintenance.
|
||
Чтобы узнать, что же произошло с ntpd, воспользуемся командой
|
||
+systemctl status+\footnote{Прим. перев.: Стоит заметить, что формат вывода
|
||
данной команды менялся по мере развития systemd~--- появлялись дополнительные
|
||
поля с информацией, был добавлен вывод журнала службы
|
||
(см.~главу~\ref{sec:journal}) и т.д. Здесь приведен пример вывода этой команды
|
||
на момент написания исходной статьи (лето 2010 года).}:
|
||
\begin{Verbatim}[commandchars=\\\{\}]
|
||
[root@lambda] ~# systemctl status ntpd.service
|
||
ntpd.service - Network Time Service
|
||
Loaded: loaded (/etc/systemd/system/ntpd.service)
|
||
Active: \textcolor{red}{maintenance}
|
||
Main: 953 (code=exited, status=255)
|
||
CGroup: name=systemd:/systemd-1/ntpd.service
|
||
[root@lambda] ~#
|
||
\end{Verbatim}
|
||
|
||
systemd сообщает нам, что ntpd был запущен (с идентификатором процесса 953) и
|
||
аварийно завершил работу (с кодом выхода 255)\footnote{Прим. перев.:
|
||
Впоследствии, про просьбам пользователей, считавших, что слово <<maintenance>>
|
||
недостаточно точно отражает ситуацию, оно было заменено на <<failed>>.}.
|
||
|
||
В последующих версиях systemd, мы планируем добавить возможность вызова в
|
||
таких ситуациях ABRT (Automated Bug Report Tool), но для этого необходима
|
||
поддержка со стороны самого ABRT. Соответствующий запрос уже
|
||
\href{https://bugzilla.redhat.com/show_bug.cgi?id=622773}{направлен} его
|
||
разработчикам, однако пока не~встретил среди них поддержки.
|
||
|
||
Резюме: использование +systemctl+ и +systemctl status+ является современной,
|
||
более удобной и эффективной альтернативой разглядыванию быстро пробегающих по
|
||
экрану сообщений в классическом SysV. +systemctl status+ дает возможность
|
||
получить развернутую информацию о характере ошибки и, кроме того, в отличие
|
||
от сообщений SysV, показывает не~только ошибки при запуске, но и ошибки,
|
||
возникшие во время исполнения службы.
|
||
|
||
\section{О службах и процессах}
|
||
\label{sec:cgls}
|
||
|
||
В большинстве современных Linux-систем количество одновременно работающих
|
||
процессов обычно весьма значительно. Понять, откуда взялся и что делает тот
|
||
или иной процесс, становится все сложнее и сложнее. Многие службы используют
|
||
сразу несколько рабочих процессов, и это отнюдь не~всегда можно легко
|
||
распознать по выводу команды +ps+. Встречаются еще более сложные ситуации,
|
||
когда демон запускает сторонние процессы~--- например, веб-сервер выполняет
|
||
CGI-программы, а демон cron~--- команды, предписанные ему в crontab.
|
||
|
||
Немного помочь в решении этой проблемы может древовидная иерархия процессов,
|
||
отображаемая по команде +ps xaf+. Именно <<немного помочь>>, а не~решить
|
||
полностью. В частности, процессы, родители которых умирают раньше их самих,
|
||
становят потомками PID~1 (процесса init), что сразу затрудняет процесс
|
||
выяснения их происхождения. Кроме того, процесс может избавиться от связи с
|
||
родителем через две последовательные операции +fork()+ (в целом, эта возможность
|
||
признается нужной и полезной, и является частью используемого в Unix подхода
|
||
к разработке демонов). Также, не~будем забывать, что процесс легко может
|
||
изменить свое имя посредством +PR_SETNAME+, или задав значение
|
||
+argv[0]+, что также усложняет процесс его опознания\footnote{Прим.
|
||
перев.: Стоит отметить, что перечисленные ситуации могут возникнуть не~только
|
||
вследствие ошибок в коде и/или конфигурации программ, но и в результате злого
|
||
умысла. Например, очень часто встречается ситуация, когда установленный на
|
||
взломанном сервере процесс-бэкдор маскируется под нормального демона, меняя
|
||
себе имя, скажем, на httpd.}.
|
||
|
||
systemd предлагает простой путь для решения обсуждаемой задачи. Запуская
|
||
новый процесс, systemd помещает его в отдельную контрольную группу
|
||
с соответствующим именем. Контрольные группы Linux предоставляют очень
|
||
удобный инструмент для иерархической структуризации процессов: когда
|
||
какой-либо процесс порождает потомка, этот потомок автоматически включается в
|
||
ту же группу, что и родитель. При этом, что очень важно, непривилегированные
|
||
процессы не~могут изменить свое положение в этой иерархии. Таким образом,
|
||
контрольные группы позволяют точно установить происхождение конкретного
|
||
процесса, вне зависимости от того, сколько раз он форкался и переименовывал
|
||
себя~--- имя его контрольной группы невозможно спрятать или изменить. Кроме
|
||
того, при штатном завершении родительской службы, будут завершены и все
|
||
порожденные ею процессы, как бы они ни~пытались сбежать. С systemd уже
|
||
невозможна ситуация, когда после остановки web-сервера, некорректно
|
||
форкнувшийся CGI-процесс продолжает исполняться вплоть до последних секунд
|
||
работы системы.
|
||
|
||
В этой статье мы рассмотрим две простых команды, которые позволят вам
|
||
наглядно оценить схему взаимоотношений systemd и порожденных им процессов.
|
||
Первая из этих команд~--- все та же +ps+, однако на этот раз в ее параметры
|
||
добавлено указание выводить сведения по контрольным группам, а также другую
|
||
интересную информацию:
|
||
|
||
\begin{landscape}
|
||
\begin{Verbatim}[fontsize=\small]
|
||
$ ps xawf -eo pid,user,cgroup,args
|
||
PID USER CGROUP COMMAND
|
||
2 root - [kthreadd]
|
||
3 root - \_ [ksoftirqd/0]
|
||
[...]
|
||
4281 root - \_ [flush-8:0]
|
||
1 root name=systemd:/systemd-1 /sbin/init
|
||
455 root name=systemd:/systemd-1/sysinit.service /sbin/udevd -d
|
||
28188 root name=systemd:/systemd-1/sysinit.service \_ /sbin/udevd -d
|
||
28191 root name=systemd:/systemd-1/sysinit.service \_ /sbin/udevd -d
|
||
1096 dbus name=systemd:/systemd-1/dbus.service /bin/dbus-daemon --system --address=systemd: --nofork --systemd-activation
|
||
1131 root name=systemd:/systemd-1/auditd.service auditd
|
||
1133 root name=systemd:/systemd-1/auditd.service \_ /sbin/audispd
|
||
1135 root name=systemd:/systemd-1/auditd.service \_ /usr/sbin/sedispatch
|
||
1171 root name=systemd:/systemd-1/NetworkManager.service /usr/sbin/NetworkManager --no-daemon
|
||
4028 root name=systemd:/systemd-1/NetworkManager.service \_ /sbin/dhclient -d -4 -sf /usr/libexec/nm-dhcp-client.action -pf /var/run/dhclient-wlan0.pid -lf /var/lib/dhclient/dhclient-7d32a784-ede9-4cf6-9ee3-60edc0bce5ff-wlan0.lease -
|
||
1175 avahi name=systemd:/systemd-1/avahi-daemon.service avahi-daemon: running [epsilon.local]
|
||
1194 avahi name=systemd:/systemd-1/avahi-daemon.service \_ avahi-daemon: chroot helper
|
||
1193 root name=systemd:/systemd-1/rsyslog.service /sbin/rsyslogd -c 4
|
||
1195 root name=systemd:/systemd-1/cups.service cupsd -C /etc/cups/cupsd.conf
|
||
1207 root name=systemd:/systemd-1/mdmonitor.service mdadm --monitor --scan -f --pid-file=/var/run/mdadm/mdadm.pid
|
||
1210 root name=systemd:/systemd-1/irqbalance.service irqbalance
|
||
1216 root name=systemd:/systemd-1/dbus.service /usr/sbin/modem-manager
|
||
1219 root name=systemd:/systemd-1/dbus.service /usr/libexec/polkit-1/polkitd
|
||
1242 root name=systemd:/systemd-1/dbus.service /usr/sbin/wpa_supplicant -c /etc/wpa_supplicant/wpa_supplicant.conf -B -u -f /var/log/wpa_supplicant.log -P /var/run/wpa_supplicant.pid
|
||
1249 68 name=systemd:/systemd-1/haldaemon.service hald
|
||
1250 root name=systemd:/systemd-1/haldaemon.service \_ hald-runner
|
||
1273 root name=systemd:/systemd-1/haldaemon.service \_ hald-addon-input: Listening on /dev/input/event3 /dev/input/event9 /dev/input/event1 /dev/input/event7 /dev/input/event2 /dev/input/event0 /dev/input/event8
|
||
1275 root name=systemd:/systemd-1/haldaemon.service \_ /usr/libexec/hald-addon-rfkill-killswitch
|
||
1284 root name=systemd:/systemd-1/haldaemon.service \_ /usr/libexec/hald-addon-leds
|
||
1285 root name=systemd:/systemd-1/haldaemon.service \_ /usr/libexec/hald-addon-generic-backlight
|
||
1287 68 name=systemd:/systemd-1/haldaemon.service \_ /usr/libexec/hald-addon-acpi
|
||
1317 root name=systemd:/systemd-1/abrtd.service /usr/sbin/abrtd -d -s
|
||
1332 root name=systemd:/systemd-1/getty@.service/tty2 /sbin/mingetty tty2
|
||
1339 root name=systemd:/systemd-1/getty@.service/tty3 /sbin/mingetty tty3
|
||
1342 root name=systemd:/systemd-1/getty@.service/tty5 /sbin/mingetty tty5
|
||
1343 root name=systemd:/systemd-1/getty@.service/tty4 /sbin/mingetty tty4
|
||
1344 root name=systemd:/systemd-1/crond.service crond
|
||
1346 root name=systemd:/systemd-1/getty@.service/tty6 /sbin/mingetty tty6
|
||
1362 root name=systemd:/systemd-1/sshd.service /usr/sbin/sshd
|
||
1376 root name=systemd:/systemd-1/prefdm.service /usr/sbin/gdm-binary -nodaemon
|
||
1391 root name=systemd:/systemd-1/prefdm.service \_ /usr/libexec/gdm-simple-slave --display-id /org/gnome/DisplayManager/Display1 --force-active-vt
|
||
1394 root name=systemd:/systemd-1/prefdm.service \_ /usr/bin/Xorg :0 -nr -verbose -auth /var/run/gdm/auth-for-gdm-f2KUOh/database -nolisten tcp vt1
|
||
1495 root name=systemd:/user/lennart/1 \_ pam: gdm-password
|
||
1521 lennart name=systemd:/user/lennart/1 \_ gnome-session
|
||
1621 lennart name=systemd:/user/lennart/1 \_ metacity
|
||
1635 lennart name=systemd:/user/lennart/1 \_ gnome-panel
|
||
1638 lennart name=systemd:/user/lennart/1 \_ nautilus
|
||
1640 lennart name=systemd:/user/lennart/1 \_ /usr/libexec/polkit-gnome-authentication-agent-1
|
||
1641 lennart name=systemd:/user/lennart/1 \_ /usr/bin/seapplet
|
||
1644 lennart name=systemd:/user/lennart/1 \_ gnome-volume-control-applet
|
||
1646 lennart name=systemd:/user/lennart/1 \_ /usr/sbin/restorecond -u
|
||
1652 lennart name=systemd:/user/lennart/1 \_ /usr/bin/devilspie
|
||
1662 lennart name=systemd:/user/lennart/1 \_ nm-applet --sm-disable
|
||
1664 lennart name=systemd:/user/lennart/1 \_ gnome-power-manager
|
||
1665 lennart name=systemd:/user/lennart/1 \_ /usr/libexec/gdu-notification-daemon
|
||
1670 lennart name=systemd:/user/lennart/1 \_ /usr/libexec/evolution/2.32/evolution-alarm-notify
|
||
1672 lennart name=systemd:/user/lennart/1 \_ /usr/bin/python /usr/share/system-config-printer/applet.py
|
||
1674 lennart name=systemd:/user/lennart/1 \_ /usr/lib64/deja-dup/deja-dup-monitor
|
||
1675 lennart name=systemd:/user/lennart/1 \_ abrt-applet
|
||
1677 lennart name=systemd:/user/lennart/1 \_ bluetooth-applet
|
||
1678 lennart name=systemd:/user/lennart/1 \_ gpk-update-icon
|
||
1408 root name=systemd:/systemd-1/console-kit-daemon.service /usr/sbin/console-kit-daemon --no-daemon
|
||
1419 gdm name=systemd:/systemd-1/prefdm.service /usr/bin/dbus-launch --exit-with-session
|
||
1453 root name=systemd:/systemd-1/dbus.service /usr/libexec/upowerd
|
||
1473 rtkit name=systemd:/systemd-1/rtkit-daemon.service /usr/libexec/rtkit-daemon
|
||
1496 root name=systemd:/systemd-1/accounts-daemon.service /usr/libexec/accounts-daemon
|
||
1499 root name=systemd:/systemd-1/systemd-logger.service /lib/systemd/systemd-logger
|
||
1511 lennart name=systemd:/systemd-1/prefdm.service /usr/bin/gnome-keyring-daemon --daemonize --login
|
||
1534 lennart name=systemd:/user/lennart/1 dbus-launch --sh-syntax --exit-with-session
|
||
1535 lennart name=systemd:/user/lennart/1 /bin/dbus-daemon --fork --print-pid 5 --print-address 7 --session
|
||
1603 lennart name=systemd:/user/lennart/1 /usr/libexec/gconfd-2
|
||
1612 lennart name=systemd:/user/lennart/1 /usr/libexec/gnome-settings-daemon
|
||
1615 lennart name=systemd:/user/lennart/1 /usr/libexec/gvfsd
|
||
1626 lennart name=systemd:/user/lennart/1 /usr/libexec//gvfs-fuse-daemon /home/lennart/.gvfs
|
||
1634 lennart name=systemd:/user/lennart/1 /usr/bin/pulseaudio --start --log-target=syslog
|
||
1649 lennart name=systemd:/user/lennart/1 \_ /usr/libexec/pulse/gconf-helper
|
||
1645 lennart name=systemd:/user/lennart/1 /usr/libexec/bonobo-activation-server --ac-activate --ior-output-fd=24
|
||
1668 lennart name=systemd:/user/lennart/1 /usr/libexec/im-settings-daemon
|
||
1701 lennart name=systemd:/user/lennart/1 /usr/libexec/gvfs-gdu-volume-monitor
|
||
1707 lennart name=systemd:/user/lennart/1 /usr/bin/gnote --panel-applet --oaf-activate-iid=OAFIID:GnoteApplet_Factory --oaf-ior-fd=22
|
||
1725 lennart name=systemd:/user/lennart/1 /usr/libexec/clock-applet
|
||
1727 lennart name=systemd:/user/lennart/1 /usr/libexec/wnck-applet
|
||
1729 lennart name=systemd:/user/lennart/1 /usr/libexec/notification-area-applet
|
||
1733 root name=systemd:/systemd-1/dbus.service /usr/libexec/udisks-daemon
|
||
1747 root name=systemd:/systemd-1/dbus.service \_ udisks-daemon: polling /dev/sr0
|
||
1759 lennart name=systemd:/user/lennart/1 gnome-screensaver
|
||
1780 lennart name=systemd:/user/lennart/1 /usr/libexec/gvfsd-trash --spawner :1.9 /org/gtk/gvfs/exec_spaw/0
|
||
1864 lennart name=systemd:/user/lennart/1 /usr/libexec/gvfs-afc-volume-monitor
|
||
1874 lennart name=systemd:/user/lennart/1 /usr/libexec/gconf-im-settings-daemon
|
||
1903 lennart name=systemd:/user/lennart/1 /usr/libexec/gvfsd-burn --spawner :1.9 /org/gtk/gvfs/exec_spaw/1
|
||
1909 lennart name=systemd:/user/lennart/1 gnome-terminal
|
||
1913 lennart name=systemd:/user/lennart/1 \_ gnome-pty-helper
|
||
1914 lennart name=systemd:/user/lennart/1 \_ bash
|
||
29231 lennart name=systemd:/user/lennart/1 | \_ ssh tango
|
||
2221 lennart name=systemd:/user/lennart/1 \_ bash
|
||
4193 lennart name=systemd:/user/lennart/1 | \_ ssh tango
|
||
2461 lennart name=systemd:/user/lennart/1 \_ bash
|
||
29219 lennart name=systemd:/user/lennart/1 | \_ emacs systemd-for-admins-1.txt
|
||
15113 lennart name=systemd:/user/lennart/1 \_ bash
|
||
27251 lennart name=systemd:/user/lennart/1 \_ empathy
|
||
29504 lennart name=systemd:/user/lennart/1 \_ ps xawf -eo pid,user,cgroup,args
|
||
1968 lennart name=systemd:/user/lennart/1 ssh-agent
|
||
1994 lennart name=systemd:/user/lennart/1 gpg-agent --daemon --write-env-file
|
||
18679 lennart name=systemd:/user/lennart/1 /bin/sh /usr/lib64/firefox-3.6/run-mozilla.sh /usr/lib64/firefox-3.6/firefox
|
||
18741 lennart name=systemd:/user/lennart/1 \_ /usr/lib64/firefox-3.6/firefox
|
||
28900 lennart name=systemd:/user/lennart/1 \_ /usr/lib64/nspluginwrapper/npviewer.bin --plugin /usr/lib64/mozilla/plugins/libflashplayer.so --connection /org/wrapper/NSPlugins/libflashplayer.so/18741-6
|
||
4016 root name=systemd:/systemd-1/sysinit.service /usr/sbin/bluetoothd --udev
|
||
4094 smmsp name=systemd:/systemd-1/sendmail.service sendmail: Queue runner@01:00:00 for /var/spool/clientmqueue
|
||
4096 root name=systemd:/systemd-1/sendmail.service sendmail: accepting connections
|
||
4112 ntp name=systemd:/systemd-1/ntpd.service /usr/sbin/ntpd -n -u ntp:ntp -g
|
||
27262 lennart name=systemd:/user/lennart/1 /usr/libexec/mission-control-5
|
||
27265 lennart name=systemd:/user/lennart/1 /usr/libexec/telepathy-haze
|
||
27268 lennart name=systemd:/user/lennart/1 /usr/libexec/telepathy-logger
|
||
27270 lennart name=systemd:/user/lennart/1 /usr/libexec/dconf-service
|
||
27280 lennart name=systemd:/user/lennart/1 /usr/libexec/notification-daemon
|
||
27284 lennart name=systemd:/user/lennart/1 /usr/libexec/telepathy-gabble
|
||
27285 lennart name=systemd:/user/lennart/1 /usr/libexec/telepathy-salut
|
||
27297 lennart name=systemd:/user/lennart/1 /usr/libexec/geoclue-yahoo
|
||
\end{Verbatim}
|
||
(Данный листинг был сокращен за счет удаления из него строк, описывающих
|
||
потоки ядра, так как они никак не~относятся к обсуждаемой нами теме.)
|
||
\end{landscape}
|
||
|
||
Обратите внимание на третий столбец, показывающий имя контрольной группы,
|
||
в которую systemd поместил данный процесс. Например, процесс +udev+
|
||
находится в группе +name=systemd:/systemd-1/sysinit.service+. В эту группу
|
||
входят процессы, порожденные службой +sysinit.service+, которая запускается
|
||
на ранней стадии загрузки.
|
||
|
||
Вы можете очень сильно упростить себе работу, если назначите для
|
||
вышеприведенной команды какой-нибудь простой и короткий псевдоним, например
|
||
\begin{Verbatim}
|
||
alias psc='ps xawf -eo pid,user,cgroup,args'
|
||
\end{Verbatim}
|
||
---~теперь для получения исчерпывающей информации по процессам достаточно будет
|
||
нажать всего четыре клавиши.
|
||
|
||
Альтернативный способ получить ту же информацию~--- воспользоваться утилитой
|
||
+systemd-cgls+, входящей в комплект поставки systemd. Она отображает иерархию
|
||
контрольных групп в виде псевдографической диаграммы-дерева\footnote{Прим.
|
||
перев.: Стоит заметить, что в нижеприведенном листинге используется
|
||
ASCII-псевдографика. Между тем, уже довольно давно отображение иерархии
|
||
процессов в +systemd-cgls+ и +systemctl status+ производится в более
|
||
выразительной ├юникодной └псевдографике. ASCII-вариант выводится только в том
|
||
случае, если консоль не~поддерживает Unicode.}:
|
||
|
||
\begin{landscape}
|
||
\begin{Verbatim}[fontsize=\small]
|
||
$ systemd-cgls
|
||
+ 2 [kthreadd]
|
||
[...]
|
||
+ 4281 [flush-8:0]
|
||
+ user
|
||
| \ lennart
|
||
| \ 1
|
||
| + 1495 pam: gdm-password
|
||
| + 1521 gnome-session
|
||
| + 1534 dbus-launch --sh-syntax --exit-with-session
|
||
| + 1535 /bin/dbus-daemon --fork --print-pid 5 --print-address 7 --session
|
||
| + 1603 /usr/libexec/gconfd-2
|
||
| + 1612 /usr/libexec/gnome-settings-daemon
|
||
| + 1615 /ushr/libexec/gvfsd
|
||
| + 1621 metacity
|
||
| + 1626 /usr/libexec//gvfs-fuse-daemon /home/lennart/.gvfs
|
||
| + 1634 /usr/bin/pulseaudio --start --log-target=syslog
|
||
| + 1635 gnome-panel
|
||
| + 1638 nautilus
|
||
| + 1640 /usr/libexec/polkit-gnome-authentication-agent-1
|
||
| + 1641 /usr/bin/seapplet
|
||
| + 1644 gnome-volume-control-applet
|
||
| + 1645 /usr/libexec/bonobo-activation-server --ac-activate --ior-output-fd=24
|
||
| + 1646 /usr/sbin/restorecond -u
|
||
| + 1649 /usr/libexec/pulse/gconf-helper
|
||
| + 1652 /usr/bin/devilspie
|
||
| + 1662 nm-applet --sm-disable
|
||
| + 1664 gnome-power-manager
|
||
| + 1665 /usr/libexec/gdu-notification-daemon
|
||
| + 1668 /usr/libexec/im-settings-daemon
|
||
| + 1670 /usr/libexec/evolution/2.32/evolution-alarm-notify
|
||
| + 1672 /usr/bin/python /usr/share/system-config-printer/applet.py
|
||
| + 1674 /usr/lib64/deja-dup/deja-dup-monitor
|
||
| + 1675 abrt-applet
|
||
| + 1677 bluetooth-applet
|
||
| + 1678 gpk-update-icon
|
||
| + 1701 /usr/libexec/gvfs-gdu-volume-monitor
|
||
| + 1707 /usr/bin/gnote --panel-applet --oaf-activate-iid=OAFIID:GnoteApplet_Factory --oaf-ior-fd=22
|
||
| + 1725 /usr/libexec/clock-applet
|
||
| + 1727 /usr/libexec/wnck-applet
|
||
| + 1729 /usr/libexec/notification-area-applet
|
||
| + 1759 gnome-screensaver
|
||
| + 1780 /usr/libexec/gvfsd-trash --spawner :1.9 /org/gtk/gvfs/exec_spaw/0
|
||
| + 1864 /usr/libexec/gvfs-afc-volume-monitor
|
||
| + 1874 /usr/libexec/gconf-im-settings-daemon
|
||
| + 1882 /usr/libexec/gvfs-gphoto2-volume-monitor
|
||
| + 1903 /usr/libexec/gvfsd-burn --spawner :1.9 /org/gtk/gvfs/exec_spaw/1
|
||
| + 1909 gnome-terminal
|
||
| + 1913 gnome-pty-helper
|
||
| + 1914 bash
|
||
| + 1968 ssh-agent
|
||
| + 1994 gpg-agent --daemon --write-env-file
|
||
| + 2221 bash
|
||
| + 2461 bash
|
||
| + 4193 ssh tango
|
||
| + 15113 bash
|
||
| + 18679 /bin/sh /usr/lib64/firefox-3.6/run-mozilla.sh /usr/lib64/firefox-3.6/firefox
|
||
| + 18741 /usr/lib64/firefox-3.6/firefox
|
||
| + 27251 empathy
|
||
| + 27262 /usr/libexec/mission-control-5
|
||
| + 27265 /usr/libexec/telepathy-haze
|
||
| + 27268 /usr/libexec/telepathy-logger
|
||
| + 27270 /usr/libexec/dconf-service
|
||
| + 27280 /usr/libexec/notification-daemon
|
||
| + 27284 /usr/libexec/telepathy-gabble
|
||
| + 27285 /usr/libexec/telepathy-salut
|
||
| + 27297 /usr/libexec/geoclue-yahoo
|
||
| + 28900 /usr/lib64/nspluginwrapper/npviewer.bin --plugin /usr/lib64/mozilla/plugins/libflashplayer.so --connection /org/wrapper/NSPlugins/libflashplayer.so/18741-6
|
||
| + 29219 emacs systemd-for-admins-1.txt
|
||
| + 29231 ssh tango
|
||
| \ 29519 systemd-cgls
|
||
\ systemd-1
|
||
+ 1 /sbin/init
|
||
+ ntpd.service
|
||
| \ 4112 /usr/sbin/ntpd -n -u ntp:ntp -g
|
||
+ systemd-logger.service
|
||
| \ 1499 /lib/systemd/systemd-logger
|
||
+ accounts-daemon.service
|
||
| \ 1496 /usr/libexec/accounts-daemon
|
||
+ rtkit-daemon.service
|
||
| \ 1473 /usr/libexec/rtkit-daemon
|
||
+ console-kit-daemon.service
|
||
| \ 1408 /usr/sbin/console-kit-daemon --no-daemon
|
||
+ prefdm.service
|
||
| + 1376 /usr/sbin/gdm-binary -nodaemon
|
||
| + 1391 /usr/libexec/gdm-simple-slave --display-id /org/gnome/DisplayManager/Display1 --force-active-vt
|
||
| + 1394 /usr/bin/Xorg :0 -nr -verbose -auth /var/run/gdm/auth-for-gdm-f2KUOh/database -nolisten tcp vt1
|
||
| + 1419 /usr/bin/dbus-launch --exit-with-session
|
||
| \ 1511 /usr/bin/gnome-keyring-daemon --daemonize --login
|
||
+ getty@.service
|
||
| + tty6
|
||
| | \ 1346 /sbin/mingetty tty6
|
||
| + tty4
|
||
| | \ 1343 /sbin/mingetty tty4
|
||
| + tty5
|
||
| | \ 1342 /sbin/mingetty tty5
|
||
| + tty3
|
||
| | \ 1339 /sbin/mingetty tty3
|
||
| \ tty2
|
||
| \ 1332 /sbin/mingetty tty2
|
||
+ abrtd.service
|
||
| \ 1317 /usr/sbin/abrtd -d -s
|
||
+ crond.service
|
||
| \ 1344 crond
|
||
+ sshd.service
|
||
| \ 1362 /usr/sbin/sshd
|
||
+ sendmail.service
|
||
| + 4094 sendmail: Queue runner@01:00:00 for /var/spool/clientmqueue
|
||
| \ 4096 sendmail: accepting connections
|
||
+ haldaemon.service
|
||
| + 1249 hald
|
||
| + 1250 hald-runner
|
||
| + 1273 hald-addon-input: Listening on /dev/input/event3 /dev/input/event9 /dev/input/event1 /dev/input/event7 /dev/input/event2 /dev/input/event0 /dev/input/event8
|
||
| + 1275 /usr/libexec/hald-addon-rfkill-killswitch
|
||
| + 1284 /usr/libexec/hald-addon-leds
|
||
| + 1285 /usr/libexec/hald-addon-generic-backlight
|
||
| \ 1287 /usr/libexec/hald-addon-acpi
|
||
+ irqbalance.service
|
||
| \ 1210 irqbalance
|
||
+ avahi-daemon.service
|
||
| + 1175 avahi-daemon: running [epsilon.local]
|
||
+ NetworkManager.service
|
||
| + 1171 /usr/sbin/NetworkManager --no-daemon
|
||
| \ 4028 /sbin/dhclient -d -4 -sf /usr/libexec/nm-dhcp-client.action -pf /var/run/dhclient-wlan0.pid -lf /var/lib/dhclient/dhclient-7d32a784-ede9-4cf6-9ee3-60edc0bce5ff-wlan0.lease -cf /var/run/nm-dhclient-wlan0.conf wlan0
|
||
+ rsyslog.service
|
||
| \ 1193 /sbin/rsyslogd -c 4
|
||
+ mdmonitor.service
|
||
| \ 1207 mdadm --monitor --scan -f --pid-file=/var/run/mdadm/mdadm.pid
|
||
+ cups.service
|
||
| \ 1195 cupsd -C /etc/cups/cupsd.conf
|
||
+ auditd.service
|
||
| + 1131 auditd
|
||
| + 1133 /sbin/audispd
|
||
| \ 1135 /usr/sbin/sedispatch
|
||
+ dbus.service
|
||
| + 1096 /bin/dbus-daemon --system --address=systemd: --nofork --systemd-activation
|
||
| + 1216 /usr/sbin/modem-manager
|
||
| + 1219 /usr/libexec/polkit-1/polkitd
|
||
| + 1242 /usr/sbin/wpa_supplicant -c /etc/wpa_supplicant/wpa_supplicant.conf -B -u -f /var/log/wpa_supplicant.log -P /var/run/wpa_supplicant.pid
|
||
| + 1453 /usr/libexec/upowerd
|
||
| + 1733 /usr/libexec/udisks-daemon
|
||
| + 1747 udisks-daemon: polling /dev/sr0
|
||
| \ 29509 /usr/libexec/packagekitd
|
||
+ dev-mqueue.mount
|
||
+ dev-hugepages.mount
|
||
\ sysinit.service
|
||
+ 455 /sbin/udevd -d
|
||
+ 4016 /usr/sbin/bluetoothd --udev
|
||
+ 28188 /sbin/udevd -d
|
||
\ 28191 /sbin/udevd -d
|
||
\end{Verbatim}
|
||
(Как и предыдущий, этот листинг был сокращен за счет удаления перечня потоков
|
||
ядра.)
|
||
\end{landscape}
|
||
|
||
Как видно из листинга, данная команда наглядно показывает принадлежность
|
||
процессов к их контрольным группам, а следовательно, и к службам, так как
|
||
systemd именует группы в соответствии с названиями служб. Например, из
|
||
приведенного листинга нетрудно понять, что служба системного аудита
|
||
+auditd.service+ порождает три отдельных процесса: +auditd+,
|
||
+audispd+ и +sedispatch+.
|
||
|
||
Наиболее внимательные читатели, вероятно, уже заметили, что некоторые процессы
|
||
помещены в группу +/user/lennart/1+. Дело в том, что systemd занимается
|
||
отслеживанием и группировкой не~только процессов, относящихся к системным
|
||
службам, но и процессов, запущенных в рамках пользовательских сеансов. В
|
||
последующих статьях мы обсудим этот вопрос более подробно.
|
||
|
||
\section{Преобразование SysV init-скрипта в systemd service-файл}
|
||
\label{sec:convert}
|
||
|
||
Традиционно, службы Unix и Linux (демоны) запускаются через SysV init-скрипты.
|
||
Эти скрипты пишутся на языке Bourne Shell (+/bin/sh+), располагаются в
|
||
специальном каталоге (обычно +/etc/rc.d/init.d/+) и вызываются с одним из
|
||
стандартных параметров (+start+, +stop+, +reload+ и т.п.)~--- таким образом
|
||
указывается действие, которое необходимо произвести над службой (запустить,
|
||
остановить, заставить перечитать конфигурацию). При запуске службы такой
|
||
скрипт, как правило, вызывает бинарник демона, который, в свою очередь,
|
||
форкается, порождая фоновый процесс (т.е. демонизируется). Заметим, что
|
||
shell-скрипты, как правило, отличается низкой скоростью работы, излишней
|
||
подробностью изложения и крайней хрупкостью. Читать их, из-за изобилия
|
||
всевозможного вспомогательного и дополнительного кода, чрезвычайно тяжело.
|
||
Впрочем, нельзя не~упомянуть, что эти скрипты являются очень гибким
|
||
инструментом (ведь, по сути, это всего лишь код, который можно модифицировать
|
||
как угодно). С другой стороны, многие задачи, возникающие при работе со
|
||
службами, довольно тяжело решить средствами shell-скриптов. К таким
|
||
задачам относятся: организация параллельного исполнения, корректное
|
||
отслеживание процессов, конфигурирование различных параметров среды исполнения
|
||
процесса. systemd обеспечивает совместимость с init-скриптами, однако, с учетом
|
||
описанных выше их недостатков, более правильным решением будет использование
|
||
штатных service-файлов systemd для всех установленных в системе служб. Стоит
|
||
отметить что, в отличие от init-скриптов, которые часто приходится
|
||
модифицировать при переносе из одного дистрибутива в другой, один и тот же
|
||
service-файл будет работать в любом дистрибутиве, использующем systemd (а таких
|
||
дистрибутивов с каждым днем становится все больше и больше). Далее мы вкратце
|
||
рассмотрим процесс преобразования SysV init-скрипта в service-файл systemd.
|
||
Вообще говоря, service-файл должен создаваться разработчиками каждого демона, и
|
||
включаться в комплект его поставки. Если вам удалось успешно создать
|
||
работоспособный service-файл для какого-либо демона, настоятельно рекомендуем
|
||
вам отправить этот файл разработчикам. Вопросы по полноценной интеграции
|
||
демонов с systemd, с максимальным использованием всех его возможностей, будут
|
||
рассмотрены в последующих статьях этого цикла, пока же ограничимся ссылкой на
|
||
\href{http://www.freedesktop.org/software/systemd/man/daemon.html}{страницу}
|
||
официальной документации.
|
||
|
||
Итак, приступим. В качестве примера возьмем init-скрипт демона ABRT (Automatic
|
||
Bug Reporting Tool, службы, занимающейся сбором crash dump'ов). Исходный
|
||
скрипт (в варианте для дистрибутива Fedora) можно загрузить
|
||
\href{http://0pointer.de/public/abrtd}{здесь}.
|
||
|
||
Начнем с того, что прочитаем исходный скрипт (неожиданный ход, правда?) и
|
||
выделим полезную информацию из груды хлама. Практически у всех init-скриптов
|
||
б\'{о}льшая часть кода является чисто вспомогательной, и мало чем отличается от
|
||
одного скрипта к другому. Как правило, при создании новых скриптов этот код
|
||
просто копируется из уже существующих (разработка в стиле copy-paste). Итак,
|
||
в исследуемом скрипте нас интересует следующая информация:
|
||
|
||
\begin{itemize}
|
||
\item Строка описания службы: <<Daemon to detect crashing apps>>. Как
|
||
нетрудно заметить, комментарии в заголовке скрипта весьма
|
||
пространны и описывают не~сколько саму службу, сколько
|
||
скрипт, ее запускающий. service-файлы systemd тоже содержат
|
||
описание, но оно относится исключительно к службе, а не~к
|
||
service-файлу.
|
||
\item LSB-заголовок\footnote{LSB-заголовок~--- определенная в
|
||
\href{http://refspecs.freestandards.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic/initscrcomconv.html}{Linux
|
||
Standard Base} схема записи метаданных о службах в блоках
|
||
комментариев соответствующих init-скриптов. Изначально эта
|
||
схема была введена именно для того, чтобы стандартизировать
|
||
init-скрипты во всех дистрибутивах. Однако разработчики
|
||
многих дистрибутивов не~считают нужным точно исполнять
|
||
требования LSB, и поэтому формы представления метаданных в
|
||
различных дистрибутивах могут отличаться. Вследствие этого,
|
||
при переносе init-скрипта из одного дистрибутива в другой,
|
||
скрипт приходится модифицировать. Например, демон пересылки
|
||
почты при описании зависимостей может именоваться
|
||
+MTA+ или +smtpdaemon+ (Fedora), +smtp+
|
||
(openSUSE), +mail-transport-agent+ (Debian и Ubuntu),
|
||
+mail-transfer-agent+. Таким образом, можно утверждать, что
|
||
стандарт LSB не~справляется с возложенной на него задачей.},
|
||
содержащий информацию о зависимостях. systemd, базирующийся
|
||
на идеях socket-активации, обычно не~требует явного описания
|
||
зависимостей (либо требует самого минимального описания).
|
||
Заметим, что основополагающие принципы systemd, включая
|
||
socket-активацию, рассмотрены в статье
|
||
\href{http://0pointer.de/blog/projects/systemd.html}{Rethinking
|
||
PID~1}, в которой systemd был впервые представлен широкой
|
||
публике\footnote{Прим. перев.: Ее русский перевод (сделанный
|
||
совершенно независимо от данного документа, совсем другим
|
||
человеком) можно прочитать здесь:
|
||
\href{http://tux-the-penguin.blogspot.com/2010/09/systemd.html}{часть~1},
|
||
\href{http://tux-the-penguin.blogspot.com/2010/09/systemd-ii.html}{часть~2}.}.
|
||
Возвращаясь к нашему примеру: в данном случае ценной
|
||
информацией о зависимостях является только строка
|
||
+Required-Start: $syslog+, сообщающая, что для работы
|
||
abrtd требуется демон системного лога. Информация о второй
|
||
зависимости, +$local_fs+, является избыточной, так как
|
||
systemd приступает к запуску служб уже после того, как все
|
||
файловые системы готовы для работы.
|
||
\item Также, LSB-заголовок сообщает, что данная служба должна быть
|
||
запущена на уровнях исполнения (runlevels) 3 (консольный
|
||
многопользовательский) и 5 (графический
|
||
многопользовательской).
|
||
\item Исполняемый бинарник демона называется +/usr/sbin/abrtd+.
|
||
\end{itemize}
|
||
|
||
Вот и вся полезная информация. Все остальное содержимое 115-строчного скрипта
|
||
является чисто вспомогательным кодом: операции синхронизации и упорядочивания
|
||
запуска (код, относящийся к lock-файлам), вывод информационных сообщений
|
||
(команды +echo+), разбор входных параметров (монструозный блок
|
||
+case+).
|
||
|
||
На основе приведенной выше информации, мы можем написать следующий
|
||
service-файл:
|
||
\begin{Verbatim}
|
||
[Unit]
|
||
Description=Daemon to detect crashing apps
|
||
After=syslog.target
|
||
|
||
[Service]
|
||
ExecStart=/usr/sbin/abrtd
|
||
Type=forking
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
\end{Verbatim}
|
||
|
||
Рассмотрим этот файл поподробнее.
|
||
|
||
Секция +[Unit]+ содержит самую общую информацию о службе. Не~будем
|
||
забывать, что systemd управляет не~только службами, но и многими другими
|
||
объектами, в частности, устройствами, точками монтирования, таймерами и т.п.
|
||
Общее наименование всех этих объектов~--- юнит (unit). Одноименная секция
|
||
конфигурационного файла определяет наиболее общие свойства, которые могут
|
||
быть присущи любому юниту. В нашем случае это, во-первых, строка описания, и
|
||
во-вторых, указание, что данный юнит рекомендуется активировать после запуска
|
||
демона системного лога\footnote{Строго говоря, эту зависимость здесь
|
||
указывать не~нужно~--- в системах, в которых демон системного лога активируется
|
||
через сокет, данная зависимость является избыточной. Современные реализации
|
||
демона системного лога (например, rsyslog начиная с пятой версии)
|
||
поддерживают активацию через сокет. В системах, использующих такие
|
||
реализации, явное указание +After=syslog.target+ будет избыточным, так
|
||
как соответствующая функциональность поддерживается автоматически. Однако,
|
||
эту строчку все-таки стоит указать для обеспечения совместимости с системами,
|
||
использующими устаревшие реализации демона системного лога.}. Эта информация,
|
||
как мы помним, была указана в LSB-заголовке исходного init-скрипта. В нашем
|
||
конфигурационном файле мы указываем зависимость от демона системного лога при
|
||
помощи директивы +After+, указывающей на юнит +syslog.taget+. Это
|
||
специальный юнит, позволяющий ссылаться на любую реализацию демона системного
|
||
лога, независимо от используемой программы (например, rsyslog или syslog-ng)
|
||
и типа активации (как обычной службы или через log-сокет). Подробнее о таких
|
||
специальных юнитах можно почитать
|
||
\href{http://www.freedesktop.org/software/systemd/man/systemd.special.html}%
|
||
{страницу} официальной документации. Обратите внимание, что директива +After+,
|
||
в отсутствие директивы +Requires+, задает лишь порядок загрузки, но
|
||
не~задает жесткой зависимости. То есть, если при загрузке конфигурация
|
||
systemd будет предписывать запуск как демона системного лога, так и abrtd, то
|
||
сначала будет запущен демон системного лога, и только потом abrtd. Если же
|
||
конфигурация не~будет содержать явного указания запустить демон системного
|
||
лога, он не~будет запущен даже при запуске abrtd. И это поведение нас
|
||
полностью устраивает, так как abrtd прекрасно может обходиться и без демона
|
||
системного лога. В противном случае, мы могли бы воспользоваться директивой
|
||
+Requires+, задающей жесткую зависимость между юнитами.
|
||
|
||
Следующая секция, +[Service]+, содержит информацию о службе. Сюда включаются
|
||
настройки, относящие именно к службам, но не~к другим типам юнитов. В нашем
|
||
случае, таких настроек две: +ExecStart+, определяющая расположение бинарника
|
||
демона и аргументы, с которыми он будет вызван (в нашем случае они
|
||
отсутствуют), и +Type+, позволяющая задать метод, по которому systemd определит
|
||
окончание периода запуска службы. Традиционный для Unix метод демонизации
|
||
процесса, когда исходный процесс форкается, порождая демона, после чего
|
||
завершается, описывается типом +forking+ (как в нашем случае). Таким образом,
|
||
systemd считает службу запущенной с момента завершения работы исходного
|
||
процесса, и рассматривает в качестве основного процесса этой службы
|
||
порожденный им процесс-демон.
|
||
|
||
И наконец, третья секция, +[Install]+. Она содержит рекомендации по
|
||
установке конкретного юнита, указывающие, в каких ситуациях он должен быть
|
||
активирован. В нашем случае, служба abrtd запускается при активации юнита
|
||
+multi-user.target+. Это специальный юнит, примерно соответствующий роли
|
||
третьего уровня исполнения классического SysV\footnote{В том контексте, в
|
||
котором он используется в большинстве дистрибутивов семейства Red Hat, а
|
||
именно, многопользовательский режим без запуска графической оболочки.}.
|
||
Директива +WantedBy+ никак не~влияет на уже работающую службу, но она
|
||
играет важную роль при выполнении команды +systemctl enable+, задавая, в каких
|
||
условиях должен активироваться устанавливаемый юнит. В нашем примере, служба
|
||
abrtd будет активироваться при переходе в состояние +multi-user.target+,
|
||
т.е., при каждой нормальной\footnote{Прим. перев.: К <<ненормальным>> загрузкам
|
||
можно отнести, например, загрузки в режимах +emergency.target+ или
|
||
+rescue.target+ (является аналогом первого уровня исполнения в классической
|
||
SysV).} загрузке\footnote{Обратите внимание, что режим графической загрузки в
|
||
systemd (+graphical.target+, аналог runlevel 5 в SysV) является надстройкой над
|
||
режимом многопользовательской консольной загрузки (+multi-user.target+, аналог
|
||
runlevel 3 в SysV). Таким образом, все службы, запускаемые в режиме
|
||
+multi-user.target+, будут также запускаться и в режиме +graphical.target+.}.
|
||
|
||
Вот и все. Мы получили минимальный рабочий service-файл systemd. Чтобы
|
||
проверить его работоспособность, скопируем его в
|
||
+/etc/systemd/system/abrtd.service+, после чего командой
|
||
+systemctl daemon-reload+ уведомим systemd об изменении конфигурации.
|
||
Теперь нам остается только запустить нашу службу:
|
||
+systemctl start abrtd.service+. Проверить состояние службы можно
|
||
командой +systemctl status abrtd.service+, а чтобы остановить ее, нужно
|
||
скомандовать +systemctl stop abrtd.service+. И наконец, команда
|
||
+systemctl enable abrtd.service+ выполнит установку service-файла,
|
||
обеспечив его активацию при каждой загрузке (аналог +chkconfig abrtd on+
|
||
в классическом SysV).
|
||
|
||
Приведенный выше service-файл является практический точным переводом
|
||
исходного init-скрипта, и он никак не~использует широкий спектр возможностей,
|
||
предоставляемых systemd. Ниже приведен немного улучшенный вариант этого же
|
||
файла:
|
||
|
||
\begin{Verbatim}
|
||
[Unit]
|
||
Description=ABRT Automated Bug Reporting Tool
|
||
After=syslog.target
|
||
|
||
[Service]
|
||
Type=dbus
|
||
BusName=com.redhat.abrt
|
||
ExecStart=/usr/sbin/abrtd -d -s
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
\end{Verbatim}
|
||
|
||
Чем же новый вариант отличается от предыдущего? Ну, прежде всего, мы уточнили
|
||
описание службы. Однако, ключевым изменением является замена значения +Type+ с
|
||
+forking+ на +dbus+ и связанные с ней изменения: добавление имени службы в шине
|
||
D-Bus (директива +BusName+) и задание дополнительных аргументов abrtd
|
||
<<+-d -s+>>. Но зачем вообще нужна эта замена? Каков ее практический смысл?
|
||
Чтобы ответить на этот вопрос, мы снова возвращаемся к демонизации. В ходе
|
||
данной операции процесс дважды форкается и отключается от всех терминалов. Это
|
||
очень удобно при запуске демона через скрипт, но в случае использования таких
|
||
продвинутых систем инициализации, как systemd, подобное поведение не~дает
|
||
никаких преимуществ, но вызывает неоправданные задержки. Даже если мы оставим в
|
||
стороне вопрос скорости загрузки, останется такой важный аспект, как
|
||
отслеживание состояния служб. systemd решает и эту задачу, контролируя работу
|
||
службы и при необходимости реагируя на различные события. Например, при
|
||
неожиданном падении основного процесса службы, systemd должен зарегистрировать
|
||
идентификатор и код выхода процесса, также, в зависимости от настроек, он может
|
||
попытаться перезапустить службу, либо активировать какой-либо заранее заданный
|
||
юнит. Операция демонизации несколько затрудняет решение этих задач, так как
|
||
обычно довольно сложно найти связь демонизированного процесса с исходным
|
||
(собственно, смысл демонизации как раз и сводится к уничтожению этой связи) и,
|
||
соответственно, для systemd сложнее определить, какой из порожденных в рамках
|
||
данной службы процессов является основным. Чтобы упростить для него решение этой
|
||
задачи, мы и воспользовались типом запуска +dbus+. Он подходит для всех служб,
|
||
которые в конце процесса инициализации регистрируют свое имя на шине
|
||
D-Bus\footnote{В настоящее время практически все службы дистрибутива Fedora
|
||
после запуска регистрируется на шине D-Bus.}. ABRTd относится к ним. С новыми
|
||
настройками, systemd запустит процесс abrtd, который уже не~будет форкаться
|
||
(согласно указанным нами ключам <<+-d -s+>>), и в качестве момента окончания
|
||
периода запуска данной службы systemd будет рассматривать момент регистрации
|
||
имени +com.redhat.abrt+ на шине D-Bus. В этом случае основным для данной службы
|
||
будет считаться процесс, непосредственно порожденный systemd. Таким образом,
|
||
systemd располагает удобным методом для определения момента окончания запуска
|
||
службы, а также может легко отслеживать ее состояние.
|
||
|
||
Собственно, это все, что нужно было сделать. Мы получили простой
|
||
конфигурационный файл, в 10 строчках которого содержится больше полезной
|
||
информации, чем в 115 строках исходного init-скрипта. Добавляя в наш файл по
|
||
одной строчке, мы можем использовать различные полезные функции systemd,
|
||
создание аналога которых в традиционном init-скрипте потребовало бы
|
||
значительных усилий. Например, добавив строку +Restart=restart-always+, мы
|
||
приказываем systemd автоматически перезапускать службу после каждого ее
|
||
падения. Или, например, добавив +OOMScoreAdjust=-500+, мы попросим ядро сберечь
|
||
эту службу, даже если OOM Killer выйдет на тропу войны. А если мы добавим
|
||
строчку +CPUSchedulingPolicy=idle+, процесс abrtd будет работать только в те
|
||
моменты, когда система больше ничем не~занята, что позволит не~создавать помех
|
||
для процессов, активно использующих CPU.
|
||
|
||
За более подробным описанием всех опций настройки, вы можете обратиться к
|
||
страницам руководства
|
||
\href{http://www.freedesktop.org/software/systemd/man/systemd.unit.html}{systemd.unit},
|
||
\href{http://www.freedesktop.org/software/systemd/man/systemd.service.html}{systemd.service},
|
||
\href{http://www.freedesktop.org/software/systemd/man/systemd.exec.html}{systemd.exec}.
|
||
Полный список доступных страниц можно просмотреть
|
||
\href{http://www.freedesktop.org/software/systemd/man/}{здесь}.
|
||
|
||
Конечно, отнюдь не~все init-скрипты так же легко преобразовать в
|
||
service-файлы. Но, к счастью, <<проблемных>> скриптов не~так уж и много.
|
||
|
||
\section{Убить демона}
|
||
|
||
Убить системного демона нетрудно, правда? Или\ldots{} все не~так просто?
|
||
|
||
Если ваш демон функционирует как один процесс, все действительно очень просто.
|
||
Вы командуете +killall rsyslogd+, и демон системного лога останавливается.
|
||
Впрочем, этот метод не~вполне корректен, так как он действует не~только на
|
||
самого демона, но и на другие процессы с тем же именем. Иногда подобное
|
||
поведение может привести к неприятным последствиям. Более правильным будет
|
||
использование pid-файла: \verb+kill $(cat /var/run/syslogd.pid)+. Вот, вроде
|
||
бы, и все, что вам нужно\ldots{} Или мы упускаем еще что-то?
|
||
|
||
Действительно, мы забываем одну простую вещь: существуют службы, такие, как
|
||
Apache, crond, atd, которые по роду служебной деятельности должны запускать
|
||
дочерние процессы. Это могут быть совершенно посторонние, указанные
|
||
пользователем программы (например, задачи cron/at, CGI-скрипты) или полноценные
|
||
серверные процессы (например, Apache workers). Когда вы убиваете основной
|
||
процесс, он может остановить все дочерние процессы. А может и не~остановить. В
|
||
самом деле, если служба функционирует в штатном режиме, ее обычно останавливают
|
||
командой +stop+. К прямому вызову +kill+ администратор, как правило,
|
||
прибегает только в аварийной ситуации, когда служба работает неправильно и
|
||
может не~среагировать на стандартную команду остановки. Таким образом, убив,
|
||
например, основной сервер Apache, вы можете получить от него в наследство
|
||
работающие CGI-скрипты, причем их родителем автоматически станет PID~1 (init),
|
||
так что установить их происхождение будет не~так-то просто.
|
||
|
||
\href{http://www.freedesktop.org/wiki/Software/systemd}{systemd} спешит к нам
|
||
на помощь. Команда +systemctl kill+ позволит отправить сигнал всем
|
||
процессам, порожденным в рамках данной службы. Например:
|
||
|
||
\begin{Verbatim}
|
||
# systemctl kill crond.service
|
||
\end{Verbatim}
|
||
|
||
Вы можете быть уверены, что всем процессам службы cron будет отправлен сигнал
|
||
+SIGTERM+. Разумеется, можно отправить и любой другой сигнал. Скажем, если ваши
|
||
дела совсем уж плохи, вы можете воспользоваться и +SIGKILL+:
|
||
|
||
\begin{Verbatim}
|
||
# systemctl kill -s SIGKILL crond.service
|
||
\end{Verbatim}
|
||
|
||
После ввода этой команды, служба cron будет жестоко убита вместе со всеми ее
|
||
дочерними процессами, вне зависимости от того, сколько раз она форкалась, и
|
||
как бы она ни пыталась сбежать из-под нашего контроля при помощи двойного
|
||
форка или
|
||
\href{http://ru.wikipedia.org/wiki/Fork-%D0%B1%D0%BE%D0%BC%D0%B1%D0%B0}{форк-бомбардировки}%
|
||
\footnote{Прим. перев.: Стоит особо отметить, что использование контрольных
|
||
групп не~только упрощает процесс уничтожения форк-бомб, но и значительно
|
||
уменьшает ущерб от работающей форк-бомбы. Так как systemd автоматически помещает
|
||
каждую службу и каждый пользовательский сеанс в свою контрольную группу по
|
||
ресурсу процессорного времени, запуск форк-бомбы одним пользователем или службой
|
||
не~создаст значительных проблем с отзывчивостью системы у других пользователей и
|
||
служб. Таким образом, в качестве основной угрозы форк-бомбардировки остаются
|
||
лишь возможности исчерпания памяти и идентификаторов процессов (PID). Впрочем, и
|
||
их тоже можно легко устранить: достаточно задать соответствующие лимиты в
|
||
конфигурационном файле службы (см.
|
||
\href{http://www.freedesktop.org/software/systemd/man/systemd.exec.html}%
|
||
{systemd.exec(5)} и
|
||
\href{http://www.freedesktop.org/software/systemd/man/systemd.resource-control.html}%
|
||
{systemd.resource-control(5)}).}.
|
||
|
||
В некоторых случаях возникает необходимость отправить сигнал именно основному
|
||
процессу службы. Например, используя +SIGHUP+, мы можем заставить демона
|
||
перечитать файлы конфигурации. Разумеется, передавать HUP вспомогательным
|
||
процессам в этом случае совершенно необязательно. Для решения подобной задачи
|
||
неплохо подойдет и классический метод с pid-файлом, однако у systemd и на этот
|
||
случай есть простое решение, избавляющее вас от необходимости искать нужный
|
||
файл:
|
||
|
||
\begin{Verbatim}
|
||
# systemctl kill -s HUP --kill-who=main crond.service
|
||
\end{Verbatim}
|
||
|
||
Итак, что же принципиально новое привносит systemd в рутинный процесс
|
||
убийства демона? Прежде всего: впервые в истории Linux представлен способ
|
||
принудительной остановки службы, не~зависящий от того, насколько
|
||
добросовестно основной процесс службы выполняет свои обязательства по
|
||
остановке дочерних процессов. Как уже упоминалось выше, необходимость
|
||
отправить процессу +SIGTERM+ или +SIGKILL+ обычно возникает именно
|
||
в нештатной ситуации, когда вы уже не~можете быть уверены, что демон
|
||
корректно исполнит все свои обязанности.
|
||
|
||
После прочтения сказанного выше у вас может возникнуть вопрос: в чем разница
|
||
между +systemctl kill+ и +systemctl stop+? Отличие состоит в том,
|
||
что +kill+ просто отправляет сигнал заданному процессу, в то время как
|
||
+stop+ действует по <<официально>> определенному методу, вызывая команду,
|
||
определенную в параметре +ExecStop+ конфигурации службы. Обычно команды
|
||
+stop+ бывает вполне достаточно для остановки службы, и к +kill+
|
||
приходится прибегать только в крайних случаях, например, когда служба
|
||
<<зависла>> и не~реагирует на команды.
|
||
|
||
Кстати говоря, при использовании параметра <<+-s+>>, вы можете указывать
|
||
названия сигналов как с префиксом SIG, так и без него~--- оба варианта будут
|
||
работать.
|
||
|
||
В завершение стоит сказать, что для нас весьма интересным и неожиданным
|
||
оказался тот факт, что до появления systemd в Linux просто не~существовало
|
||
инструментов, позволяющих корректно отправить сигнал службе в целом, а
|
||
не~отдельному процессу.
|
||
|
||
\section{Три уровня выключения}
|
||
\label{sec:off}
|
||
|
||
В \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+\footnote{Прим. перев.: Впоследствии в программу
|
||
+systemctl+ была добавлена поддержка команд +mask+ и +unmask+,
|
||
упрощающих процесс блокирования и разблокирования юнитов.
|
||
Например, для блокирования службы +ntpd.service+ теперь
|
||
достаточно команды +systemctl mask ntpd.service+. Фактически
|
||
она сделает то же самое, что и приведенная выше команда +ln+.}.
|
||
После такой операции служба не~может быть запущена ни~вручную,
|
||
ни~автоматически. Символьная ссылка создается в каталоге
|
||
+/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+), что
|
||
является прямым указанием на запуск службы, если она еще
|
||
не~запущена. Вследствие таких ошибок, отключенная служба может
|
||
<<ожить>> в самый неподходящий момент.}.
|
||
\end{itemize}
|
||
|
||
После прочтения изложенного выше, у читателя может возникнуть вопрос: как
|
||
отменить произведенные изменения? Что ж, ничего сложного тут нет:
|
||
+systemctl start+ отменяет действия +systemctl stop+, +systemctl enable+
|
||
отменяет действие +systemctl disable+, а +rm+ отменяет действие
|
||
+ln+.
|
||
|
||
\section{Смена корня}
|
||
\label{sec:chroots}
|
||
|
||
Практически все администраторы и разработчики рано или поздно встречаются с
|
||
\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://www.freedesktop.org/software/systemd/man/systemd.service.html}{страницах}
|
||
\href{http://www.freedesktop.org/software/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://www.freedesktop.org/software/systemd/man/systemd.exec.html}%
|
||
{странице руководства}.)
|
||
|
||
Фактически, FSNS по множеству параметров превосходят +chroot()+. Скорее всего,
|
||
Avahi и RealtimeKit в ближайшем будущем перейдут от +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://www.freedesktop.org/software/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}
|
||
|
||
После быстрой загрузки вы получите приглашение оболочки, запущенной внутри
|
||
полноценной ОС, функционирующей в контейнере. Изнутри контейнера невозможно
|
||
увидеть процессы, которые находятся вне его. Контейнер сможет пользоваться сетью
|
||
хоста\footnote{Прим. перев.: Впоследствии в +systemd-nspawn+ была добавлена
|
||
опция +--private-network+, позволяющая изолировать систему внутри контейнера от
|
||
сети хоста: такая система будет видеть только свой собственный интерфейс
|
||
обратной петли.}, однако не~имеет возможности изменить ее настройки (это может
|
||
привести к серии ошибок в процессе загрузки гостевой ОС, но ни~одна из этих
|
||
ошибок не~должна быть критической). Контейнер получает доступ к +/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+\footnote{Прим. перев.: После объединения проектов systemd
|
||
и udev, эта служба называется +systemd-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 подготовит
|
||
соответствующее устройство, проверит и смонтирует ее.
|
||
|
||
После внесения изменений в +/etc/fstab+ и перезагрузки мы получаем:
|
||
\begin{Verbatim}
|
||
systemd[1]: Startup finished in 2s 47ms 112us (kernel) + 2s 663ms 942us (initrd)
|
||
+ 5s 540ms 522us (userspace) = 10s 251ms 576us.
|
||
\end{Verbatim}
|
||
|
||
Прекрасно! Несколькими простыми действиями мы выиграли почти семь секунд. И эти
|
||
два изменения исправляют только две наиболее очевидные проблемы. При более
|
||
аккуратном и детальном исследовании, обнаружится еще множество моментов, которые
|
||
можно улучшить. Например, на другом моем компьютере, лаптопе X300 двухлетней
|
||
давности (и даже два года назад он был не~самым быстрым на Земле), после
|
||
небольшой доработки, время загрузки до полноценной среды GNOME
|
||
составило около четырех секунд, и это еще не~предел совершенства.
|
||
|
||
+systemd-analyze blame+~--- простой и удобный инструмент для поиска медленно
|
||
запускающихся служб, однако он обладает одним существенным недостатком: он
|
||
не~показывает, насколько эффективно параллельный запуск снижает потерю времени
|
||
для медленно запускающихся служб. Чтобы вы могли наглядно оценить этот фактор,
|
||
мы подготовили команду +systemd-analyze plot+. Использовать ее очень просто:
|
||
\begin{Verbatim}
|
||
$ systemd-analyze plot > plot.svg
|
||
$ eog plot.svg
|
||
\end{Verbatim}
|
||
|
||
Она создает наглядные диаграммы, показывающие моменты запуска служб и время,
|
||
затраченное на их запуск, по отношению к другим службам. На текущий момент, она
|
||
не~показывает явно, кто кого ожидает, но догадаться обычно
|
||
несложно\footnote{Прим. перев.: Начиная с systemd 203, добавлена поддержка
|
||
специальной команды +systemd-analyze critical-chain+, которая выводит самую
|
||
медленную цепочку юнитов (цепь в графе зависимостей, формируемом при
|
||
загрузке). Время запуска всех юнитов в этой цепочке определяет итоговое время
|
||
загрузки системы.}.
|
||
|
||
Чтобы продемонстрировать эффект, порожденный двумя нашими оптимизациями,
|
||
приведем ссылки на соответствующие графики:
|
||
\href{http://0pointer.de/public/blame.svg}{до} и
|
||
\href{http://0pointer.de/public/blame2.svg}{после} (для полноты описания приведем также и
|
||
соответствующие данные +systemd-analyze blame+:
|
||
\href{http://0pointer.de/public/blame.txt}{до} и
|
||
\href{http://0pointer.de/public/blame2.txt}{после}).
|
||
|
||
У наиболее эрудированных читателей может возникнуть вопрос: как это соотносится с
|
||
программой \href{https://github.com/mmeeks/bootchart}{bootchart}? Действительно,
|
||
+systemd-analyze plot+ и +bootchart+ рисуют похожие графики. Однако, bootchart
|
||
является намного более мощным инструментом~--- он детально показывает, что
|
||
именно происходило во время загрузки, и отображает соответствующие графики
|
||
использования процессора и ввода-вывода. +systemd-analyze plot+ оперирует более
|
||
высокоуровневой информацией: сколько времени затратила та или иная служба во
|
||
время запуска, и какие службы были вынуждены ее ожидать. Используя оба этих
|
||
инструмента, вы значительно упростите себе поиск причин замедления вашей
|
||
загрузки.
|
||
|
||
Но прежде, чем вы вооружитесь описанными здесь средствами и начнете
|
||
отправлять багрепорты авторам и сопровождающим программ, которые замедляют вашу
|
||
загрузку, обдумайте все еще раз. Эти инструменты предоставляют вам <<сырую>>
|
||
информацию. Постарайтесь не~ошибиться, интерпретируя ее. Например, в
|
||
при рассмотрении приведенного выше примера я показал, что проблема была вовсе
|
||
не~в +udev-settle.service+, и не~в опросе устройств для ModemManager'а, а в
|
||
неудачной реализации подсистемы хранения, требующей ожидать окончания опроса
|
||
устройств для продолжения загрузки. Именно эту проблему и нужно исправлять.
|
||
Поэтому постарайтесь правильно определить источник проблем. Возлагайте вину на
|
||
тех, кто действительно виноват.
|
||
|
||
Как уже говорилось выше, все три описанных здесь инструмента доступны в
|
||
Fedora~15 <<из коробки>>.
|
||
|
||
Итак, какие же выводы мы можем сделать из этой истории?
|
||
\begin{itemize}
|
||
\item +systemd-analyze+~--- отличный инструмент. Фактически, это
|
||
встроенный профилировщик systemd.
|
||
\item Постарайтесь не~ошибиться, интерпретируя вывод профилировщика!
|
||
\item Всего два небольших изменения могут ускорить загрузку системы на
|
||
семь секунд.
|
||
\item Программное обеспечение, не~способное работать с динамически
|
||
меняющимся набором устройств, создает проблемы, и должно быть
|
||
исправлено.
|
||
\item Принудительное использование в стандартной установке Fedora~15
|
||
промышленной реализации подсистем хранения, возможно, является
|
||
не~самым правильным решением.
|
||
\end{itemize}
|
||
|
||
\section{Новые конфигурационные файлы}
|
||
|
||
Одно из ключевых достоинств
|
||
\href{http://www.freedesktop.org/wiki/Software/systemd}{systemd}~--- наличие
|
||
полного набора программ, необходимых на ранних стадиях загрузки, причем эти
|
||
программы написаны на простом, быстром, надежном и легко поддающемся
|
||
распараллеливанию языке C. Теперь можно отказаться от <<простыней>>
|
||
shell-скриптов, разработанных для этих задач различными дистрибутивами. Наш
|
||
<<Проект нулевой оболочки>>\footnote{Наш девиз~--- <<Первой оболочкой,
|
||
запускающейся при старте системы, должна быть GNOME shell>>. Формулировка
|
||
оставляет желать лучшего, но все же неплохо передает основную идею.} увенчался
|
||
полным успехом. Уже сейчас возможности предоставляемого нами инструментария
|
||
покрывают практически все нужды настольных и встраиваемых систем, а также
|
||
б\'{о}льшую часть потребностей серверов:
|
||
\begin{itemize}
|
||
\item Проверка и монтирование всех файловых систем.
|
||
\item Обновление и активация квот на всех файловых системах.
|
||
\item Установка имени хоста.
|
||
\item Настройка сетевого интерфейса обратной петли (+lo+).
|
||
\item Подгрузка правил SELinux, обновление
|
||
меток безопасности в динамических каталогах +/run+ и +/dev+.
|
||
\item Регистрация в ядре дополнительных бинарных форматов (например,
|
||
Java, Mono, WINE) через API-файловую систему +binfmt_misc+.
|
||
\item Установка системной локали.
|
||
\item Настройка шрифта и раскладки клавиатуры в консоли.
|
||
\item Создание, очистка, удаление временных файлов и каталогов.
|
||
\item Применение предписанных в +/etc/fstab+ опций к смонтированным
|
||
ранее API-файловым системам.
|
||
\item Применение настроек +sysctl+.
|
||
\item Поддержка технологии упреждающего чтения (read ahead), включая
|
||
автоматический сбор информации.
|
||
\item Обновление записей в +utmp+ при включении и выключении системы.
|
||
\item Сохранение и восстановление затравки для генерации случайных чисел
|
||
(random seed).
|
||
\item Принудительная загрузка указанных модулей ядра.
|
||
\item Поддержка шифрованных дисков и разделов.
|
||
\item Автоматический запуск +getty+ на serial-консолях.
|
||
\item Взаимодействие с Plymouth.
|
||
\item Создание уникального идентификатора системы.
|
||
\item Настройка часового пояса.
|
||
\end{itemize}
|
||
|
||
В стандартной установке Fedora~15 запуск shell-скриптов требуется только для
|
||
некоторых устаревших служб, а также для подсистемы хранения данных (поддержка
|
||
LVM, RAID и multipath). Если они вам не~нужны, вы легко можете отключить их, и
|
||
наслаждаться загрузкой, полностью очищенной от shell-костылей (лично я это
|
||
сделал уже давно). Такая загрузка является уникальной возможностью Linux-систем.
|
||
|
||
Большинство перечисленных выше компонентов настраиваются через конфигурационные
|
||
файлы в каталоге +/etc+. Некоторые из этих файлов стандартизированы для всех
|
||
дистрибутивов, и поэтому реализация их поддержки в наших инструментах
|
||
не~представляла особого труда. Например, это относится к файлам +/etc/fstab+,
|
||
+/etc/crypttab+, +/etc/sysctl.conf+. Однако множество других, нестандартно
|
||
расположенных файлов и каталогов вынуждали нас добавлять в код огромное
|
||
количество операторов +#ifdef+, чтобы обеспечить поддержку различных вариантов
|
||
расположения конфигураций в разных дистрибутивах. Такое положение дел сильно
|
||
усложняет жизнь нам всем, и при этом ничем не~оправдано~--- все эти файлы решают
|
||
одни и те же задачи, просто немного по-разному.
|
||
|
||
Чтобы улучшить ситуацию и установить единый стандарт расположения базовых
|
||
конфигурационных файлов во всех дистрибутивах, мы заставили systemd пользоваться
|
||
дистрибутивно-специфическими конфигурациями только в качестве \emph{резервного}
|
||
варианта~--- основным источником информации становится определенный нами
|
||
стандартный набор конфигурационных файлов. Разумеется, там, где это возможно, мы
|
||
старались не~придумывать чего-то принципиально нового, а брали лучшее из
|
||
решений, предложенных существующими дистрибутивами. Ниже приводится небольшой
|
||
обзор этого нового набора конфигурационных файлов, поддерживаемых systemd во
|
||
всех дистрибутивах:
|
||
\begin{itemize}
|
||
\item
|
||
\hreftt{http://www.freedesktop.org/software/systemd/man/hostname.html}%
|
||
{/etc/hostname}:
|
||
имя хоста для данной системы. Одна из наиболее простых и важных
|
||
системных настроек. В разных дистрибутивах оно настраивалось
|
||
по-разному: Fedora использовала +/etc/sysconfig/network+,
|
||
OpenSUSE~--- +/etc/HOSTNAME+, Debian~--- +/etc/hostname+. Мы
|
||
остановились на варианте, предложенном Debian.
|
||
\item
|
||
\hreftt{http://www.freedesktop.org/software/systemd/man/vconsole.conf.html}%
|
||
{/etc/vconsole.conf}:
|
||
конфигурация раскладки клавиатуры и шрифта для консоли.
|
||
\item
|
||
\hreftt{http://www.freedesktop.org/software/systemd/man/locale.conf.html}%
|
||
{/etc/locale.conf}:
|
||
конфигурация общесистемной локали.
|
||
\item
|
||
\hreftt{http://www.freedesktop.org/software/systemd/man/modules-load.d.html}%
|
||
{/etc/modules-load.d/*.conf}:
|
||
каталог\footnote{Прим. перев.: Для описания этого и трех
|
||
последующих каталогов автор пользуется термином <<drop-in
|
||
directory>>. Данный термин означает каталог, в который можно
|
||
поместить множество независимых файлов настроек, и при чтении
|
||
конфигурации все эти файлы будут обработаны (впрочем, часто
|
||
накладывается ограничение~--- обрабатываются только файлы с
|
||
именами, соответствующими маске, обычно +*.conf+). Такой подход
|
||
позволяет значительно упростить процесс как ручного, так и
|
||
автоматического конфигурирования различных компонентов~--- для
|
||
внесения изменений в настройки уже не~нужно редактировать
|
||
основной конфигурационный файл, достаточно лишь
|
||
скопировать/переместить в нужный каталог небольшой файл с
|
||
указанием специфичных параметров.} для перечисления модулей
|
||
ядра, которые нужно принудительно подгрузить при загрузке
|
||
(впрочем, необходимость в этом возникает достаточно редко).
|
||
\item
|
||
\hreftt{http://www.freedesktop.org/software/systemd/man/sysctl.d.html}%
|
||
{/etc/sysctl.d/*.conf}:
|
||
каталог для задания параметров ядра (+sysctl+). Дополняет
|
||
классический конфигурационный файл +/etc/sysctl.conf+.
|
||
\item
|
||
\hreftt{http://www.freedesktop.org/software/systemd/man/tmpfiles.d.html}%
|
||
{/etc/tmpfiles.d/*.conf}:
|
||
каталог для управления настройками временных файлов (systemd
|
||
обеспечивает создание, очистку и удаление временных файлов и
|
||
каталогов, как во время загрузки, так и во время работы
|
||
системы).
|
||
\item
|
||
\hreftt{http://www.freedesktop.org/software/systemd/man/binfmt.d.html}%
|
||
{/etc/binfmt.d/*.conf}:
|
||
каталог для регистрации дополнительных бинарных форматов
|
||
(например, форматов Java, Mono, WINE).
|
||
\item
|
||
\hreftt{http://www.freedesktop.org/software/systemd/man/os-release.html}%
|
||
{/etc/os-release}:
|
||
стандарт для файла, обеспечивающего идентификацию дистрибутива и
|
||
его версии. Сейчас различные дистрибутивы используют для этого
|
||
разные файлы (например, +/etc/fedora-release+ в Fedora), и
|
||
поэтому для решения такой простой задачи, как вывод имени
|
||
дистрибутива, необходимо использовать базу данных, содержащую
|
||
перечень возможных названий файлов. Проект LSB попытался создать
|
||
такой инструмент~---
|
||
\hreftt{http://refspecs.freestandards.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/lsbrelease.html}{lsb\_release}~---
|
||
однако реализация столь простой функции через скрипт на Python'е
|
||
является не~самым оптимальным решением. Чтобы исправить
|
||
сложившуюся ситуацию, мы решили перейти к единому простому
|
||
формату представления этой информации.
|
||
\item
|
||
\hreftt{http://www.freedesktop.org/software/systemd/man/machine-id.html}%
|
||
{/etc/machine-id}:
|
||
файл с идентификатором данного компьютера (перекрывает
|
||
аналогичный идентификатор D-Bus). Гарантируется, что в любой
|
||
системе, использующей systemd, этот файл будет существовать и
|
||
содержать корректную информацию (если его нет, он автоматически
|
||
создается при загрузке). Мы вынесли этот файл из-под эгиды
|
||
D-Bus, чтобы упростить решение множества задач, требующих
|
||
наличия уникального и постоянного идентификатора компьютера.
|
||
\item
|
||
\hreftt{http://www.freedesktop.org/software/systemd/man/machine-info.html}%
|
||
{/etc/machine-info}:
|
||
новый конфигурационный файл, хранящий информации о полном
|
||
(описательном) имени хоста (например, <<Компьютер Леннарта>>) и
|
||
значке, которым он будет обозначаться в графических оболочках,
|
||
работающих с сетью (раньше этот значок мог определяться,
|
||
например, файлом +/etc/favicon.png+). Данный конфигурационный
|
||
файл обслуживается демоном
|
||
\hreftt{http://www.freedesktop.org/wiki/Software/systemd/hostnamed}{systemd-hostnamed}.
|
||
\end{itemize}
|
||
|
||
Одна из важнейших для нас задач~--- убедить \emph{вас} использовать эти новые
|
||
конфигурационные файлы в ваших инструментах для настройки системы. Если ваши
|
||
конфигурационные фронтенды будут использовать новые файлы, а не~их старые
|
||
аналоги, это значительно облегчит портирование таких фронтендов между
|
||
дистрибутивами, и вы внесете свой вклад в стандартизацию Linux. В конечном счете
|
||
это упростит жизнь и администраторам, и пользователям. Разумеется, на текущий
|
||
момент эти файлы полностью поддерживаются только дистрибутивами, основанными на
|
||
systemd, но уже сейчас в их число входят практически все ключевые дистрибутивы,
|
||
\href{http://www.ubuntu.com/}{за исключением
|
||
одного}\footnote{Прим. перев.: В конце 2010~года энтузиаст Andrew Edmunds
|
||
\href{http://cgit.freedesktop.org/systemd/systemd/commit/?id=858dae181bb5461201ac1c04732d3ef4c67a0256}{добавил}
|
||
в systemd базовую поддержку Ubuntu и
|
||
\href{https://wiki.ubuntu.com/systemd}{подготовил} соответствующие пакеты,
|
||
однако его инициатива не~встретила поддержки среди менеджеров Canonical. На
|
||
момент написания этих строк проект остается заброшенным с декабря 2010~г.}. В
|
||
этом есть что-то от <<проблемы курицы и яйца>>: стандарт становится настоящим
|
||
стандартом только тогда, когда ему начинают следовать. В будущем мы намерены
|
||
аккуратно форсировать процесс перехода на новые конфигурационные файлы:
|
||
поддержка старых файлов будет удалена из systemd. Разумеется, процесс будет
|
||
идти медленно, шаг за шагом. Но конечной его целью является переход всех
|
||
дистрибутивов на единый набор базовых конфигурационных файлов.
|
||
|
||
Многие из этих файлов используются не~только программами для настройки системы,
|
||
но и апстримными проектами. Например, мы предлагаем проектам Mono, Java, WINE и
|
||
другим помещать конфигурацию для регистрации своих бинарных форматов в
|
||
+/etc/binfmt.d/+ средствами их собственной сборочной системы. Специфичные для
|
||
дистрибутивов механизмы поддержки бинарных форматов больше не~нужны, и ваш
|
||
проект будет работать одинаково хорошо во всех дистрибутивах. Аналогичное
|
||
предложение мы обращаем и ко всем разработчикам программ, которым требуется
|
||
автоматическое создание/очистка временных файлов и каталогов,
|
||
например, в каталоге +/run+ (\href{http://lwn.net/Articles/436012/}{ранее
|
||
известном} как +/var/run+). Таким проектам достаточно просто поместить
|
||
соответствующий конфигурационный файл в +/etc/tmpfiles.d/+, тоже средствами
|
||
собственной сборочной системы. Помимо прочего, подобный подход позволит
|
||
увеличить скорость загрузки, так как, в отличие от SysV, не~требует множества
|
||
shell-скриптов, выполняющих тривиальные задачи (регистрация бинарных форматов,
|
||
удаление/создание временных файлов/каталогов и т.п.). И пример того случая,
|
||
когда апстримная поддержка стандартной конфигурации дала бы огромные
|
||
преимущества~--- X11 (и его аналоги) могли бы устанавливать раскладку клавиатуры
|
||
на основании данных из +/etc/vconsole.conf+.
|
||
|
||
Разумеется, я понимаю, что отнюдь не~всех полностью устроят выбранные нами имена
|
||
и форматы конфигурационных файлов. Но нам все же нужно было что-то выбрать, и мы
|
||
выбрали то, что должно устроить большинство людей. Форматы конфигурационных
|
||
файлов максимально просты, и их можно легко читать и записывать даже из
|
||
shell-скриптов. Да, +/etc/bikeshed.conf+ могло бы быть неплохим именем
|
||
для файла конфигурации!\footnote{Прим. перев.: Здесь автор намекает на
|
||
\href{http://en.wikipedia.org/wiki/Parkinson's_Law_of_Triviality}{Паркинсоновский
|
||
Закон Тривиальности}, который гласит, что самые жаркие споры возникают вокруг
|
||
наиболее простых вопросов. В частности, в качестве примера Паркинсон приводит
|
||
обсуждение строительства атомной электростанции и гаража для велосипедов (bike
|
||
shed)~--- если первое из этих решений принимается довольно быстро, то вокруг
|
||
второго разгорается множество дискуссий по самым разным аспектам.}
|
||
|
||
\textbf{Помогите нам стандартизировать Linux! Используйте новые конфигурационные
|
||
файлы! Поддерживайте их в апстриме, поддерживайте их во всех дистрибутивах!}
|
||
|
||
И если у вас возникнет такой вопрос: да, все эти файлы так или иначе
|
||
обсуждались с разными разработчиками из различных дистрибутивов. И некоторые из
|
||
разработчиков планируют обеспечить поддержку новой конфигурации даже в
|
||
системах без systemd.
|
||
|
||
\section{О судьбе /etc/sysconfig и /etc/default}
|
||
|
||
В дистрибутивах, основанных на Red Hat и SUSE, это каталог называется
|
||
+/etc/sysconfig+. В дистрибутивах на базе Debian, его зовут +/etc/default+.
|
||
Во многих других дистрибутивах также присутствуют каталоги похожего назначения.
|
||
Связанные с ними вопросы неоднократно появляются в дискуссиях пользователей и
|
||
разработчиков systemd. В этой статье мне хотелось бы рассказать, что я, как
|
||
разработчик systemd, думаю об этих каталогах, и пояснить, почему, на мой взгляд,
|
||
от них лучше отказаться. Стоит отметить, что это мое личное мнение, и оно
|
||
может не~совпадать с позицией проекта Fedora или моего работодателя.
|
||
|
||
Начнем с небольшого исторического экскурса. Каталог +/etc/sysconfig+ появился в
|
||
дистрибутивах Red Hat и SUSE задолго до того, как я присоединился к этим
|
||
проектам~--- иными словами, это было очень давно.
|
||
Некоторое время спустя, в Debian появился аналогичный по смыслу каталог
|
||
+/etc/default+. Многие дистрибутивы используют такие каталоги, называя их
|
||
по-разному. Они имеются даже в некоторых ОС семейства Unix. (Например, в SCO.
|
||
Если эта тема вас заинтересовала~--- рекомендую обратиться к вашему знакомому
|
||
ветерану Unix, он расскажет гораздо подробнее и интереснее, чем я.) Несмотря на
|
||
то, что подобные каталоги широко используются в Linux и Unix, они совершенно
|
||
не~стандартизированы~--- ни в POSIX, ни в LSB/FHS, и результате мы имеем целый
|
||
зоопарк их различных реализаций в разных дистрибутивах.
|
||
|
||
Назначение этих каталогов определено весьма расплывчато. Абсолютное большинство
|
||
находящихся в них файлов являются включаемыми\footnote{Прим. перев.: Здесь автор
|
||
использует термин sourcable, происходящий от bash'овской директивы +source+,
|
||
обеспечивающей включение в скрипт кода из внешнего файла. В классическом POSIX
|
||
shell это соответствует оператору-точке <<+.+>>. В отличие от прямого запуска
|
||
одного скрипта из другого, включаемый код исполняется той же самой оболочкой,
|
||
что и основной код, и при возвращении в основной скрипт сохраняются переменные
|
||
окружения, определенные во включаемом коде. Как правило, код для включения
|
||
не~содержит shebang'а (+#!/bin/sh+ в начале файла).} shell-скриптами, содержащими,
|
||
главным образом, определения переменных. Большинство файлов из этих каталогов
|
||
включаются в одноименные скрипты SysV init. Данный принцип отражен в
|
||
\href{http://www.debian.org/doc/debian-policy/ch-opersys.html#s-sysvinit}{Debian
|
||
Policy Manual (раздел 9.3.2)} и в
|
||
\href{http://fedoraproject.org/wiki/Packaging:SysVInitScript}{Fedora Packaging
|
||
Guidelines}, однако в обоих дистрибутивах иногда встречаются файлы,
|
||
не~соответствующие описанной схеме, например, не~имеющие соответствующего
|
||
init-скрипта, или даже сами не~являющиеся скриптами.
|
||
|
||
Но почему вообще появились эти каталоги? Чтобы ответить на такой вопрос,
|
||
обратимся к истории развития концепции SysV init-скриптов. Исторически,
|
||
сложилось так, что они располагаются в каталоге под названием +/etc/rc.d/init.d+
|
||
(или что-то похожее). Отметим, что каталог +/etc+ вообще-то предназначен для
|
||
хранения файлов конфигурации, а не~исполняемого кода (в частности, скриптов).
|
||
Однако, в начале своей истории, init-скрипты рассматривались именно как файлы
|
||
конфигурации, и редактирование их администратором было общепринятой практикой.
|
||
Но со временем, по мере роста и усложнения этих скриптов, их стали рассматривать
|
||
уже не~как файлы конфигурации, а как некие программы. Чтобы упростить их
|
||
настройку и обеспечить безопасность процесса обновления, настройки были вынесены
|
||
в отдельные файлы, загружаемые при работе init-скриптов.
|
||
|
||
Попробуем составить некоторое представление о настройках, которые можно сделать
|
||
через эти файлы. Вот краткий и неполный список различных параметров, которые
|
||
могут быть заданы через переменные окружения в таких файлах (составлен мною по
|
||
результатам исследования соответствующих каталогов в Fedora и Debian):
|
||
\begin{itemize}
|
||
\item Дополнительные параметры командной строки для бинарника демона.
|
||
\item Настройки локали для демона.
|
||
\item Тайм-аут остановки для демона.
|
||
\item Режим остановки для демона.
|
||
\item Общесистемные настройки, например, системная локаль, часовой пояс,
|
||
параметры клавиатуры для консоли.
|
||
\item Избыточная информация о системных настройках, например, указание,
|
||
установлены ли аппаратные часы по Гринвичу или по местному
|
||
времени.
|
||
\item Списки правил брандмауэра, не~являются скриптами (!).
|
||
\item Привязка к процессорным ядрам для демона.
|
||
\item Настройки, не~относящиеся к процессу загрузки, например,
|
||
информация по установке пакетов с новыми ядрами, конфигурация
|
||
nspluginwrapper, разрешение на выполнение
|
||
предварительного связывания (prelinking) библиотек.
|
||
\item Указание, нужно ли запускать данную службу или нет.
|
||
\item Настройки сети.
|
||
\item Перечень модулей ядра, которые должны быть подгружены
|
||
принудительно.
|
||
\item Нужно ли отключать питание компьютера при остановке системы
|
||
(+poweroff+) или нет (+halt+).
|
||
\item Права доступа для файлов устройств (!).
|
||
\item Описание соответствующей SysV службы.
|
||
\item Идентификатор пользователя/группы, значение umask для демона.
|
||
\item Ограничения по ресурсам для демона.
|
||
\item Приоритет OOM killer'а для демона.
|
||
\end{itemize}
|
||
|
||
А теперь давайте ответим на вопрос: что же такого неправильного в
|
||
+/etc/sysconfig+ (+/etc/default+) и почему этим каталогам нет места в мире
|
||
systemd?
|
||
\begin{itemize}
|
||
\item Прежде всего, утрачены основная цель и смысл существования таких
|
||
каталогов: файлы конфигурации юнитов systemd не~являются
|
||
программами, в отличие от init-скриптов SysV. Эти файлы
|
||
представляют собой простые, декларативные описания конкретных
|
||
задач и функций, и обычно содержат не~более шести строк. Они
|
||
легко могут быть сгенерированы и проанализованы без
|
||
использования Bourne shell. Их легко читать и понимать. Кроме
|
||
того, их легко модифицировать: достаточно скопировать файл из
|
||
+/lib/systemd/system+ в +/etc/systemd/system+, после чего внести
|
||
в созданную копию необходимые изменения (при этом можно быть
|
||
уверенным, что изменения не~будут затерты пакетным менеджером).
|
||
Изначальная причина появления обсуждаемых каталогов~---
|
||
необходимость разделять код и параметры конфигурации~--- больше
|
||
не~существует, так как файлы описания юнитов не~являются кодом.
|
||
Проще говоря, обсуждаемые каталоги являются решением проблемы,
|
||
которой уже не~существует.
|
||
\item Обсуждаемые каталоги и файлы в них очень сильно привязаны к
|
||
специфике дистрибутивов. Мы же планируем, используя systemd,
|
||
способствовать стандартизации и унификации дистрибутивов. В
|
||
частности, одним из факторов такой стандартизации является
|
||
рекомендация распространять соответствующие файлы конфигурации
|
||
юнитов сразу с апстримным продуктом, а не~возлагать эту работу
|
||
на создателей пакетов, как это делалась во времена SysV.
|
||
Так как расположение обсуждаемых каталогов и настраиваемые через
|
||
них параметры сильно отличаются от дистрибутива к дистрибутиву,
|
||
пытаться поддерживать их в апстримных файлах конфигурации юнитов
|
||
просто бессмысленно. Хранение параметров конфигурации в этих
|
||
каталогах~--- один из факторов, превращающих Linux в зоопарк
|
||
несовместимых решений.
|
||
\item Большинство настроек, задаваемых через эти каталоги, являются
|
||
избыточными в мире systemd. Например, различные службы позволяют
|
||
задать таким методом параметры исполнения процесса, в частности,
|
||
идентификатор пользователя/группы, ограничения ресурсов,
|
||
привязки к ядрам CPU, приоритет OOM killer'а. Однако, эти
|
||
настройки поддерживаются лишь некоторыми init-скриптами, причем
|
||
одна и та же настройка в различных скриптах может называться
|
||
по-разному. С другой стороны, в мире systemd, все эти настройки
|
||
доступны для всех служб без исключения, и всегда задаются
|
||
одинаково, через одни и те же параметры конфигурационных файлов.
|
||
\item Файлы конфигурации юнитов имеют множество удобных и простых в
|
||
использовании настроек среды исполнения процесса, гораздо
|
||
больше, чем могут предоставить файлы из +/etc/sysconfig+.
|
||
\item Необходимость в некоторых из этих настроек весьма сомнительна.
|
||
Например, возьмем вышеупомянутую возможность задавать
|
||
идентификатор пользователя/группы для процесса. Эту задачу
|
||
должен решать разработчик ПО или дистрибутива. Вмешательство
|
||
администратора в данную настройку, как правило, лишено
|
||
смысла~--- только разработчик располагает всей информацией,
|
||
позволяющий предотвратить конфликты идентификаторов и имен
|
||
пользователей и групп.
|
||
\item Формат файлов, используемых для сохранения настроек, плохо
|
||
подходит для данной задачи. Так как эти файлы, как правило,
|
||
являются включаемыми shell-скриптами, ошибки при их чтении очень
|
||
трудно отследить. Например, ошибка в имени переменной приведет к
|
||
тому, что переменная не~будет изменена, однако никакого
|
||
предупреждения при этом не~выводится.
|
||
\item Кроме того, такая организация не~исключает влияния
|
||
конфигурационных параметров на среду исполнения: например,
|
||
изменение переменных +IFS+ и +LANG+ может существенно повлиять
|
||
на результат интерпретации init-скрипта.
|
||
\item Интерпретация этих файлов требует запуска еще одного экземпляра
|
||
оболочки, что приводит к задержкам при загрузке\footnote{Прим.
|
||
перев.: Здесь автор несколько заблуждается. Скрипты, включенные
|
||
через директиву +source+, исполняются тем же экземпляром
|
||
оболочки, что и вызвавший их скрипт.}.
|
||
\item Файлы из +/etc/sysconfig+ часто пытаются использовать в качестве
|
||
суррогатной замены файлов конфигурации для тех демонов, которые
|
||
не~имеют встроенной поддержки конфигурационных файлов. В
|
||
частности, вводятся специальные переменные, позволяющие задать
|
||
аргументы командной строки, используемые при запуске демона.
|
||
Встроенная поддержка конфигурационных файлов является более
|
||
удобной альтернативой такому подходу, ведь, глядя на ключи
|
||
<<+-k+>>, <<+-a+>>, <<+-f+>>, трудно догадаться об их
|
||
назначении. Очень часто, из-за ограниченности словаря, на
|
||
различных демонов одни и те же ключи действуют совершенно
|
||
по-разному (для одного демона ключ <<+-f+>> содержит указание
|
||
демонизироваться при запуске, в то время как для другого эта
|
||
опция действует прямо противоположным образом.) В отличие от
|
||
конфигурационных файлов, строка запуска не~может включать
|
||
полноценных комментариев.
|
||
\item Некоторые из настроек, задаваемых в +/etc/sysconfig+, являются
|
||
полностью избыточными. Например, во многие дистрибутивах
|
||
подобным методом указывается, установлены ли аппаратные часы
|
||
компьютера по Гринвичу, или по местному времени. Однако эта же
|
||
настройка задается третьей строкой файла +/etc/adjtime+,
|
||
поддерживаемого во всех дистрибутивах. Использование
|
||
избыточного и не~стандартизированного параметра конфигурации
|
||
только добавляет путаницу и не~несет никакой пользы.
|
||
\item Многие файлы настроек из +/etc/sysconfig+ позволяют отключать
|
||
запуск соответствующей службы. Однако эта операция уже
|
||
поддерживается штатно для всех служб, через команды
|
||
+systemctl enable+/+disable+ (или +chkconfig on+/+off+).
|
||
Добавление дополнительного уровня настройки не~приносит никакой
|
||
пользы и лишь усложняет работу администратора.
|
||
\item Что касается списка принудительно загружаемых модулей ядра~--- в
|
||
настоящее время существуют куда более удобные пути для
|
||
автоматической подгрузки модулей при загрузке системы. Например,
|
||
многие модули автоматически подгружаются +udev+'ом при
|
||
обнаружении соответствующего оборудования. Этот же принцип
|
||
распространяется на ACPI и другие высокоуровневые технологии.
|
||
Одно из немногих исключений из этого правила~--- к сожалению, в
|
||
настоящее время не~поддерживается автоматическая загрузка
|
||
модулей на основании информации о возможностях процессора,
|
||
однако это будет исправлено в ближайшем будущем\footnote{Прим.
|
||
перев.: Необходимые патчи уже приняты в ядро, и
|
||
соответствующая функция поддерживается Linux, начиная с версии
|
||
3.3.}. В случае, если нужный вам модуль ядра все же не~может
|
||
быть подгружен автоматически, все равно существует гораздо более
|
||
удобные методы указать его принудительную подгрузку~---
|
||
например, путем создания соответствующего файла в каталоге
|
||
\hreftt{http://www.freedesktop.org/software/systemd/man/modules-load.d.html}%
|
||
{/etc/modules-load.d/} (стандартный метод настройки
|
||
принудительной подгрузки модулей).
|
||
\item И наконец, хотелось бы отметить, что каталог +/etc+ определен как
|
||
место для хранения системных настроек (<<Host-specific system
|
||
configuration>>, согласно FHS). Наличие внутри него подкаталога
|
||
+sysconfig+, который тоже содержит системную конфигурацию,
|
||
является очевидно избыточным.
|
||
\end{itemize}
|
||
|
||
Что же можно предложить в качестве современной, совместимой с systemd
|
||
альтернативы настройке системы через файлы в этих каталогах? Ниже приведены
|
||
несколько рекомендаций, как лучше поступить с задаваемыми таким образом параметрами
|
||
конфигурации:
|
||
\begin{itemize}
|
||
\item Попробуйте просто отказаться от них. Если они полностью избыточны (например,
|
||
настройка аппаратных часов на Гринвич/местное время), то убрать
|
||
их будет довольно легко (если не~рассматривать вопросы
|
||
обеспечения совместимости). Если аналогичные по смыслу опции
|
||
штатно поддерживаются systemd, нет никакого смысла дублировать
|
||
их где-то еще (перечень опций, которые можно задать для любой
|
||
службы, приведен на страницах справки
|
||
\href{http://www.freedesktop.org/software/systemd/man/systemd.exec.html}%
|
||
{systemd.exec(5)} и
|
||
\href{http://www.freedesktop.org/software/systemd/man/systemd.service.html}%
|
||
{systemd.service(5)}.)
|
||
Если же ваша настройка просто добавляет еще один уровень
|
||
отключения запуска службы~--- не~плодите лишние сущности,
|
||
откажитесь от нее.
|
||
\item Найдите для них более подходящее место. Например, в случае с
|
||
некоторыми общесистемными настройками (такими, как локаль или
|
||
часовой пояс), мы надеемся аккуратно подтолкнуть дистрибутивы в
|
||
правильном направлении (см. предыдущую главу).
|
||
\item Добавьте их поддержку в штатную систему настройки демона через
|
||
собственные файлы конфигурации. К счастью, большинство служб,
|
||
работающих в Linux, являются свободным программным обеспечением,
|
||
так что сделать это довольно просто.
|
||
\end{itemize}
|
||
|
||
Существует лишь одна причина поддерживать файлы +/etc/sysconfig+ еще некоторое
|
||
время: необходимо обеспечить совместимость при обновлении. Тем не~менее, как
|
||
минимум в новых пакетах, от таких файлов лучше отказаться.
|
||
|
||
В том случае, если требование совместимости критично, вы можете задействовать
|
||
эти конфигурационные файлы даже в том случае, если настраиваете службы через
|
||
штатные unit-файлы systemd. Если ваш файл из +sysconfig+ содержит лишь
|
||
определения переменных, можно воспользоваться опцией
|
||
+EnvironmentFile=-/etc/sysconfig/foobar+ (подробнее об этой опции см.
|
||
\href{http://www.freedesktop.org/software/systemd/man/systemd.exec.html}{systemd.exec(5)}),
|
||
позволяющей прочитать из файла набор переменных окружения, который будет
|
||
установлен при запуске службы. Если же для задания настроек вам необходим
|
||
полноценный язык программирования~--- ничто не~мешает им воспользоваться.
|
||
Например, вы можете создать в +/usr/lib/<your package>/+ простой скрипт,
|
||
который включает соответствующие файлы, а затем запускает бинарник демона через
|
||
+exec+. После чего достаточно просто указать этот скрипт в опции +ExecStart=+
|
||
вместо бинарника демона.
|
||
|
||
\section{Экземпляры служб}
|
||
\label{sec:instances}
|
||
|
||
Большинство служб в Linux/Unix являются одиночными (singleton): в каждый момент
|
||
времени на данном хосте работает только один экземпляр службы. В качестве
|
||
примера таких одиночных служб можно привести Syslogd, Postfix, Apache. Однако,
|
||
существуют службы, запускающие по несколько экземпляров себя на одном хосте.
|
||
Например, службы наподобие Dovecot IMAP запускают по одному экземпляру на каждый
|
||
локальный порт и/или IP-адрес. Другой пример, который можно встретить
|
||
практически во всех системах~--- \emph{getty}, небольшая служба, запускающаяся на
|
||
каждом TTY (от +tty1+ до +tty6+). На некоторых серверах, в зависимости от
|
||
сделанных администратором настроек или параметров загрузки, могут запускаться
|
||
дополнительные экземпляры getty, для подключаемых к COM-портам терминалов или
|
||
для консоли системы виртуализации. Еще один пример службы, работающей в
|
||
нескольких экземплярах (по крайней мере, в мире systemd)~--- \emph{fsck},
|
||
программа проверки файловой системы, которая запускается по одному экземпляру
|
||
на каждое блочное устройство, требующее такой проверки. И наконец, стоит
|
||
упомянуть службы с активацией в стиле inetd~--- при обращении через сокет, по
|
||
одному экземпляру на каждое соединение. В этой статье я попытаюсь рассказать,
|
||
как в systemd реализовано управление <<многоэкземплярными>> службами, и какие
|
||
выгоды системный администратор может извлечь из этой возможности.
|
||
|
||
Если вы читали предыдущие статьи из этого цикла, вы, скорее всего, уже знаете,
|
||
что службы systemd именуются по схеме \emph{foobar}+.service+, где
|
||
\emph{foobar}~--- строка, идентифицирующая службу (проще говоря, ее имя), а
|
||
+.service+~--- суффикс, присутствующий в именах всех файлов конфигурации служб.
|
||
Сами эти файлы могут находиться в каталогах +/etc/systemd/systemd+ и
|
||
+/lib/systemd/system+ (а также, возможно, и в других\footnote{Прим. перев.:
|
||
Перечень каталогов, в которых выполняется поиск общесистемных юнит-файлов,
|
||
приведен на странице руководства
|
||
\href{http://www.freedesktop.org/software/systemd/man/systemd.html}{systemd(1)}
|
||
(раздел <<System unit directories>>). Указанные выше каталоги
|
||
+/etc/systemd/systemd+ и +/lib/systemd/system+ соответствуют значениям по
|
||
умолчанию для упомянутых там переменных pkg-config +systemdsystemconfdir+ и
|
||
+systemdsystemunitdir+ соответственно. Начиная с systemd версии 198, данный
|
||
перечень, в более точной и развернутой форме, присутствует на странице
|
||
\href{http://www.freedesktop.org/software/systemd/man/systemd.unit.html}%
|
||
{systemd.unit(5)}.}). Для служб, работающих в нескольких экземплярах, эта схема
|
||
становится немного сложнее: \emph{foobar}+@+\emph{quux}+.service+, где
|
||
\emph{foobar}~--- имя службы, общее для всех экземпляров, а \emph{quux}~---
|
||
идентификатор конкретного экземпляра. Например, +serial-gett@ttyS2.service+~---
|
||
это служба getty для COM-порта, запущенная на +ttyS2+.
|
||
|
||
При необходимости, экземпляры служб можно легко создать динамически. Скажем, вы
|
||
можете, безо всяких дополнительных настроек, запустить новый экземпляр getty на
|
||
последовательном порту, просто выполнив +systemctl start+ для нового экземпляра:
|
||
\begin{Verbatim}
|
||
# systemctl start serial-getty@ttyUSB0.service
|
||
\end{Verbatim}
|
||
|
||
Получив такую команду, systemd сначала пытается найти файл конфигурации юнита с
|
||
именем, точно соответствующим запрошенному. Если такой файл найти не~удается
|
||
(при работе с экземплярами служб обычно так и происходит), из имени файла
|
||
удаляется идентификатор экземпляра, и полученное имя используется при поиске
|
||
\emph{шаблона} конфигурации. В нашем случае, если отсутствует файл с именем
|
||
+serial-getty@ttyUSB0.service+, используется файл-шаблон под названием
|
||
+serial-getty@.service+. Таким образом, для всех экземпляров данной службы,
|
||
используется один и тот же шаблон конфигурации. В случае с getty для COM-портов,
|
||
этот шаблон, поставляемый в комплекте с systemd
|
||
(файл +/lib/systemd/system/serial-getty@.service+) выглядит примерно так:
|
||
\begin{Verbatim}
|
||
[Unit]
|
||
Description=Serial Getty on %I
|
||
BindTo=dev-%i.device
|
||
After=dev-%i.device systemd-user-sessions.service
|
||
|
||
[Service]
|
||
ExecStart=-/sbin/agetty -s %I 115200,38400,9600
|
||
Restart=always
|
||
RestartSec=0
|
||
\end{Verbatim}
|
||
(Заметим, что приведенная здесь версия немного сокращена, по сравнению с реально
|
||
используемой в systemd. Удалены не~относящиеся к теме нашего обсуждения
|
||
параметры конфигурации, обеспечивающие совместимость с SysV, очистку экрана и
|
||
удаление предыдущих пользователей с текущего TTY. Если вам интересно, можете
|
||
посмотреть
|
||
\href{http://cgit.freedesktop.org/systemd/systemd/plain/units/serial-getty@.service.m4}{полную
|
||
версию}.)
|
||
|
||
Этот файл похож на обычный файл конфигурации юнита, с единственным отличием: в
|
||
нем используются спецификаторы \%I и \%i. В момент загрузки юнита, systemd
|
||
заменяет эти спецификаторы на идентификатор экземпляра службы. В нашем случае,
|
||
при обращении к экземпляру +serial-getty@ttyUSB0.service+, они заменяются на
|
||
<<+ttyUSB0+>>. Результат такой замены можно проверить, например, запросив
|
||
состояние для нашей службы:
|
||
\begin{Verbatim}
|
||
$ systemctl status serial-getty@ttyUSB0.service
|
||
serial-getty@ttyUSB0.service - Getty on ttyUSB0
|
||
Loaded: loaded (/lib/systemd/system/serial-getty@.service; static)
|
||
Active: active (running) since Mon, 26 Sep 2011 04:20:44 +0200; 2s ago
|
||
Main PID: 5443 (agetty)
|
||
CGroup: name=systemd:/system/getty@.service/ttyUSB0
|
||
└ 5443 /sbin/agetty -s ttyUSB0 115200,38400,9600
|
||
\end{Verbatim}
|
||
Собственно, это и есть ключевая идея организации экземпляров служб. Как видите,
|
||
systemd предоставляет простой в использовании механизм шаблонов, позволяющих
|
||
динамически создавать экземпляры служб. Добавим несколько дополнительных
|
||
замечаний, позволяющих эффективно использовать этот механизм:
|
||
|
||
Вы можете создавать дополнительные экземпляры таких служб, просто добавляя
|
||
символьные ссылки в каталоги +*.wants/+. Например, чтобы обеспечить запуск getty
|
||
на ttyUSB0 при каждой загрузке, достаточно создать такую ссылку:
|
||
\begin{Verbatim}
|
||
# ln -s /lib/systemd/system/serial-getty@.service \
|
||
/etc/systemd/system/getty.target.wants/serial-getty@ttyUSB0.service
|
||
\end{Verbatim}
|
||
При этом файл конфигурации, на который указывает ссылка (в нашем случае
|
||
+serial-getty@.service+), будет вызван с тем именем экземпляра, которое указанно
|
||
в названии этой ссылки (в нашем случае~--- +ttyUSB0+).
|
||
|
||
Вы не~сможете обратиться к юниту-шаблону без указания идентификатора экземпляра.
|
||
В частности, команда +systemctl start serial-getty@.service+ завершится ошибкой.
|
||
|
||
Иногда возникает необходимость отказаться от использования общего шаблона
|
||
для конкретного экземпляра (т.е. конфигурация данного экземпляра настолько
|
||
сильно отличается от параметров остальных экземпляров данной службы, что
|
||
механизм шаблонов оказывается неэффективен). Специально для таких случаев, в
|
||
systemd и заложен предварительный поиск файла с именем, точно соответствующим
|
||
указанному (прежде чем использовать общий шаблон). Таким образом, вы можете
|
||
поместить файл с именем, точно соответствующим полному титулу экземпляра, в
|
||
каталог +/etc/systemd/system/+~--- и содержимое этого файла, при обращении
|
||
к выбранному экземпляру, полностью перекроет все настройки, сделанные в общем
|
||
шаблоне.
|
||
|
||
В приведенном выше файле, в некоторых местах используется спецификатор +%I+, а
|
||
в других~--- +%i+. У вас может возникнуть закономерный вопрос~--- чем они
|
||
отличаются? +%i+ всегда точно соответствует идентификатору экземпляра, в то
|
||
время, как +%I+ соответствует неэкранированной (unescaped) версии этого
|
||
идентификатора. Когда идентификатор не~содержит спецсимволов (например,
|
||
+ttyUSB0+), результат в обоих случаях одинаковый. Но имена устройств, например,
|
||
содержат слеши (<</>>), которые не~могут присутствовать в имени юнита (и в имени
|
||
файла на Unix). Поэтому, перед использованием такого имени в качестве
|
||
идентификатора устройства, оно должно быть экранировано~--- <</>> заменяются на
|
||
<<->>, а большинство других специальных символов (включая <<->>) заменяются
|
||
последовательностями вида +\xAB+, где AB~--- ASCII-код символа, записанный в
|
||
шестнадцатеричной системе счисления\footnote{Согласен, этот алгоритм дает на
|
||
выходе не~очень читабельный результат. Но этим грешат практически все алгоритмы
|
||
экранирования. В данном случае, был использован механизм экранирования из udev,
|
||
с одним изменением. В конце концов, нам нужно было выбрать что-то. Если вы
|
||
собираетесь комментировать наш алгоритм экранирования~--- пожалуйста, оставьте
|
||
свой адрес, чтобы я мог приехать к вам и раскрасить ваш сарай для велосипедов в
|
||
синий с желтыми полосами. Спасибо!}. Например, чтобы обратиться к
|
||
последовательному USB-порту по его адресу на шине, нам нужно использовать имя
|
||
наподобие +serial/by-path/pci-0000:00:1d.0-usb-0:1.4:1.1-port0+. Экранированная
|
||
версия этого имени~---
|
||
+serial-by\x2dpath-pci\x2d0000:00:1d.0\x2dusb\x2d0:1.4:1.1\x2dport0+. +%I+ будет
|
||
ссылаться на первую из этих строк, +%i+~--- на вторую. С практической точки
|
||
зрения, это означает, что спецификатор +%i+ можно использовать в том случае,
|
||
когда надо сослаться на имена других юнитов, например, чтобы описать
|
||
дополнительные зависимости (в случае с +serial-getty@.service+, этот
|
||
спецификатор используется для ссылки на юнит +dev-%i.device+, соответствующий
|
||
одноименному устройству). В то время как +%I+ удобно использовать в командной
|
||
строке (+ExecStart+) и для формирования читабельных строк описания. Рассмотрим
|
||
работу этих принципов на примере нашего юнит-файла\footnote{Прим. перев.: как
|
||
видно из нижеприведенного примера, в командной строке +systemctl+ используется
|
||
экранированное имя юнита, что создает определенные трудности даже при
|
||
наличии в оболочке <<умного>> автодополнения. Однако, начиная с systemd v186,
|
||
при работе с +systemctl+ можно указывать неэкранированные имена юнитов.}:
|
||
\begin{landscape}
|
||
\begin{Verbatim}[fontsize=\small]
|
||
# systemctl start 'serial-getty@serial-by\x2dpath-pci\x2d0000:00:1d.0\x2dusb\x2d0:1.4:1.1\x2dport0.service'
|
||
# systemctl status 'serial-getty@serial-by\x2dpath-pci\x2d0000:00:1d.0\x2dusb\x2d0:1.4:1.1\x2dport0.service'
|
||
serial-getty@serial-by\x2dpath-pci\x2d0000:00:1d.0\x2dusb\x2d0:1.4:1.1\x2dport0.service - Serial Getty on serial/by-path/pci-0000:00:1d.0-usb-0:1.4:1.1-port0
|
||
Loaded: loaded (/lib/systemd/system/serial-getty@.service; static)
|
||
Active: active (running) since Mon, 26 Sep 2011 05:08:52 +0200; 1s ago
|
||
Main PID: 5788 (agetty)
|
||
CGroup: name=systemd:/system/serial-getty@.service/serial-by\x2dpath-pci\x2d0000:00:1d.0\x2dusb\x2d0:1.4:1.1\x2dport0
|
||
└ 5788 /sbin/agetty -s serial/by-path/pci-0000:00:1d.0-usb-0:1.4:1.1-port0 115200 38400 9600
|
||
\end{Verbatim}
|
||
\end{landscape}
|
||
Как видите, в качестве идентификатора экземпляра используется экранированная
|
||
версия, в то время как в строке описания и в командной строке +getty+
|
||
фигурирует неэкранированный вариант, как и предполагалось.
|
||
|
||
(Небольшое замечание: помимо +%i+ и +%I+, существует еще несколько
|
||
спецификаторов, и большинство из них доступно и в обычных файлах конфигурации
|
||
юнитов, а не~только в шаблонах. Подробности можно посмотреть на
|
||
\href{http://www.freedesktop.org/software/systemd/man/systemd.unit.html}%
|
||
{странице руководства}, содержащей полный перечень этих спецификаторов с
|
||
краткими пояснениями.)
|
||
|
||
\section{Службы с активацией в стиле inetd}
|
||
\label{sec:inetd}
|
||
|
||
В одной из предыдущих глав (гл.~\ref{sec:convert}) я рассказывал, как можно
|
||
преобразовать SysV init-скрипт в юнит-файл systemd. В этой главе мы рассмотрим,
|
||
как провести аналогичное преобразование для служб inetd.
|
||
|
||
Начнем с небольшого экскурса в историю вопроса. Уже многие годы
|
||
inetd считается одной из базовых служб Unix-систем. Работая как суперсервер, он
|
||
слушает сетевые сокеты от имени различных служб, и активирует соответствующие
|
||
службы при поступлении на эти сокеты входящих соединений. Таким образом, он
|
||
обеспечивает механизм активации по запросу (on-demand activation). Подобный
|
||
подход избавляет от необходимости держать постоянно работающими все серверные
|
||
процессы, что позволяет поддерживать множество служб даже на системах с очень
|
||
ограниченными ресурсами. В дистрибутивах Linux можно встретить
|
||
несколько различных реализаций inetd. Наиболее популярные из них~--- BSD
|
||
inetd и xinetd. Хотя inetd во многих дистрибутивах до сих пор устанавливается по
|
||
умолчанию, сейчас он уже редко используется для запуска сетевых служб~--- теперь
|
||
большинство из них запускаются автономно при загрузке системы (основной
|
||
аргумент в пользу такой схемы~--- она позволяет обеспечить более высокую
|
||
производительность служб).
|
||
|
||
Одной из ключевых возможностей systemd (а также launchd от Apple) является
|
||
сокет-активация~--- тот же самый механизм, давным-давно реализованный inetd,
|
||
однако в данном случае ключевые цели немного другие. Сокет-активация в стиле
|
||
systemd прежде всего ориентирована на локальные сокеты (+AF_UNIX+), хотя
|
||
поддерживаются также и сетевые сокеты (+AF_INET+). И более важное отличие~---
|
||
сокет-активация в systemd обеспечивает не~только экономию системных ресурсов,
|
||
но также и эффективную параллелизацию работы (так как она позволяет запускать
|
||
клиентские и серверные процессы одновременно, непосредственно после создания
|
||
сокета), упрощение процесса конфигурации (отпадает необходимость явно указывать
|
||
зависимости между службами) и увеличение надежности (перезапуск службы,
|
||
служебный или экстренный~--- в случае падения~--- не~приводит к недоступности
|
||
сокета). Тем не~менее, systemd ничуть не~хуже inetd может запускать службы в
|
||
ответ на входящие сетевые соединения.
|
||
|
||
Любая сокет-активация требует соответствующей поддержки со стороны самой
|
||
запускаемой службы. systemd предоставляет очень простой интерфейс, который может
|
||
быть использован службами для обеспечения сокет-активации. В основе этого
|
||
\href{http://0pointer.de/blog/projects/socket-activation.html}{простого и
|
||
минималистичного} механизма лежит функция
|
||
\hreftt{http://www.freedesktop.org/software/systemd/man/sd_listen_fds.html}%
|
||
{sd\_listen\_fds()}.
|
||
Однако, интерфейс, традиционно используемый в inetd, еще проще. Он позволяет
|
||
передавать запускаемой службе только один сокет, который формируется из потоков
|
||
STDIN и STDOUT запущенного процесса. Поддержка этого механизма также
|
||
присутствует в systemd~--- для обеспечения совместимости со множеством служб,
|
||
у которых сокет-активация реализована только в стиле inetd.
|
||
|
||
Прежде, чем мы перейдем к примерам, рассмотрим три различных схемы, использующих
|
||
сокет-активацию:
|
||
\begin{enumerate}
|
||
\item \textbf{Сокет-активация, ориентированная на параллелизацию,
|
||
упрощение и надежность:} сокеты создаются на ранних стадиях
|
||
загрузки, и единственный экземпляр службы тут же начинает
|
||
обслуживать все поступающие запросы. Такая схема подходит для
|
||
часто используемых системных служб~--- очевидно, что такие
|
||
службы лучше запускать как можно раньше, и по возможности
|
||
параллельно. В качестве примера можно привести D-Bus и Syslog.
|
||
\item \textbf{Сокет-активация для одиночных служб:} сокеты создаются на
|
||
ранних стадиях загрузки, и единственный экземпляр службы
|
||
запускается при получении входящих запросов. Такая схема больше
|
||
подходит для редко используемых служб, позволяя обеспечить
|
||
экономию системных ресурсов и уменьшить время загрузки, просто
|
||
отложив активацию службы до того момента, когда она
|
||
действительно понадобится. Пример~--- CUPS.
|
||
\item \textbf{Сокет-активация для служб, запускающих по экземпляру на
|
||
каждое соединение:} сокеты создаются на ранней стадии загрузки,
|
||
и при каждом входящем соединении запускается экземпляр службы,
|
||
которому передается сокет соединения (слушающий сокет при этом
|
||
остается у суперсервера, inetd или systemd). Эта схема подходит
|
||
для редко используемых служб, не~критичных по производительности
|
||
(каждый новый процесс занимает сравнительно немного ресурсов).
|
||
Пример: SSH.
|
||
\end{enumerate}
|
||
|
||
Описанные схемы отнюдь не~эквивалентны с точки зрения производительности. Первая
|
||
и вторая схема, после завершения запуска службы, обеспечивают точно такую же
|
||
производительность, как и в случае с независимой (stand-alone) службой (т.е.
|
||
службой, запущенной без использования суперсервера и сокет-активации), так как
|
||
слушающий сокет передается непосредственно процессу службы, и дальнейшая
|
||
обработка входящих соединений происходит точно так же, как и в независимой
|
||
службе. В то же время, производительность третьей из предложенных схем порою
|
||
оставляет желать лучшего: каждое входящее соединение порождает еще один процесс
|
||
службы, что, при большом количестве соединений, может привести к значительному
|
||
потреблению системных ресурсов. Впрочем, эта схема имеет и свои достоинства.
|
||
В частности, обеспечивается эффективная изоляция обработки клиентских запросов
|
||
и, кроме того, выбор такой схемы активации значительно упрощает процесс
|
||
разработки службы.
|
||
|
||
В systemd наибольшее внимание уделяется первой из этих схем, однако остальные
|
||
две тоже прекрасно поддерживаются. В свое время, я
|
||
\href{http://0pointer.de/blog/projects/socket-activation2.html}{рассказывал},
|
||
какие изменения в коде службы нужно сделать для обеспечения работы по второй
|
||
схеме, на примере сервера CUPS. Что же касается inetd, то он предназначен прежде
|
||
всего для работы по третьей схеме, хотя поддерживает и вторую (но не~первую).
|
||
Именно из-за специфики этой схемы inetd получил репутации <<медленного>> (что,
|
||
на самом деле, немного несправедливо).
|
||
|
||
Итак, изучив теоретическую сторону вопроса, перейдем к практике и рассмотрим,
|
||
как inetd-служба может быть интегрирована с механизмами сокет-активации systemd.
|
||
В качестве примера возьмем SSH, известную и широко распространенную службу. На
|
||
большинстве систем, где она используется, частота обращений к ней не~превышает
|
||
одного раза в час (как правило, она \emph{значительно} меньше этой величины).
|
||
SSH уже очень давно поддерживает сокет-активацию в стиле inetd, согласно третьей
|
||
схеме. Так как необходимость в данной службе возникает сравнительно редко, и
|
||
число одновременно работающих процессов обычно невелико, она хорошо подходит для
|
||
использования по этой схеме. Перерасход системных ресурсов должен быть
|
||
незначителен: большую часть времени такая служба фактически не~выполняется и
|
||
не~тратит ресурсы. Когда кто-либо начинает сеанс удаленной работы, она
|
||
запускается, и останавливается немедленно по завершении сеанса, освобождая
|
||
ресурсы. Что ж, посмотрим, как в systemd можно воспользоваться режимом
|
||
совместимости с inetd и обеспечить сокет-активацию SSH.
|
||
|
||
Так выглядит строка с конфигурацией службы SSH для классического inetd:
|
||
\begin{Verbatim}
|
||
ssh stream tcp nowait root /usr/sbin/sshd sshd -i
|
||
\end{Verbatim}
|
||
|
||
Аналогичный фрагмент конфигурации для xinetd:
|
||
\begin{Verbatim}
|
||
service ssh {
|
||
socket_type = stream
|
||
protocol = tcp
|
||
wait = no
|
||
user = root
|
||
server = /usr/sbin/sshd
|
||
server_args = -i
|
||
}
|
||
\end{Verbatim}
|
||
|
||
Б\'{о}льшая часть опций интуитивно понятна, кроме того, нетрудно заметить, что оба
|
||
этих текста повествуют об одном и том же. Однако, некоторые моменты не~вполне
|
||
очевидны. Например, номер прослушиваемого порта (22) не~указывается в явном
|
||
виде. Он определяется путем поиска имени службы в базе данных +/etc/services+.
|
||
Подобный подход был некогда весьма популярен в Unix, но сейчас он уже постепенно
|
||
выходит из моды, и поэтому в современных версиях xinetd поддерживается возможность
|
||
явного указания номера порта. Одна из наиболее интересных настроек названа
|
||
не~вполне очевидно~--- +nowait+ (+wait=no+). Она определяет, будет ли служба
|
||
работать по второй (+wait+) или третьей (+nowait+) схеме. И наконец, ключ +-i+
|
||
активирует inetd-режим в SSH-сервере (без этого ключа он работает в независимом
|
||
режиме).
|
||
|
||
На языке systemd эти фрагменты конфигурации превращаются в два файла. Первый из
|
||
них, +sshd.socket+, содержит информацию о прослушиваемом сокете:
|
||
\begin{Verbatim}
|
||
[Unit]
|
||
Description=SSH Socket for Per-Connection Servers
|
||
|
||
[Socket]
|
||
ListenStream=22
|
||
Accept=yes
|
||
|
||
[Install]
|
||
WantedBy=sockets.target
|
||
\end{Verbatim}
|
||
|
||
Смысл большинства опций вполне очевиден. Сделаю лишь пару замечаний.
|
||
+Accept=yes+ соответствует режиму +nowait+. Надеюсь, предложенное мною название
|
||
более точно отражает смысл опции~--- в режиме +nowait+ суперсвервер сам вызывает
|
||
+accept()+ для слушающего сокета, в то время как в режиме +wait+ эта работа
|
||
ложится на процесс службы. Опция +WantedBy=sockets.target+ обеспечивает
|
||
активацию данного юнита в нужный момент при загрузке системы.
|
||
|
||
Второй из этих файлов~--- +sshd@.service+:
|
||
\begin{Verbatim}
|
||
[Unit]
|
||
Description=SSH Per-Connection Server
|
||
|
||
[Service]
|
||
ExecStart=-/usr/sbin/sshd -i
|
||
StandardInput=socket
|
||
\end{Verbatim}
|
||
|
||
Большинство представленных здесь опций, как всегда, понятны интуитивно. Особое
|
||
внимание стоит обратить лишь на строчку +StandardInput=socket+, которая,
|
||
собственно, и включает для данной службы режим совместимости с inetd-активацией.
|
||
Опция +StandardInput=+ позволяет указать, куда будет подключен поток STDIN
|
||
процесса данной службы (подробности см.
|
||
\href{http://www.freedesktop.org/software/systemd/man/systemd.exec.html}%
|
||
{на странице руководства}). Задав для нее значение +socket+, мы обеспечиваем
|
||
подключение этого потока к сокету соединения, как того и требует механизм
|
||
inetd-активации. Заметим, что явно указывать опцию +StandardOutput=+ в данном
|
||
случае необязательно~--- она автоматически устанавливается в то же значение, что
|
||
и +StandardInput+, если явно не~указано что-то другое. Кроме того, можно
|
||
отметить наличие <<+-+>> перед именем бинарного файла sshd. Таким образом мы
|
||
указываем systemd игнорировать код выхода процесса sshd. По умолчанию, systemd
|
||
сохраняет коды выхода для всех экземпляров службы, завершившихся с ошибкой
|
||
(сбросить эту информацию можно командой +systemctl reset-failed+). SSH довольно
|
||
часто завершается с ненулевым кодом выхода, и мы разрешаем systemd
|
||
не~регистрировать подобные <<ошибки>>.
|
||
|
||
Служба +sshd@.service+ предназначена для работы в виде независимых экземпляров
|
||
(такие службы мы рассматривали в предыдущей главе~\ref{sec:instances}). Для
|
||
каждого входящего соединения systemd будет создавать свой экземпляр этой службы,
|
||
причем идентификатор экземпляра формируется на основе реквизитов соединения.
|
||
|
||
Быть может, вы спросите: почему для настройки inetd-службы в systemd требуется
|
||
два файла конфигурации, а не~один? Отвечаем: чтобы избежать излишнего
|
||
усложнения, мы обеспечиваем максимально прозрачную связь между работающими
|
||
юнитами и соответствующими им юнит-файлами. Такой подход позволяет независимо
|
||
оперировать юнитом сокета и юнитами соответствующих служб при формировании графа
|
||
зависимостей и при управлении юнитами. В частности, вы можете остановить
|
||
(удалить) сокет, не~затрагивая уже работающие экземпляры соответствующей службы,
|
||
или остановить любой из этих экземпляров, не~трогая другие экземпляры и сам
|
||
сокет.
|
||
|
||
Посмотрим, как наш пример будет работать. После того, как мы поместим оба
|
||
предложенных выше файла в каталог +/etc/systemd/system+, мы сможем включить
|
||
сокет (то есть, обеспечить его активацию при каждой нормальной загрузке) и
|
||
запустить его (то есть активировать в текущем сеансе работы):
|
||
|
||
\begin{Verbatim}
|
||
# systemctl enable sshd.socket
|
||
ln -s '/etc/systemd/system/sshd.socket' '/etc/systemd/system/sockets.target.wants/sshd.socket'
|
||
# systemctl start sshd.socket
|
||
# systemctl status sshd.socket
|
||
sshd.socket - SSH Socket for Per-Connection Servers
|
||
Loaded: loaded (/etc/systemd/system/sshd.socket; enabled)
|
||
Active: active (listening) since Mon, 26 Sep 2011 20:24:31 +0200; 14s ago
|
||
Accepted: 0; Connected: 0
|
||
CGroup: name=systemd:/system/sshd.socket
|
||
\end{Verbatim}
|
||
|
||
Итак, наш сокет уже прослушивается, но входящих соединений на него пока
|
||
не~поступало (счетчик +Accepted:+ показывает количество принятых соединений
|
||
с момента создания сокета, счетчик +Connected:+~--- количество активных
|
||
соединений на текущий момент).
|
||
|
||
Подключимся к нашему серверу с двух разных хостов, и посмотрим на список служб:
|
||
\begin{Verbatim}[fontsize=\small]
|
||
$ systemctl --full | grep ssh
|
||
sshd@172.31.0.52:22-172.31.0.4:47779.service loaded active running SSH Per-Connection Server
|
||
sshd@172.31.0.52:22-172.31.0.54:52985.service loaded active running SSH Per-Connection Server
|
||
sshd.socket loaded active listening SSH Socket for Per-Connection Servers
|
||
\end{Verbatim}
|
||
|
||
Как и следовало ожидать, работают два экземпляра нашей службы, по одному на
|
||
соединение, и их в названиях указаны IP-адреса и TCP-порты источника и
|
||
получателя. (Заметим, что в случае с локальными сокетами типа +AF_UNIX+ там были
|
||
бы указаны идентификаторы процесса и пользователя, соответствующие подключенному
|
||
клиенту.) Таким образом, мы можем независимо выслеживать и убивать отдельные
|
||
экземпляры sshd (например, если нам нужно прервать конкретный удаленный сеанс):
|
||
\begin{Verbatim}
|
||
# systemctl kill sshd@172.31.0.52:22-172.31.0.4:47779.service
|
||
\end{Verbatim}
|
||
|
||
Вот, пожалуй, и все, что вам нужно знать о портировании inetd-служб в systemd и
|
||
дальнейшем их использовании.
|
||
|
||
Применительно к SSH, в большинстве случаев схема с inetd-активацией позволяет
|
||
сэкономить системные ресурсы и поэтому оказывается более эффективным решением,
|
||
чем использование обычного service-файла sshd, обеспечивающего запуск одиночной
|
||
службы без использования сокет-активации (поставляется в составе пакета и может
|
||
быть включен при необходимости). В ближайшее время я собираюсь направить
|
||
соответствующий запрос относительно нашего пакета SSH в багтрекер Fedora.
|
||
|
||
В завершение нашей дискуссии, сравним возможности xinetd и systemd, и выясним,
|
||
может ли systemd полностью заменить xinetd, или нет. Вкратце: systemd
|
||
поддерживает далеко не~все возможности xinetd и поэтому отнюдь не~является его
|
||
полноценной заменой на все случаи жизни. В частности, если вы заглянете в
|
||
\href{http://linux.die.net/man/5/xinetd.conf}{список параметров конфигурации}
|
||
xinetd, вы заметите, что далеко не~все эти опции доступны в systemd. Например, в
|
||
systemd нет и никогда не~будет встроенных служб +echo+, +time+, +daytime+,
|
||
+discard+ и т.д. Кроме того, systemd не~поддерживает TCPMUX и RPC. Впрочем,
|
||
б\'{о}льшая часть этих опций уже не~актуальна в наше время. Подавляющее
|
||
большинство inetd-служб не~используют эти опции (в частности, ни~одна из
|
||
имеющихся в Fedora xinetd-служб не~требует поддержки перечисленных опций). Стоит
|
||
отметить, что xinetd имеет некоторые интересные возможности, отсутствующие в
|
||
systemd, например, списки контроля доступа для IP-адресов (IP ACL). Однако,
|
||
большинство администраторов, скорее всего, согласятся, что настройка брандмауэра
|
||
является более эффективным решением подобных задач\footnote{Прим. перев.: Стоит
|
||
отметить, что приведенный пример является не~единственным случаем, когда
|
||
возможности брандмауэра Linux дублируются опциями xinetd. Например, количество
|
||
соединений с каждого хоста может быть ограничено критерием connlimit, а
|
||
скорость поступления входящих соединений можно контролировать сочетанием
|
||
критериев limit и conntrack (+ctstate NEW+). Критерий recent позволяет создать
|
||
аналог простейшей IDS/IPS, реализованной механизмом SENSORS в xinetd. Кроме
|
||
того, в ряде случаев возможности брандмауэра значительно превосходят
|
||
функциональность xinetd. Например, критерий hashlimit, опять-таки в сочетании с
|
||
conntrack, позволяет ограничить скорость поступления входящих соединений с
|
||
каждого хоста (не~путать с критерием connlimit, ограничивающим количество
|
||
одновременно открытых соединений). Также стоит отметить, что интегрированная в
|
||
Linux подсистема ipset гораздо лучше подходит для работы с большими списками
|
||
разрешенных/запрещенных адресов, нежели встроенные механизмы xinetd.}, а для
|
||
ценителей устаревших технологий systemd предлагает поддержку tcpwrap. С другой
|
||
стороны, systemd тоже предоставляет ряд возможностей, отсутствующих в xinetd, в
|
||
частности, индивидуальный контроль над каждым экземпляром службы (см. выше), и
|
||
внушительный
|
||
\href{http://www.freedesktop.org/software/systemd/man/systemd.exec.html}{набор
|
||
настроек} для контроля окружения, в котором запускаются экземпляры. Я надеюсь,
|
||
что возможностей systemd должно быть достаточно для решения большинства задач, а
|
||
в тех редких случаях, когда вам потребуются специфические опции xinetd~--- ничто
|
||
не~мешает вам запустить его в дополнение к systemd. Таким образом, уже сейчас в
|
||
большинстве случаев xinetd можно выкинуть из числа обязательных системных
|
||
компонентов. Можно сказать, что systemd не~просто возвращает функциональность
|
||
классического юниксового inetd, но еще и восстанавливает ее ключевую роль в
|
||
Linux-системах.
|
||
|
||
Теперь, вооруженные этими знаниями, вы можете портировать свои службы с inetd на
|
||
systemd. Но, конечно, будет лучше, если этим займутся разработчики из апстрима
|
||
приложения, или сопровождающие вашего дистрибутива.
|
||
|
||
\section{К вопросу о безопасности}
|
||
\label{sec:security}
|
||
|
||
Одно из важнейших достоинств Unix-систем~--- концепция разделения привилегий
|
||
между различными компонентами ОС. В частности, службы обычно работают от имени
|
||
специальных системных пользователей, имеющих ограниченные полномочия, что
|
||
позволяет уменьшить ущерб для системы в случае взлома этих служб.
|
||
|
||
Однако, такой подход предоставляет лишь самую минимальную защиту, так как
|
||
системные службы, хотя уже и не~получают полномочий администратора (root), все
|
||
равно имеют практически те же права, что и обычные пользователи. Чтобы
|
||
обеспечить более эффективную защиту, нужно поставить более жесткие ограничения,
|
||
отняв у служб некоторые привилегии, присущие обычным пользователям.
|
||
|
||
Такая возможность предоставляется системами мандатного контроля доступа (далее
|
||
MAC, от Mandatory Access Control), например, SELinux. Если вам нужно обеспечить
|
||
высокий уровень безопасности на своем сервере, то вам определенно стоит обратить
|
||
свое внимание на SELinux. Что же касается systemd, то он предоставляет
|
||
разработчикам и администраторам целый арсенал возможностей по ограничению
|
||
локальных служб, и эти механизмы работают независимо от систем MAC. Таким
|
||
образом, вне зависимости от того, смогли ли вы разобраться с SELinux~--- у вас
|
||
появляется еще несколько инструментов, позволяющих повысить уровень
|
||
безопасности.
|
||
|
||
В этой главе мы рассмотрим несколько таких опций, предоставляемых systemd, и
|
||
обсудим вопросы их практического применения. Реализация этих опций основана на
|
||
использовании ряда уникальных технологий безопасности, интегрированных в ядро
|
||
Linux уже очень давно, но при этом практически неизвестных для большинства
|
||
разработчиков. Мы постарались сделать соответствующие опции systemd максимально
|
||
простыми в использовании, чтобы заинтересовать администраторов и апстримных
|
||
разработчиков. Вот краткий перечень наиболее интересных
|
||
возможностей\footnote{Прим. перев.: В приведенном здесь списке не~упомянута
|
||
встроенная в systemd поддержка фильтров seccomp (опция +SystemCallFilter=+),
|
||
так как она была добавлена уже после написания исходной статьи, в выпуске
|
||
systemd 187.}:
|
||
\begin{itemize}
|
||
\item Изолирование служб от сети
|
||
\item Предоставление службам независимых каталогов +/tmp+
|
||
\item Ограничение доступа служб к отдельным каталогам
|
||
\item Принудительное отключение полномочий (capabilities) для служб
|
||
\item Запрет форка, ограничение на создание файлов
|
||
\item Контроль доступа служб к файлам устройств
|
||
\end{itemize}
|
||
|
||
Все эти опции описаны в man-страницах systemd, главным образом, в
|
||
\href{http://www.freedesktop.org/software/systemd/man/systemd.exec.html}{systemd.exec(5)}%
|
||
\footnote{Прим. перев.: Начиная с systemd версии 206, значительная часть
|
||
обсуждаемых здесь настроек вынесена на отдельную страницу
|
||
\href{http://www.freedesktop.org/software/systemd/man/systemd.cgroup.html}%
|
||
{systemd.cgroup(5)}. Начиная с systemd 208, эта страница переименована в
|
||
\href{http://www.freedesktop.org/software/systemd/man/systemd.resource-control.html}%
|
||
{systemd.resource-control(5)}.}. Если вам потребуется дополнительная информация,
|
||
вы можете обратиться к ним.
|
||
|
||
Все эти опции доступны на системах с systemd, вне зависимости от использования
|
||
SELinux или любой другой реализации MAC.
|
||
|
||
Все эти опции не~так уж и обременительны, и поэтому их разумнее будет
|
||
использовать даже в тех случаях, когда явная необходимость в них, казалось бы,
|
||
отсутствует. Например: даже если вы полагаете, что ваша служба никогда не~пишет
|
||
в каталог +/tmp+, и поэтому использование +PrivateTmp=yes+ (см. ниже) вроде бы и
|
||
не~обязательно~--- лучше включить эту опцию, просто потому, что вы не~можете
|
||
знать наверняка, как будут вести себя используемые вами сторонние библиотеки (и
|
||
плагины для них). В частности, вы никогда не~узнаете, какие модули NSS могут
|
||
быть включены в каждой конкретной системе, и пользуются ли они каталогом +/tmp+.
|
||
|
||
Мы надеемся, что перечисленные опции окажутся полезными как для администраторов,
|
||
защищающих свои системы, так и для апстримных разработчиков, желающих сделать
|
||
свои службы безопасными <<из коробки>>. Мы настоятельно рекомендуем
|
||
разработчикам использовать такие опции по умолчанию в апстримных
|
||
service-файлах~--- это сравнительно несложно, но дает значительные преимущества
|
||
в плане безопасности.
|
||
|
||
\subsection{Изолирование служб от сети}
|
||
|
||
Простая, но мощная опция, которой вы можете воспользоваться при настройке
|
||
службы~--- +PrivateNetwork=+:
|
||
\begin{Verbatim}
|
||
...
|
||
[Service]
|
||
ExecStart=...
|
||
PrivateNetwork=yes
|
||
...
|
||
\end{Verbatim}
|
||
Добавление этой строчки обеспечивает полную изоляцию от сети всех процессов
|
||
данной службы. Они будут видеть лишь интерфейс обратной петли (+lo+), причем
|
||
полностью изолированный от обратной петли основной системы. Чрезвычайно
|
||
эффективная защита против сетевых атак.
|
||
|
||
\begin{caveat}
|
||
Некоторым службам сеть необходима для нормальной работы. Разумеется, никто и
|
||
не~говорит о том, чтобы применять +PrivateNetwork=yes+ к сетевым службам, таким,
|
||
как Apache. Однако даже те службы, которые не~ориентированы на сетевое
|
||
взаимодействие, могут нуждаться в сети для нормального функционирования, и порой
|
||
эта потребность не~вполне очевидна. Например, если ваша система хранит
|
||
учетные записи пользователей в базе LDAP, для выполнения системных вызовов
|
||
наподобие +getpwnam()+ может потребоваться обращение к сети. Впрочем, даже в
|
||
такой ситуации, как правило, можно использовать +PrivateNetwork=yes+, за
|
||
исключением случаев, когда службе может потребоваться информация о пользователях
|
||
с UID~$\geq1000$.
|
||
\end{caveat}
|
||
|
||
Если вас интересуют технические подробности: эта возможность реализована с
|
||
использованием технологии сетевых пространств имен (network namespaces). При
|
||
задействовании данной опции, для службы создается новое пространство имен, в
|
||
котором настраивается только интерфейс обратной петли.
|
||
|
||
\subsection{Предоставление службам независимых каталогов \texttt{/tmp}}
|
||
|
||
Еще одна простая, но мощная опция настройки служб~--- +PrivateTmp=+:
|
||
\begin{Verbatim}
|
||
...
|
||
[Service]
|
||
ExecStart=...
|
||
PrivateTmp=yes
|
||
...
|
||
\end{Verbatim}
|
||
При задействовании этой опции, служба получит свой собственный каталог +/tmp+,
|
||
полностью изолированный от общесистемного +/tmp+. По давно сложившейся традиции,
|
||
в Unix-системах каталог +/tmp+ является общедоступным для всех локальных служб и
|
||
пользователей. За все эти годы, он стал источником огромного количества проблем
|
||
безопасности. Чаще всего встречаются атаки с использованием символьных ссылок
|
||
(symlink attacks) и атаки на отказ в обслуживании (DoS attacks). Использование
|
||
независимой версии данного каталога для каждой службы делает подобные уязвимости
|
||
практически бесполезными.
|
||
|
||
Для релиза Fedora~17
|
||
\href{https://fedoraproject.org/wiki/Features/ServicesPrivateTmp}{утверждена}
|
||
инициатива, направленная на включение этой опции по умолчанию для большинства
|
||
системных служб.
|
||
|
||
\begin{caveat}
|
||
Некоторые службы используют +/tmp+ не~по назначению,
|
||
помещая туда сокеты IPC и другие подобные элементы, что само по себе уже
|
||
является уязвимостью (хотя бы потому, что используемые при передаче информации
|
||
файлы и каталоги должны иметь предсказуемые имена, что делает подобные службы
|
||
уязвимыми к атакам через символьные ссылки и атакам на отказ в обслуживании).
|
||
Подобные объекты лучше помещать в каталог +/run+, так как в нем присутствует
|
||
строгое разделение доступа. В качестве примера такого некорректного
|
||
использования +/tmp+ можно привести X11, размещающий там свои коммуникационные
|
||
сокеты (впрочем, в данном конкретном случае некоторые меры безопасности все же
|
||
присутствуют: сокеты размещаются в защищенном подкаталоге, который создается на
|
||
ранних стадиях загрузки). Разумеется, для служб, использующих +/tmp+ в целях
|
||
коммуникации, включение опции +PrivateTmp=yes+ недопустимо. К счастью, подобных
|
||
служб сейчас уже не~так уж и много\footnote{Прим. перев.: Начиная с systemd 209,
|
||
поддерживается опция +JoinsNamespaceOf=+ (секция +[Unit]+), позволяющая
|
||
поместить два и более юнитов в одну <<песочницу>> (пространство имен), в
|
||
результате чего такие юниты смогут взаимодействовать между собой, как через
|
||
сетевой интерфейс обратной петли, так и через файлы в каталоге +/tmp+,
|
||
при этом оставаясь изолированными от остальной системы. Подробности можно
|
||
уточнить на странице руководства
|
||
\href{http://www.freedesktop.org/software/systemd/man/systemd.unit.html}{systemd.unit(5)}.}.
|
||
\end{caveat}
|
||
|
||
Эта опция использует технологию пространств имен файловых систем (filesystem
|
||
namespaces), реализованную в Linux. При включении данной опции, для службы
|
||
создается новое пространство имен, отличающееся от иерархии каталогов основной
|
||
системы только каталогом +/tmp+.
|
||
|
||
\subsection{Ограничение доступа служб к отдельным каталогам}
|
||
|
||
Используя опции +ReadOnlyDirectories=+ и +InaccessibleDirectories=+, вы можете
|
||
ограничить доступ службы к указанным каталогам только чтением, либо вообще
|
||
запретить его:
|
||
\begin{Verbatim}
|
||
...
|
||
[Service]
|
||
ExecStart=...
|
||
InaccessibleDirectories=/home
|
||
ReadOnlyDirectories=/var
|
||
...
|
||
\end{Verbatim}
|
||
Добавление этих двух строчек в файл конфигурации приводит к тому, что служба
|
||
полностью утрачивает доступ к содержимому каталога +/home+ (она видит лишь
|
||
пустой каталог с правами доступа 000), а также не~может писать внутрь каталога
|
||
+/var+.
|
||
|
||
\begin{caveat}
|
||
К сожалению, в настоящее время опция +ReadOnlyDirectories=+ не~применяется
|
||
рекурсивно к точкам монтирования, расположенным в поддереве указанного каталога
|
||
(т.е. файловые системы, смонтированные в подкаталогах +/var+, по-прежнему
|
||
останутся доступными на запись, если, конечно, не~перечислить их все поименно).
|
||
Мы собираемся исправить это в ближайшее время.
|
||
\end{caveat}
|
||
|
||
Механизм работы этой опции тоже реализован с использованием пространств имен
|
||
файловых систем.
|
||
|
||
\subsection{Принудительное отключение полномочий (capabilities) для служб}
|
||
|
||
Еще одна чрезвычайно эффективная опция~--- +CapabilityBoundingSet=+, позволяющая
|
||
контролировать список capabilities, которые смогут получить процессы службы:
|
||
\begin{Verbatim}
|
||
...
|
||
[Service]
|
||
ExecStart=...
|
||
CapabilityBoundingSet=CAP_CHOWN CAP_KILL
|
||
...
|
||
\end{Verbatim}
|
||
В нашем примере, служба может иметь лишь полномочия +CAP_CHOWN+ и +CAP_KILL+.
|
||
Попытки какого-либо из процессов службы получить любые другие полномочия, даже с
|
||
использованием suid-бинарников, будут пресекаться. Список возможных полномочий
|
||
приведен на странице документации
|
||
\href{http://linux.die.net/man/7/capabilities}{capabilities(7)}. К сожалению,
|
||
некоторые из них являются слишком общими (разрешают очень многое), например,
|
||
+CAP_SYS_ADMIN+, однако выборочное делегирование полномочий все же является
|
||
неплохой альтернативой запуску службы с полными административными (рутовыми)
|
||
привилегиями.
|
||
|
||
Как правило, определение списка полномочий, необходимых для работы конкретной
|
||
службы, является довольно трудоемким процессом, требующим ряда проверок. Чтобы
|
||
немного упростить эту задачу, мы добавили возможность создания <<черного
|
||
списка>> привилегий, как альтернативы описанному выше механизму <<белого
|
||
списка>>. Вместо того, чтобы указывать, какие привилегии можно оставить, вы
|
||
можете перечислить, какие из них оставлять точно нельзя. Например: привилегия
|
||
+CAP_SYS_PTRACE+, предназначенная для отладчиков, дает очень широкие полномочия
|
||
в отношении всех процессов системы. Очевидно, что такие службы, как Apache,
|
||
не~занимаются отладкой чужих процессов, и поэтому мы можем спокойно отнять у них
|
||
данную привилегию:
|
||
\begin{Verbatim}
|
||
...
|
||
[Service]
|
||
ExecStart=...
|
||
CapabilityBoundingSet=~CAP_SYS_PTRACE
|
||
...
|
||
\end{Verbatim}
|
||
Наличие символа +~+ после знака равенства инвертирует принцип работы опции:
|
||
следующий за ним перечень привилегий рассматривается уже не~как белый, а как
|
||
черный список.
|
||
|
||
\begin{caveat}
|
||
Некоторые службы, при отсутствии определенных привилегий, могут вести себя
|
||
некорректно. Как уже говорилось выше, формирование списка необходимых полномочий
|
||
для каждой конкретной службы может оказаться довольно трудной задачей, и лучше
|
||
всего будет обратиться с соответствующим запросом к разработчикам службы.
|
||
\end{caveat}
|
||
|
||
\begin{caveat}[~2]
|
||
\href{https://forums.grsecurity.net/viewtopic.php?f=7&t=2522}{Привилегии~---
|
||
отнюдь не~лекарство от всех бед.} Чтобы они работали действительно эффективно,
|
||
возможно, придется дополнить их другими методиками защиты.
|
||
\end{caveat}
|
||
|
||
Чтобы проверить, какие именно привилегии имеют сейчас ваши процессы, вы можете
|
||
воспользоваться программой +pscap+ из комплекта +libcap-ng-utils+.
|
||
|
||
Применение опции +CapabilityBoundingSet=+ является простой, прозрачной и удобной
|
||
альтернативой введению аналогичных ограничений через модификацию исходного кода
|
||
всех системных служб.
|
||
|
||
\subsection{Запрет форка, ограничение на создание файлов}
|
||
|
||
Некоторые меры безопасности можно ввести при помощи механизма ограничения
|
||
ресурсов. Как следует из его названия, этот механизм предназначен прежде всего
|
||
для контроля использования ресурсов, а не~для контроля доступа. Однако, две его
|
||
настройки могут быть использованы для запрета определенных действий:
|
||
с помощью +RLIMIT_NPROC+ можно запретить службе форкаться (запускать
|
||
дополнительные процессы), а +RLIMIT_FSIZE+ позволяет блокировать запись в файлы
|
||
ненулевого размера:
|
||
\begin{Verbatim}
|
||
...
|
||
[Service]
|
||
ExecStart=...
|
||
LimitNPROC=1
|
||
LimitFSIZE=0
|
||
...
|
||
\end{Verbatim}
|
||
Обратите внимание, что эти ограничения будут эффективно работать только в том
|
||
случае, если служба запускается от имени простого пользователя (не~root) и
|
||
без привилегии +CAP_SYS_RESOURCE+ (блокирование этой привилегии можно
|
||
обеспечить, например, описанной выше опцией +CapabilityBoundingSet=+). В
|
||
противном случае, ничто не~мешает процессу поменять соответствующие ограничения.
|
||
|
||
\begin{caveat}
|
||
+LimitFSIZE=+ действует очень жестко. Если процесс пытается записать файл
|
||
ненулевого размера, он немедленно получает сигнал +SIGXFSZ+, который, как
|
||
правило, прекращает работу процесса (в случае, если не~назначен обработчик
|
||
сигнала). Кроме того, стоит отметить, что эта опция не~запрещает создание файлов
|
||
нулевого размера.
|
||
\end{caveat}
|
||
|
||
Подробности об этих и других опциях ограничения ресурсов можно уточнить на
|
||
странице документации \href{http://linux.die.net/man/2/setrlimit}{setrlimit(2)}.
|
||
|
||
\subsection{Контроль доступа служб к файлам устройств}
|
||
|
||
Файлы устройств предоставляют приложениям интерфейс для доступа к ядру и
|
||
драйверам. Причем драйверы, как правило, менее тщательно тестируются и не~так
|
||
аккуратно проверяются на предмет наличия уязвимостей, по сравнению с основными
|
||
компонентами ядра. Именно поэтому драйверы часто становятся объектами атаки
|
||
злоумышленников. systemd позволяет контролировать доступ к устройствам
|
||
индивидуально для каждой службы:
|
||
\begin{Verbatim}
|
||
...
|
||
[Service]
|
||
ExecStart=...
|
||
DeviceAllow=/dev/null rw
|
||
...
|
||
\end{Verbatim}
|
||
Приведенная конфигурация разрешит службе доступ только к файлу +/dev/null+,
|
||
запретив доступ ко всем остальным файлам устройств.
|
||
|
||
Реализация данной опции построена на использовании cgroups-контроллера
|
||
+devices+.
|
||
|
||
\subsection{Прочие настройки}
|
||
|
||
Помимо приведенных выше, простых и удобных в использовании опций, существует и
|
||
ряд других других настроек, позволяющих повысить уровень безопасности. Но
|
||
для их использования, как правило, уже требуются определенные изменения в
|
||
исходном коде службы, и поэтому такие настройки представляют интерес прежде
|
||
всего для апстримных разработчиков. В частности, сюда относится опция
|
||
+RootDirectory=+ (позволяющая запустить службу +chroot()+-окружении), а также
|
||
параметры +User=+ и +Group=+, определяющие пользователя и группу, от имени
|
||
которых работает служба. Использование этих опций значительно упрощает
|
||
написание демонов, так как работа по понижению привилегий ложится на плечи
|
||
systemd.
|
||
|
||
Возможно, у вас возникнет вопрос, почему описанные выше опции не~включены по
|
||
умолчанию. Отвечаем: чтобы не~нарушать совместимость. Многие из них несколько
|
||
отступают от традиций, принятых в Unix. Например, исторически сложилось, так что
|
||
+/tmp+ является общим для всех процессов и пользователей. Существуют программы,
|
||
использующие этот каталог для IPC, и включение по умолчанию режима изоляции для
|
||
+/tmp+ нарушит работу таких программ.
|
||
|
||
И несколько слов в заключение. Если вы занимаетесь сопровождением unit-файлов
|
||
в апстримном проекте или в дистрибутиве, пожалуйста, подумайте о том, чтобы
|
||
воспользоваться приведенными выше настройками. Если сопровождаемая вами служба
|
||
станет более защищенной в конфигурации по умолчанию (<<из коробки>>), от этого
|
||
выиграют не~только ваши пользователи, но и все мы, потому что Интернет станет
|
||
чуть более безопасным.
|
||
|
||
\section{Отчет о состоянии службы и ее журнал}
|
||
\label{sec:journal}
|
||
|
||
При работе с системами, использующими systemd, одной из наиболее важных и часто
|
||
используемых команд является +systemctl status+. Она выводит отчет о состоянии
|
||
службы (или другого юнита). В таком отчете приводится статус службы (работает
|
||
она или нет), список ее процессов и другая полезная информация.
|
||
|
||
В Fedora~17 мы ввели
|
||
\href{http://0pointer.de/blog/projects/the-journal.html}{Journal}, новую
|
||
реализацию системного журнала, обеспечивающую структурированное, индексированное
|
||
и надежное журналирование, при сохранении совместимости с классическими
|
||
реализациями syslog. Собственно, мы начали работу над~Journal только потому, что
|
||
хотели добавить одну, казалось бы, простую, возможность: включить в вывод
|
||
+systemctl status+ последние 10 сообщений, поступивших от службы в системный
|
||
журнал. Но на практике оказалось, что в рамках классических механизмов syslog,
|
||
реализация этой возможности чрезвычайно сложна и малоэффективна. С другой
|
||
стороны, сообщения службы в системный журнал несут очень важную информацию о ее
|
||
состоянии, и такая возможность действительно была бы очень полезной, особенно
|
||
при диагностике нештатных ситуаций.
|
||
|
||
Итак, мы интегрировали Journal в systemd, и научили +systemctl+ работать с ним.
|
||
Результат выглядит примерно так:
|
||
\begin{Verbatim}[fontsize=\small]
|
||
$ systemctl status avahi-daemon.service
|
||
avahi-daemon.service - Avahi mDNS/DNS-SD Stack
|
||
Loaded: loaded (/usr/lib/systemd/system/avahi-daemon.service; enabled)
|
||
Active: active (running) since Fri, 18 May 2012 12:27:37 +0200; 14s ago
|
||
Main PID: 8216 (avahi-daemon)
|
||
Status: "avahi-daemon 0.6.30 starting up."
|
||
CGroup: name=systemd:/system/avahi-daemon.service
|
||
├ 8216 avahi-daemon: running [omega.local]
|
||
└ 8217 avahi-daemon: chroot helper
|
||
|
||
May 18 12:27:37 omega avahi-daemon[8216]: Joining mDNS multicast group on interface eth1.IPv4 with address 172.31.0.52.
|
||
May 18 12:27:37 omega avahi-daemon[8216]: New relevant interface eth1.IPv4 for mDNS.
|
||
May 18 12:27:37 omega avahi-daemon[8216]: Network interface enumeration completed.
|
||
May 18 12:27:37 omega avahi-daemon[8216]: Registering new address record for 192.168.122.1 on virbr0.IPv4.
|
||
May 18 12:27:37 omega avahi-daemon[8216]: Registering new address record for fd00::e269:95ff:fe87:e282 on eth1.*.
|
||
May 18 12:27:37 omega avahi-daemon[8216]: Registering new address record for 172.31.0.52 on eth1.IPv4.
|
||
May 18 12:27:37 omega avahi-daemon[8216]: Registering HINFO record with values 'X86_64'/'LINUX'.
|
||
May 18 12:27:38 omega avahi-daemon[8216]: Server startup complete. Host name is omega.local. Local service cookie is 3555095952.
|
||
May 18 12:27:38 omega avahi-daemon[8216]: Service "omega" (/services/ssh.service) successfully established.
|
||
May 18 12:27:38 omega avahi-daemon[8216]: Service "omega" (/services/sftp-ssh.service) successfully established.
|
||
\end{Verbatim}
|
||
Очевидно, это отчет о состоянии всеми любимого демона mDNS/DNS-SD, причем после
|
||
списка процессов, как мы и обещали, приведены сообщения этого демона в системный
|
||
журнал. Миссия выполнена!
|
||
|
||
Команда +systemctl status+ поддерживает ряд опций, позволяющих настроить вывод
|
||
информации в соответствии с вашими пожеланиями. Отметим две наиболее интересные
|
||
из них: ключ +-f+ позволяет в реальном времени отслеживать обновление сведений
|
||
(по аналогии с +tail -f+), а ключ +-n+ задает количество выводимых журнальных
|
||
записей (как нетрудно догадаться, по аналогии с +tail -n+).
|
||
|
||
Журнальные записи собираются из трех различных источников. Во-первых, это
|
||
сообщения службы в системный журнал, отправленные через функцию libc
|
||
+syslog()+. Во-вторых, это сообщения, отправленные через штатный API системы
|
||
Journal. И наконец, это весь текст, выводимый демоном в STDOUT и STDERR. Проще
|
||
говоря, все, что демон считает нужным сказать администратору, собирается,
|
||
упорядочивается и отображается в одинаковом формате.
|
||
|
||
Добавленная нами возможность, при всей своей простоте, может оказаться
|
||
чрезвычайно полезной практически любому администратору. По-хорошему, ее стоило
|
||
реализовать еще 15 лет назад.
|
||
|
||
\section{Самодокументированный процесс загрузки}
|
||
|
||
Нам часто приходится слышать жалобы, что процесс загрузки при использовании
|
||
systemd очень сложен для понимания. Не~могу с этим согласиться. Более того, я
|
||
берусь даже утверждать обратное: по сравнению с тем, что мы имели раньше (когда
|
||
для того, чтобы разобраться в загрузке, нужно было иметь хорошие навыки
|
||
программирования на языке Bourne Shell\footnote{Чья привлекательность очень
|
||
коварна и обманчива.}), процесс загрузки стал более простым и прозрачным. Но
|
||
определенная доля истины в этом критическом замечании все же есть: даже опытному
|
||
Unix-администратору при переходе на systemd нужно изучить некоторые новые для
|
||
себя вещи. Мы, разработчики systemd, обязаны максимально упростить такое
|
||
обучение. Решая поставленную задачу, мы подготовили для вас кое-какие приятные
|
||
сюрпризы, и предоставили хорошую документацию даже там, где это казалось
|
||
невозможным.
|
||
|
||
Уже сейчас systemd располагает довольно обширной документацией, включая
|
||
\href{http://www.freedesktop.org/software/systemd/man/}{страницы руководства}
|
||
(на данный момент, их около сотни),
|
||
\href{http://www.freedesktop.org/wiki/Software/systemd}{wiki-сайт проекта}, а
|
||
также ряд статей в моем блоге. Тем не~менее, большое количество документации еще
|
||
не~гарантирует простоты понимания. Огромные груды руководств выглядят пугающе, и
|
||
неподготовленный читатель не~может разобраться, с какого места ему начинать
|
||
чтение, если его интересует просто общая концепция.
|
||
|
||
Чтобы решить данную проблему, мы добавили в systemd небольшую, но очень изящную
|
||
возможность: самодокументированный\footnote{Прим. перев.: В оригинале
|
||
использован термин <<self-explanatory>>, который также можно перевести как
|
||
<<само-объясняющий>>, <<самоочевидный>>.} процесс загрузки. Что мы под этим
|
||
подразумеваем? То, что для каждого элемента процесса загрузки теперь имеется
|
||
документация, прозрачно привязанная к этому элементу, так что ее поиск
|
||
не~представляет трудности.
|
||
|
||
Иными словами, все штатные юниты systemd (которые, собственно, и формируют
|
||
процесс загрузки, вызывая соответствующие программы) включают ссылки на страницы
|
||
документации, описывающие назначение юнита/программы, используемые ими
|
||
конфигурационные файлы и т.д. Если пользователь хочет узнать, для чего
|
||
предназначен какой-либо юнит, какова его роль в процессе загрузки и как его
|
||
можно настроить, может получить ссылки на соответствующую документацию, просто
|
||
воспользовавшись давно известной командой +systemctl status+. Возьмем для
|
||
примера +systemd-logind.service+:
|
||
\begin{Verbatim}[fontsize=\small,commandchars=\\\{\},%
|
||
% В окружении Verbatim ссылки, содержащие знак дефиса/минуса "-",
|
||
% повреждаются. Обходим это при помощи черной магии в стиле sysvinit:
|
||
codes={\catcode`+=\active},defineactive=\def+{-}]
|
||
$ systemctl status systemd-logind.service
|
||
systemd-logind.service - Login Service
|
||
Loaded: loaded (/usr/lib/systemd/system/systemd-logind.service; static)
|
||
Active: active (running) since Mon, 25 Jun 2012 22:39:24 +0200; 1 day and 18h ago
|
||
Docs: \href{http://www.freedesktop.org/software/systemd/man/systemd+logind.service.html}{man:systemd-logind.service(7)}
|
||
\href{http://www.freedesktop.org/software/systemd/man/logind.conf.html}{man:logind.conf(5)}
|
||
\url{http://www.freedesktop.org/wiki/Software/systemd/multiseat}
|
||
Main PID: 562 (systemd-logind)
|
||
CGroup: name=systemd:/system/systemd-logind.service
|
||
└ 562 /usr/lib/systemd/systemd-logind
|
||
|
||
Jun 25 22:39:24 epsilon systemd-logind[562]: Watching system buttons on /dev/input/event2 (Power Button)
|
||
Jun 25 22:39:24 epsilon systemd-logind[562]: Watching system buttons on /dev/input/event6 (Video Bus)
|
||
Jun 25 22:39:24 epsilon systemd-logind[562]: Watching system buttons on /dev/input/event0 (Lid Switch)
|
||
Jun 25 22:39:24 epsilon systemd-logind[562]: Watching system buttons on /dev/input/event1 (Sleep Button)
|
||
Jun 25 22:39:24 epsilon systemd-logind[562]: Watching system buttons on /dev/input/event7 (ThinkPad Extra Buttons)
|
||
Jun 25 22:39:25 epsilon systemd-logind[562]: New session 1 of user gdm.
|
||
Jun 25 22:39:25 epsilon systemd-logind[562]: Linked /tmp/.X11-unix/X0 to /run/user/42/X11-display.
|
||
Jun 25 22:39:32 epsilon systemd-logind[562]: New session 2 of user lennart.
|
||
Jun 25 22:39:32 epsilon systemd-logind[562]: Linked /tmp/.X11-unix/X0 to /run/user/500/X11-display.
|
||
Jun 25 22:39:54 epsilon systemd-logind[562]: Removed session 1.
|
||
\end{Verbatim}
|
||
|
||
На первый взгляд, в выводе этой команды почти ничего не~изменилось. Но если вы
|
||
вглядитесь повнимательнее, то заметите новое поле +Docs+, в котором приведены
|
||
ссылки на документацию по данной службе. В нашем случае случае это два URI,
|
||
ссылающихся на man-страницы, и один URL, указывающий на веб-страницу.
|
||
man-страницы описывают соответственно предназначение службы и ее настройки, а
|
||
веб-страница~--- базовые концепции, которые реализуются этой службой.
|
||
|
||
Тем, кто использует современные графические эмуляторы терминала, чтобы открыть
|
||
соответствующую страницу документации, достаточно щелкнуть мышью по
|
||
URI\footnote{Для нормальной работы данной функции, в эмуляторе терминала должен
|
||
быть исправлена \href{https://bugzilla.gnome.org/show_bug.cgi?id=676452}{эта
|
||
ошибка}, а в программе просмотра страниц помощи~---
|
||
\href{https://bugzilla.gnome.org/show_bug.cgi?id=676482}{вот эта}.}. Иными
|
||
словами, доступ к документации компонентов загрузки становится предельно
|
||
простым: достаточно вызвать +systemctl status+, после чего щелкнуть по
|
||
полученным ссылкам.
|
||
|
||
Если же вы используете не~графический эмулятор терминала (где можно просто
|
||
щелкнуть по URI), а настоящий терминал, наличие URI где-то в середине вывода
|
||
+systemctl status+ вряд ли покажется вам удобным. Чтобы упростить обращение к
|
||
соответствующим страницам документации без использования мыши и
|
||
копирования/вставки, мы ввели новую команду
|
||
\begin{Verbatim}
|
||
systemctl help systemd-logind.service
|
||
\end{Verbatim}
|
||
которая просто откроет в вашем терминале соответствующие man-страницы.
|
||
|
||
URI, ссылающиеся на документацию, формируются в соответствии со страницей
|
||
руководства
|
||
\href{https://www.kernel.org/doc/man-pages/online/pages/man7/url.7.html}{uri(7)}.
|
||
Поддерживаются схемы +http+/+https+ (URL для веб-страниц) и +man+/+info+
|
||
(локальные страницы руководства).
|
||
|
||
Разумеется, добавленная нами возможность не~обеспечивает полной очевидности
|
||
абсолютно всего, хотя бы потому, что пользователь должен как минимум знать про
|
||
команду +systemctl status+ (а также вообще про программу +systemctl+ и про то,
|
||
что такое юнит). Но при наличии этих базовых знаний, пользователю уже
|
||
не~составит труда получить информацию по интересующим его вопросам.
|
||
|
||
Мы надеемся, что добавленный нами механизм для связи работающего кода и
|
||
соответствующей документации станет значительным шагом к полностью прозрачному и
|
||
предельно простому для понимания процессу загрузки.
|
||
|
||
Описанная функциональность частично присутствует в Fedora~17, а в полном
|
||
объеме она будет представлена в Fedora~18.
|
||
|
||
В завершение, стоит отметить, что использованная нами идея не~является такой уж
|
||
новой: в SMF, системе инициализации Solaris, уже используется практика
|
||
указания ссылок на документацию в описании службы. Однако, в Linux такой
|
||
подход является принципиально новым, и systemd сейчас является наиболее
|
||
документированной и прозрачной системой загрузки для данной платформы.
|
||
|
||
Если вы занимаетесь разработкой или сопровождением пакетов и создаете файл
|
||
конфигурации юнита, пожалуйста, включите в него ссылки на документацию. Это
|
||
очень просто: достаточно добавить в секции +[Unit]+ поле +Documentation=+ и
|
||
перечислить в нем соответствующие URI. Подробнее об этом поле см. на странице
|
||
руководства
|
||
\href{http://www.freedesktop.org/software/systemd/man/systemd.unit.html}{systemd.unit(5)}.
|
||
Чем больше будет документированных юнитов, тем проще станет работа системного
|
||
администратора. (В частности, я уже направил в FPC
|
||
\href{https://fedorahosted.org/fpc/ticket/192}{предложение} внести
|
||
соответствующие пожелания в нормативные документы, регулирующие подготовку
|
||
пакетов для Fedora.)
|
||
|
||
Да, кстати: если вас интересует общий обзор процесса загрузки systemd, то вам
|
||
стоит обратить внимание на
|
||
\href{http://www.freedesktop.org/software/systemd/man/bootup.html}{новую
|
||
страницу руководства}, где представлена псевдографическая потоковая диаграмма,
|
||
описывающая процесс загрузки и роль ключевых юнитов.
|
||
|
||
\section{Сторожевые таймеры}
|
||
|
||
Разрабатывая systemd, мы ориентируемся на три основных области его применения:
|
||
мобильные/встраиваемые устройства, настольные системы и промышленные серверы.
|
||
Мобильные и встраиваемые системы, как правило, располагают очень скромными
|
||
ресурсами и вынуждены очень экономно расходовать энергию. Настольные системы
|
||
уже не~так сильно ограничены по мощности, хотя все же проигрывают в
|
||
этом плане промышленным серверам. Но, как ни~странно, существуют
|
||
возможности, востребованные в обоих крайних случаях (встраиваемые системы и
|
||
серверы), но не~очень актуальные в промежуточной ситуации (десктопы). В
|
||
частности, к ним относится поддержка
|
||
\href{http://ru.wikipedia.org/wiki/Watchdog}{сторожевых таймеров} (watchdogs),
|
||
как аппаратных, так и программных.
|
||
|
||
Во встраиваемых системах часто используются аппаратные сторожевые таймеры,
|
||
выполняющие сброс системы, когда программа перестает отвечать (точнее, перестает
|
||
периодически посылать таймеру сигнал <<все в порядке>>). Таким образом,
|
||
обеспечивается повышение надежности системы: что бы ни~случилось, система
|
||
обязательно попытается привести себя в рабочее состояние. На десктопах такая
|
||
функциональность практически не~востребована\footnote{Тем не~менее, сейчас
|
||
аппаратные сторожевые таймеры все чаще появляются и в настольных системах, так
|
||
как стоят они относительно дешево и доступны практически во всех современных
|
||
чипсетах.}. С другой стороны, она весьма актуальна для высокодоступных серверных
|
||
систем.
|
||
|
||
Начиная с версии 183, systemd полностью поддерживает аппаратные сторожевые
|
||
таймеры (доступные через интерфейс +/dev/watchdog+), а также обеспечивает
|
||
программный сторожевой контроль системных служб. В целом эта схема (если она
|
||
задействована) работает так. systemd периодически посылает сигналы аппаратному
|
||
таймеру. В том случае, если systemd или ядро зависают, аппаратный таймер,
|
||
не~получив очередного сигнала, автоматически перезапустит систему. Таким
|
||
образом, ядро и systemd защищены от бесконечного зависания~--- на аппаратном
|
||
уровне. В то же время, сам systemd предоставляет интерфейс, реализующий логику
|
||
программных сторожевых таймеров для отдельных служб. Таким образом можно
|
||
обеспечить, например, принудительный перезапуск службы в случае ее зависания.
|
||
Для каждой службы можно независимо настроить частоту опроса и задать
|
||
соответствующее действие. Соединив оба звена (аппаратный сторожевой таймер,
|
||
контролирующий ядро и systemd, и программные сторожевые таймеры, посредством
|
||
которых systemd контролирует отдельные службы), мы получаем надежную схему
|
||
надзора за всеми системными компонентами.
|
||
|
||
Чтобы задействовать аппаратный таймер, достаточно задать ненулевое значение
|
||
параметра +RuntimeWatchdogSec=+ в файле +/etc/systemd/system.conf+. По умолчанию
|
||
этот параметр равен нулю (т.е. аппаратный таймер не~задействован). Установив его
|
||
равным, например, <<+20s+>>, мы включим аппаратный сторожевой таймер. Если в
|
||
течение 20 секунд таймер не~получит очередного сигнала <<все в порядке>>,
|
||
система автоматически перезагрузится. Отметим, что systemd отправляет такие
|
||
сигналы с периодом, равным половине заданного интервала~--- в нашем случае,
|
||
через каждые 10 секунд. Собственно, это все. Просто задав один параметр,
|
||
вы обеспечите аппаратный контроль работоспособности systemd и
|
||
ядра\footnote{Небольшой совет: если вы занимаетесь отладкой базовых системных
|
||
компонентов~--- не~забудьте отключить сторожевой таймер. Иначе ваша система
|
||
может неожиданно перезагрузиться, когда отладчик остановит процесс init и тот
|
||
не~может вовремя отправить сообщение таймеру.}.
|
||
|
||
Заметим, что с аппаратным сторожевым таймером (+/dev/watchdog+)
|
||
должна работать только одна программа. Этой программой может быть либо systemd,
|
||
либо отдельный демон сторожевого таймера (например,
|
||
\href{http://linux.die.net/man/8/watchdog}{watchdogd})~--- выбор остается за
|
||
вами.
|
||
|
||
Стоит упомянуть здесь еще одну опцию из файла +/etc/systemd/system.conf+~---
|
||
+ShutdownWatchdogSec=+. Она позволяет настроить аппаратный сторожевой таймер для
|
||
контроля процесса перезагрузки. По умолчанию она равна <<+10min+>>. Если в
|
||
процессе остановки системы перед перезагрузкой она зависнет, аппаратный таймер
|
||
принудительно перезагрузит ее по истечении данного интервала.
|
||
|
||
Это все, что я хотел сказать об аппаратных таймерах. Двух вышеописанных опций
|
||
должно быть вполне достаточно для полноценного использования их возможностей.
|
||
А сейчас мы рассмотрим логику программных сторожевых таймеров, обеспечивающих
|
||
контроль работоспособности отдельных служб.
|
||
|
||
Прежде всего отметим, что для полноценной поддержки сторожевого контроля,
|
||
программа должна содержать специальный код, периодически отправляющий таймеру
|
||
сигналы <<все в порядке>>. Добавить поддержку такой функциональности довольно
|
||
просто. Для начала, демон должен проверить переменную окружения +WATCHDOG_USEC=+.
|
||
Если она определена, то ее значение задает контрольный интервал в микросекундах,
|
||
сформатированный в виде текстовой (ASCII) строки. В этом случае демон должен
|
||
регулярно выполнять вызов
|
||
\hreftt{http://www.freedesktop.org/software/systemd/man/sd_notify.html}{sd\_notify}+("WATCHDOG=1")+
|
||
с периодом, равным половине указанного интервала. Таким образом, поддержка
|
||
программного сторожевого контроля со стороны демона сводится к проверке значения
|
||
переменной окружения, и выполнении определенных действий в соответствии с этим
|
||
значением.
|
||
|
||
Если интересующая вас служба обеспечивает поддержку такой
|
||
функциональности, вы можете включить для нее сторожевой контроль, задав опцию
|
||
+WatchdogSec=+ в ее юнит-файле. Эта опция задает период работы таймера
|
||
(подробнее см.
|
||
\href{http://www.freedesktop.org/software/systemd/man/systemd.service.html}{systemd.service(5)}).
|
||
Если вы зададите ее, то systemd при запуске службы передаст ей соответствующее
|
||
значение +WATCHDOG_USEC=+ и, если служба перестанет своевременно отправлять
|
||
сигналы <<все в порядке>>, присвоит ей статус сбойной (failure state).
|
||
|
||
Очевидно, что одного только присвоения статуса недостаточно для обеспечения
|
||
надежной работы системы. Поэтому нам также пригодятся настройки, определяющие,
|
||
нужно ли перезапускать зависшую службу, количество попыток перезапуска, и
|
||
дальнейшие действия, если она все равно продолжает сбоить. Чтобы включить
|
||
автоматический перезапуск службы при сбое, нужно задать опцию
|
||
+Restart=on-failure+ в ее юнит-файле. Чтобы настроить, сколько раз systemd будет
|
||
пытаться перезапустить службу, воспользуйтесь настройками +StartLimitBurst=+
|
||
+StartLimitInterval=+ (первая из них определяет предельное количество попыток,
|
||
вторая~--- интервал времени, в течение которого они подсчитываются). В том
|
||
случае, если достигнут предел количества попыток за указанное время, выполняется
|
||
действие, заданное параметром +StartLimitAction=+. По умолчанию он установлен в
|
||
+none+ (никаких дополнительных действий не~будет, службу просто оставят в покое со
|
||
статусом сбойной). В качестве альтернативы можно указать одно из трех
|
||
специальных действий: +reboot+, +reboot-force+ и +reboot-immediate+.
|
||
+reboot+ соответствует обычной перезагрузке системы, с выполнением всех
|
||
сопутствующих процедур (корректное завершение всех служб, отмонтирование
|
||
файловых систем и т.д.). +reboot-force+ действует более грубо~--- даже
|
||
не~пытаясь корректно остановить службы, оно просто убивает все их процессы,
|
||
отмонтирует файловые системы и выполняет принудительную перезагрузку (в
|
||
результате, перезагрузка происходит быстрее, чем обычно, но файловые системы
|
||
остаются неповрежденными). И наконец, +reboot-immediate+ даже не~пытается отдать
|
||
дань вежливости (убить процессы и отмонтировать файловые системы)~--- оно
|
||
немедленно выполняет жесткую перезагрузку системы (это поведение практически
|
||
аналогично срабатыванию аппаратного сторожевого таймера). Все перечисленный
|
||
настройки подробно описаны на странице руководства
|
||
\href{http://www.freedesktop.org/software/systemd/man/systemd.service.html}{systemd.service(5)})%
|
||
\footnote{Прим. перев.: Автор упускает из виду одну полезную опцию,
|
||
непосредственно относящуюся к обсуждаемому вопросу~--- +OnFailure=+, задающую
|
||
юнит, который будет активирован в случае сбоя исходного юнита. Таким образом
|
||
можно обеспечить, например, запуск скриптов, отправляющих администратору
|
||
уведомление о сбое в сочетании с дополнительной информацией (для сбора которой
|
||
целесообразно задействовать команды +systemctl status+ и +journalctl+). Кроме
|
||
того, комбинирование данной настройки с опцией +OnFailureIsolate=+ позволяет
|
||
при сбое юнита перевести систему в определенное состояние (например, остановить
|
||
некоторые службы, перейти в режим восстановления, выполнить перезагрузку).}.
|
||
|
||
Таким образом, мы мы получаем гибкий механизм для настройки сторожевого контроля
|
||
служб, их перезапуска при зависании, и реагирования в ситуации, когда перезапуск
|
||
не~помогает.
|
||
|
||
Рассмотрим применение этих настроек на простом примере:
|
||
\begin{Verbatim}
|
||
[Unit]
|
||
Description=My Little Daemon
|
||
Documentation=man:mylittled(8)
|
||
|
||
[Service]
|
||
ExecStart=/usr/bin/mylittled
|
||
WatchdogSec=30s
|
||
Restart=on-failure
|
||
StartLimitInterval=5min
|
||
StartLimitBurst=4
|
||
StartLimitAction=reboot-force
|
||
\end{Verbatim}
|
||
Данная служба будет автоматически перезапущена, если она не~передаст системному
|
||
менеджеру очередной сигнал <<все в порядке>> в течение 30 секунд после
|
||
предыдущего (кроме того, перезапуск будет произведен и в случае любого другого
|
||
сбоя, например, завершения основного процесса службы с ненулевым кодом выхода).
|
||
Если потребуется более 4 перезапусков службы за 5 минут~--- будет предпринято
|
||
специальное действие, в данном случае, быстрая перезагрузка системы с корректным
|
||
отмонтированием файловых систем.
|
||
|
||
Это все, что я хотел рассказать о сторожевых таймерах в systemd. Мы надеемся,
|
||
что поддержки аппаратного отслеживания работоспособности процесса init, в
|
||
сочетании с контролем функционирования отдельных служб, должно быть достаточно
|
||
для решения большинства задач, связанных со сторожевыми таймерами.
|
||
Разрабатываете ли вы встраиваемую либо мобильную систему, или работаете с
|
||
высокодоступными серверами~--- вам определенно стоит попробовать наши решения!
|
||
|
||
(И да, если у вас возникнет вопрос, почему с аппаратным таймером должен работать
|
||
именно init, и что мешает вынести эту логику в отдельный демон~--- пожалуйста,
|
||
перечитайте эту главу еще раз, и попытайтесь увидеть всю цепочку сторожевого
|
||
контроля как единое целое: аппаратный таймер надзирает за работой systemd, а
|
||
тот, в свою очередь, следит за отдельными службами. Кроме того, мы полагаем, что
|
||
отсутствие своевременного ответа от службы нужно рассматривать и обрабатывать
|
||
так же, как и любые другие ее сбои. И наконец, взаимодействие с
|
||
+/dev/watchdog+~--- одна из самых тривиальных задач в работе ОС (обычно, она
|
||
сводится к простому вызову +ioctl()+), и для ее решения достаточно нескольких
|
||
строк кода. С другой стороны, вынос данной функции в отдельный демон потребует
|
||
организации сложного межпроцессного взаимодействия между этим демоном и
|
||
процессом init~--- очевидно, реализация такой схемы предоставит значительно
|
||
б\'{о}льший простор для ошибок, не~говоря уже о повышенном потреблении
|
||
ресурсов.)
|
||
|
||
Отметим, что встроенная в systemd поддержка аппаратного сторожевого таймера
|
||
в~конфигурации по умолчанию отключена, и поэтому никак не~мешает работе с этим
|
||
таймером из других программ. Вы без лишних проблем можете выбрать внешний
|
||
сторожевой демон, если он лучше подходит для вашей задачи.
|
||
|
||
Да, и еще: если у вас возникнет вопрос, имеется ли в вашей системе аппаратный
|
||
таймер~--- скорее всего да, если ваш компьютер не~очень старый. Чтобы получить
|
||
точный ответ, вы можете воспользоваться утилитой
|
||
\hreftt{http://karelzak.blogspot.de/2012/05/eject1-sulogin1-wdctl1.html}{wdctl},
|
||
включенной в последний релиз util-linux\footnote{Прим. перев.: Утилита +wdctl+
|
||
присутствует в util-linux, начиная с версии~2.22, которая, на~момент написания
|
||
этих строк, еще не~вышла.}. Эта программа выведет всю необходимую
|
||
информацию о вашем аппаратном сторожевом таймере.
|
||
|
||
И в завершение, я хотел бы поблагодарить ребят из
|
||
\href{http://www.pengutronix.de/}{Pengutronix}, которым приндалежит основная
|
||
заслуга реализации сторожевого контроля в systemd.
|
||
|
||
\section{Запуск getty на последовательных (и не~только) консолях}
|
||
\label{sec:getty}
|
||
|
||
\emph{Если вам лень читать всю статью целиком: для запуска getty на
|
||
последовательной консоли\footnote{Прим. перев.: Здесь и в дальнейшем автор
|
||
использует термин <<serial console>>. Точный перевод этого выражения на русский
|
||
язык звучит как <<консоль, подключаемая к последовательному порту>>. Однако,
|
||
для краткости изложения, при переводе используется не~вполне корректный, но
|
||
хорошо знакомый администраторам жаргонизм <<последовательная консоль>>. Также
|
||
отметим, что в данном документе термины <<консоль>> и <<терминал>> используются
|
||
как синонимы.} достаточно указать в загрузчике параметр ядра
|
||
\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}, мы постарались
|
||
сделать работу с ними еще проще. В этой статье я хочу рассказать о том, как в
|
||
systemd реализован запуск \href{https://ru.wikipedia.org/wiki/Getty}{getty} на
|
||
терминалах различных типов.
|
||
|
||
Для начала, хотелось бы отметить один важный момент: в большинстве случаев, чтобы
|
||
получить приглашение к логину на последовательном терминале, вам не~нужно
|
||
совершать никаких дополнительных действий: systemd сам проверит настройки ядра,
|
||
определит их них используемую ядром консоль, и автоматически запустит на ней
|
||
getty. Таким образом, вам достаточно лишь правильно указать ядру соответствующую
|
||
консоль (например, добавив к параметрам ядра в загрузчик +console=ttyS0+).
|
||
|
||
Тем не~менее, для общего образования все же стоит рассмотреть некоторые
|
||
тонкости запуска 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}%
|
||
{виртуальные консоли} (virtual terminals, VT, известные в
|
||
системе под именами +/dev/tty1+, +/dev/tty2+ и т.д.)~--- те,
|
||
которые вы можете увидеть безо всякого дополнительного
|
||
оборудования, просто переключившись на них из графического
|
||
сеанса.
|
||
|
||
\item +serial-getty@.service+ обеспечивает поддержку всех прочих
|
||
разновидностей терминалов, в том числе, подключаемых к
|
||
последовательным портам (+/dev/ttyS0+ и т.д.). Этот шаблон имеет
|
||
ряд отличий от +getty@.service+, в частности, переменная \verb+$TERM+
|
||
в нем устанавливается в значение +vt102+ (должно хорошо работать
|
||
на большинстве физических терминалов), а не~+linux+ (которое
|
||
работает правильно только на виртуальных консолях), а также
|
||
пропущены настройки, касающиеся очистки буфера прокрутки (и
|
||
поэтому имеющие смысл только на VT).
|
||
\end{itemize}
|
||
|
||
\subsection{Виртуальные консоли}
|
||
|
||
Рассмотрим механизм запуска +getty@.service+, обеспечивающий появление
|
||
приглашений логина на виртуальных консолях (последовательны терминалы пока
|
||
оставим в покое). По устоявшейся традиции, init-системы Linux обычно
|
||
настраивались на запуск фиксированного числа экземпляров getty, как правило,
|
||
шести (на первых шести виртуальных консолях, с +tty1+ по +tty6+).
|
||
|
||
В systemd мы сделали этот процесс более динамичным: чтобы добиться большей
|
||
скорости и эффективности, мы запускаем дополнительные экземпляры getty только
|
||
при необходимости. Например, +getty@tty2.service+ стартует только после того,
|
||
как вы переключитесь на вторую виртуальную консоль. Отказавшись от
|
||
обязательного запуска нескольких экземпляров getty, мы сэкономили немного
|
||
системных ресурсов, а также сделали загрузку системы чуть-чуть быстрее. При
|
||
этом, с точки зрения пользователя, все осталось так же просто: как только он
|
||
переключается на виртуальную консоль, на ней запускается getty, которая выводит
|
||
приглашение к логину. Пользователь может и не~подозревать о том, что до момента
|
||
переключения приглашения не~было. Тем не~менее, если он войдет в систему и
|
||
выполнит команду +ps+, он увидит, что getty запущены только на тех консолях, на
|
||
которых он уже побывал.
|
||
|
||
По умолчанию, автоматический запуск getty производится на виртуальных консолях с
|
||
первой по шестую (чтобы свести к минимуму отличия от привычной
|
||
конфигурации)\footnote{Тем не~менее, это поведение можно легко изменить,
|
||
задавая параметр +NAutoVTs=+ в файле
|
||
\href{http://www.freedesktop.org/software/systemd/man/logind.conf.html}{logind.conf}.}.
|
||
Отметим, что автоматический запуск getty на конкретной консоли производится
|
||
только при условии, что эта консоль не~занята другой программой. В частности,
|
||
при интенсивном использовании механизма
|
||
\href{https://en.wikipedia.org/wiki/Fast_user_switching}{быстрого переключения
|
||
пользователей} графические сеансы могут занять первые несколько консолей (чтобы
|
||
такое поведение не~заблокировало возможность запуска getty, мы предусмотрели
|
||
специальную защиту, см. чуть ниже).
|
||
|
||
Две консоли играют особую роль: +tty1+ и +tty6+. +tty1+, при загрузке в
|
||
графическом режиме, используется для запуска дисплейного менеджера, а при
|
||
загрузке в многопользовательском текстовом режиме, systemd принудительно
|
||
запускает на ней экземпляр getty, не~дожидаясь переключений\footnote{В данном
|
||
случае нет принципиальной разницы между принудительным запуском и запуском по
|
||
запросу: первая консоль используется по умолчанию, так что запрос на ее
|
||
активацию обязательно поступит.}.
|
||
|
||
Что касается +tty6+, то она используется исключительно для автоматического
|
||
запуска getty, и недоступна другим подсистемам, в частности, графическому
|
||
серверу\footnote{При необходимости, вы можете легко поменять номер резервируемой
|
||
консоли (или отключить резервирование), используя параметр +ReserveVT=+ в файле
|
||
\href{http://www.freedesktop.org/software/systemd/man/logind.conf.html}{logind.conf}.}.
|
||
Мы сделали так специально, чтобы гарантировать возможность входа в систему в
|
||
текстовом режиме, даже если графический сервер займет более пяти консолей.
|
||
|
||
\subsection{Последовательные консоли}
|
||
|
||
Работа с последовательными консолями (и всеми остальными видами не-виртуальных
|
||
консолей) реализована несколько иначе, чем с VT. По умолчанию, systemd запускает
|
||
один экземпляр +serial-getty@.service+ на основной консоли ядра\footnote{Если
|
||
для ядра настроен вывод в несколько консолей, \emph{основной} считается та, которая
|
||
идет \emph{первой} в +/sys/class/tty/console/active+, т.е. указана
|
||
\emph{последней} в строке параметров ядра.} (если она не~является виртуальной).
|
||
Консолью ядра~--- это та консоль, на которую выводятся сообщения ядра. Обычно
|
||
она настраивается в загрузчике, путем добавления к параметрам ядра аргумента
|
||
наподобие +console=ttyS0+\footnote{Подробнее об этой опции см. в файле
|
||
\href{https://www.kernel.org/doc/Documentation/kernel-parameters.txt}{kernel-parameters.txt}.}.
|
||
Таким образом, если пользователь перенаправил вывод ядра на последовательную
|
||
консоль, то по завершении загрузки он увидит на этой консоли приглашение для
|
||
логина\footnote{Отметим, что getty, а точнее, +agetty+ на такой консоли
|
||
вызывается с параметром +-s+, и поэтому не~изменяет настроек символьной
|
||
скорости (baud rate)~--- сохраняется то значение, которое было указано в строке
|
||
параметров ядра.}. Кроме того, systemd выполняет поиск консолей, предоставляемых
|
||
системами виртуализации, и запускает +serial-getty@.service+ на первой из этих
|
||
консолей (+/dev/hvc0+, +/dev/xvc0+ или +/dev/hvsi0+). Такое поведение
|
||
реализовано специальной
|
||
\href{http://www.freedesktop.org/wiki/Software/systemd/Generators}{программой-генератором}~---
|
||
\href{http://www.freedesktop.org/software/systemd/man/systemd-getty-generator.html}{systemd-getty-generator}.
|
||
Генераторы запускаются в самом начале загрузки и автоматически настраивают
|
||
различные службы в зависимости от соответствующих факторов.
|
||
|
||
В большинстве случаев, вышеописанного механизма автоматической настройки должно
|
||
быть достаточно, чтобы получить приглашение логина там, где нужно~--- без
|
||
каких-либо дополнительных настроек systemd. Тем не~менее, иногда возникает
|
||
необходимость в ручной настройке~--- например, когда необходимо запустить getty
|
||
сразу на нескольких последовательных консолях, или когда вывод сообщений ядра
|
||
направляется на один терминал, а управление производится с другого. Для решения
|
||
таких задач достаточно определить по экземпляру +serial-getty@.service+ для
|
||
каждого последовательного порта, на котором вы хотите запустить
|
||
getty\footnote{Отметим, что +systemctl enable+ \emph{для экземпляров служб}
|
||
работает только начиная с systemd версии 188 и старше (например, в Fedora 18). В
|
||
более ранних версиях придется напрямую манипулировать символьными ссылками:
|
||
\texttt{ln -s /usr/lib/systemd/system/serial-getty@.service
|
||
/etc/systemd/system/getty.target.wants/serial-getty@ttyS2.service ; systemctl
|
||
daemon-reload}.}\footnote{\label{ftn:enableserial}Прим. перев.: На самом деле,
|
||
работать с символьными ссылками пришлось бы даже в более свежих версиях
|
||
systemd (до 209 включительно), так как в файле +serial-getty@.service+
|
||
отсутствовала секция +[Install]+, в результате чего попытка выполнения
|
||
+systemctl enable+ для экземпляра соответствующей службы приводила к
|
||
закономерной ошибке. Данная проблема была устранена только в systemd 210.
|
||
В~случае использования более старых версий, исправить ее можно и самостоятельно:
|
||
достаточно скопировать указанный файл из +/usr/lib/systemd/system/+ в
|
||
+/etc/systemd/system/+, после чего дописать в него секцию +[Install]+,
|
||
содержащую параметр +WantedBy=getty.target+, и затем выполнить
|
||
+systemctl daemon-reload+.}:
|
||
\begin{Verbatim}
|
||
# systemctl enable serial-getty@ttyS2.service
|
||
# systemctl start serial-getty@ttyS2.service
|
||
\end{Verbatim}
|
||
После выполнения этих команд, getty будет принудительно запускаться для
|
||
указанных последовательных портов при всех последующих загрузках.
|
||
|
||
В некоторых ситуациях может возникнуть необходимость в тонкой настройке
|
||
параметров 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
|
||
... редактируем параметры запуска agetty ...
|
||
# ln -s /etc/systemd/system/serial-getty@ttyS2.service /etc/systemd/system/getty.target.wants/
|
||
# systemctl daemon-reload
|
||
# systemctl start serial-getty@ttyS2.service
|
||
\end{Verbatim}
|
||
В приведенном примере создает файл настроек, определяющий запуск getty на порту
|
||
+ttyS2+ (это определяется именем, под которым мы скопировали файл~---
|
||
+serial-getty@ttyS2.service+). Все изменения настроек, сделанные в данном файле,
|
||
будут распространяться только на этот порт.
|
||
|
||
Собственно, это все, что я хотел рассказать о последовательных портах,
|
||
виртуальных консолях и запуске getty на них. Надеюсь, рассказ получился
|
||
интересным.
|
||
|
||
\section{Работа с Journal}
|
||
|
||
В свое время, я уже рассказывал о некоторых возможностях journal
|
||
(см.~главу~\ref{sec:journal}), доступных из утилиты +systemctl+. Сейчас я
|
||
попробую рассказать о journal более подробно, с упором на практическое
|
||
применение его возможностей.
|
||
|
||
Если вы еще не~в курсе, что такое journal: это компонент
|
||
\href{http://www.freedesktop.org/wiki/Software/systemd}{systemd}, регистрирующий
|
||
сообщения из системного журнала (syslog), сообщения ядра (kernel log) и initrd,
|
||
а также сообщения, которые процессы служб выводят на STDOUT и STDERR. Полученная
|
||
информация индексируется и предоставляется пользователю по запросу. Journal
|
||
может работать одновременно с традиционными демоном syslog (например, rsyslog
|
||
или syslog-ng), либо полностью его заменять. За подробностями стоит
|
||
обратиться к \href{http://0pointer.de/blog/projects/the-journal.html}{первому
|
||
анонсу}.
|
||
|
||
Journal был включен в Fedora начиная с F17. В Fedora~18 journal вырос в мощный и
|
||
удобный механизм работы с системным журналом. Однако, и в~F17, и в~F18 journal
|
||
по умолчанию сохраняет информацию только в небольшой кольцевой буфер в каталоге
|
||
+/run/log/journal+. Как и все содержимое каталога +/run+, эта информация
|
||
теряется при перезагрузке\footnote{Прим. перев.: Разумеется, это никак
|
||
не~относится к традиционному демону системного лога, даже если он работает
|
||
поверх journal.}. Такой подход сильно ограничивает использование
|
||
полезных возможностей journal, однако вполне достаточен для вывода актуальных
|
||
сообщений от служб в +systemctl status+. Начиная с Fedora~19, мы собираемся
|
||
включить сохранение логов на диск, в каталог +/var/log/journal+. При этом,
|
||
логи смогут занимать гораздо больше места\footnote{Прим. перев.: В journal
|
||
отдельно задаются ограничения на размер для логов во временном хранилище
|
||
(+/run+) и в постоянном (+/var+). При превышении лимита старые журналы
|
||
удаляются. Так как +/run+ размещается на tmpfs, т.е. в
|
||
оперативной памяти, для временного хранения по умолчанию установлены более
|
||
жесткие ограничения. При необходимости, соответствующие настройки можно задать
|
||
в файле
|
||
\href{http://www.freedesktop.org/software/systemd/man/journald.conf.html}{journald.conf}.},
|
||
а значит, смогут вместить больше полезной информации. Таким образом, journal
|
||
станет еще более удобным.
|
||
|
||
\subsection{Сохранение логов на диск}
|
||
|
||
В F17 и~F18 вы можете включить сохранение логов на диск вручную:
|
||
\begin{Verbatim}
|
||
# mkdir -p /var/log/journal
|
||
\end{Verbatim}
|
||
После этого рекомендуется перезагрузить систему, чтобы заполнить журнал новыми
|
||
записями.
|
||
|
||
Так как теперь у вас есть journal, syslog вам больше не~нужен (кроме ситуаций,
|
||
когда вам совершенно необходимо иметь +/var/log/messages+ в текстовом виде), и
|
||
вы спокойно можете удалить его:
|
||
\begin{Verbatim}
|
||
# yum remove rsyslog
|
||
\end{Verbatim}
|
||
|
||
\subsection{Основы}
|
||
|
||
Итак, приступим. Нижеприведенный текст демонстрирует возможности systemd~195,
|
||
входящего в Fedora~18\footnote{Обновление со 195-й версией systemd на момент
|
||
написания этих строк находится
|
||
\href{https://admin.fedoraproject.org/updates/FEDORA-2012-16709/systemd-195-1.fc18}{на
|
||
тестировании} и вскоре будет включено в состав Fedora~18.}, так что, если
|
||
некоторые из описанных трюков не~сработают в F17~--- пожалуйста, дождитесь F18.
|
||
|
||
Начнем с основ. Доступ к логам journal осуществляется через утилиту
|
||
\href{http://www.freedesktop.org/software/systemd/man/journalctl.html}{journalctl(1)}.
|
||
Чтобы просто взглянуть на лог, достаточно ввести
|
||
\begin{Verbatim}
|
||
# journalctl
|
||
\end{Verbatim}
|
||
Если вы выполнили эту команду с полномочиями root, вы увидите все
|
||
журнальные сообщения, включая исходящие как от системных компонентов, так и от
|
||
залогиненных пользователей\footnote{Прим. перев.: А если вы выполнили эту
|
||
команду от имени непривилегированного пользователя, не~входящего в группу
|
||
+adm+, и при этом не~включили сохранение логов на диск, то вы не~увидите
|
||
ничего~--- без специальных полномочий пользователь может просматривать только
|
||
собственный лог, а он по умолчанию ведется только если логи записываются на
|
||
диск.}. Вывод этой команды форматируется в стиле
|
||
+/var/log/messages+, но при этом добавлены кое-какие улучшения:
|
||
\begin{itemize}
|
||
\item Строки с приоритетом error и выше подсвечены красным.
|
||
\item Строки с приоритетом notice и warning выделены жирным шрифтом.
|
||
\item Все отметки времени сформированы с учетом вашего часового пояса.
|
||
\item Для навигации по тексту используется просмотрщик (pager), по
|
||
умолчанию +less+\footnote{Прим. перев.: В инструментах systemd,
|
||
включая +journalctl+, просмотрщик включается только при прямом
|
||
выводе на экран, и отключается при перенаправлении вывода в файл
|
||
или передаче его по каналу (shell pipe).}.
|
||
\item Выводятся \emph{все} доступные данные, включая информацию из
|
||
файлов, прошедших ротацию (rotated logs).
|
||
\item Загрузка системы отмечается специальной строкой, отделяющей
|
||
записи, сгенерированные между (пере)загрузками.
|
||
\end{itemize}
|
||
|
||
Отметим, что в данной статье не~приводятся примеры такого вывода~--- прежде
|
||
всего, для краткости изложения, но также и для того, чтобы дать вам повод
|
||
поскорее попробовать Fedora~18 с systemd~195. Надеюсь, отсутствие таких примеров
|
||
не~помешает вам уловить суть.
|
||
|
||
\subsection{Контроль доступа}
|
||
|
||
Итак, мы получили удобный и эффективный метод просмотра логов. Но для полного
|
||
доступа к системным сообщениям требуются привилегии root, что не~всегда
|
||
удобно~--- в наше время администраторы предпочитают работать от имени
|
||
непривилегированного пользователя, переключаясь на root только при крайней
|
||
необходимости. По умолчанию, непривилегированные пользователи могут
|
||
просматривать в journal только свои собственные логи (сообщения, сгенерированные
|
||
их процессами). Чтобы предоставить пользователю доступ ко всем системным логам,
|
||
нужно включить его в группу +adm+:
|
||
\begin{Verbatim}
|
||
# usermod -a -G adm lennart
|
||
\end{Verbatim}
|
||
Разлогинившись, а затем вновь залогинившись под именем +lennart+\footnote{Прим.
|
||
перев.: Для того, чтобы обновить групповые полномочия в уже запущенных сеансах,
|
||
можно воспользоваться командой
|
||
\href{http://linux.die.net/man/1/newgrp}{newgrp(1)}: +newgrp adm+.}, я могу
|
||
просматривать сообщения от всех пользователей и системных
|
||
компонентов\footnote{Прим. перев.: Группа +adm+ была выбрана на основании опыта
|
||
дистрибутива Debian, в котором она устанавливается в качестве группы-владельца
|
||
большинства лог-файлов. При этом авторы четко разделяют полномочия групп +adm+ и
|
||
+wheel+: если последняя используется для предоставления прав \emph{изменять}
|
||
что-либо в системе, то первая дает возможность лишь \emph{просматривать}
|
||
системную информацию. Начиная с версии systemd 198, группа-владелец файлов
|
||
журнала изменена с +adm+ на +systemd-journal+, однако штатный алгоритм установки
|
||
systemd все равно выдает права на их чтение группам +adm+ и +wheel+ (через
|
||
ACL).}:
|
||
\begin{Verbatim}
|
||
$ journalctl
|
||
\end{Verbatim}
|
||
|
||
\subsection{Отслеживание логов в реальном времени}
|
||
|
||
Когда вы запускаете программу +journalctl+ без параметров, она выводит все
|
||
сообщения, сгенерированные на текущий момент, и возвращает управление оболочке.
|
||
Однако, иногда бывает полезно отслеживать их появление в режиме реального
|
||
времени. В классической реализации syslog это осуществлялось командой
|
||
+tail -f /var/log/messages+. В journal ее аналог выглядит так:
|
||
\begin{Verbatim}
|
||
$ journalctl -f
|
||
\end{Verbatim}
|
||
И работает он точно так же: выводит последние десять сообщений, после чего
|
||
переходит в режим ожидания, и выводит новые сообщения по мере их появления.
|
||
|
||
\subsection{Простейшие методы выборки записей}
|
||
|
||
При вызове +journalctl+ без параметров, она выводит все сообщения, начиная с
|
||
самого первого из сохраненных. Разумеется, это огромный объем информации. На
|
||
практике иногда бывает достаточно ограничиться сообщениями, сгенерированными с
|
||
момента последней загрузки системы\footnote{Прим. перев.: Начиная с systemd
|
||
версии 206, синтаксис опции <<+-b+>> был расширен: теперь она позволяет
|
||
просматривать логи не~только за текущую, но и за предыдущие загрузки, например,
|
||
<<+-b -1+>>~--- прошлая загрузка, <<+-b -2+>>~--- позапрошлая и т.д.}:
|
||
\begin{Verbatim}
|
||
$ journalctl -b
|
||
\end{Verbatim}
|
||
Но часто даже после такой фильтрации записей остается довольно много. Что ж, мы
|
||
можем ограничиться только наиболее важными. Итак, все сообщения с приоритетом
|
||
error и выше, начиная с момента последней загрузки:
|
||
\begin{Verbatim}
|
||
$ journalctl -b -p err
|
||
\end{Verbatim}
|
||
|
||
Если вы уже успели перезагрузить систему после того, как произошли интересующие
|
||
вас события, целесообразнее будет воспользоваться выборкой по времени:
|
||
\begin{Verbatim}
|
||
$ journalctl --since=yesterday
|
||
\end{Verbatim}
|
||
В результате мы увидим все сообщения, зарегистрированные начиная со вчерашнего
|
||
дня вплоть до настоящего момента. Прекрасно! Разумеется, этот критерий отбора можно
|
||
комбинировать с другими, например, с +-p err+. Но, допустим, нам нужно узнать о
|
||
чем-то, что случилось либо 15-го октября, либо 16-го:
|
||
\begin{Verbatim}
|
||
$ journalctl --since=2012-10-15 --until="2011-10-16 23:59:59"
|
||
\end{Verbatim}
|
||
Отлично, мы нашли то, что искали. Но вот вам сообщают, что сегодня ранним утром
|
||
наблюдались проблемы с CGI-скриптами Apache. Ладно, послушаем, что нам скажет
|
||
индеец:
|
||
\begin{Verbatim}
|
||
$ journalctl -u httpd --since=00:00 --until=9:30
|
||
\end{Verbatim}
|
||
Да, мы нашли это. Хм, похоже, что причиной стала проблема с диском +/dev/sdc+.
|
||
Посмотрим, что с ним случилось:
|
||
\begin{Verbatim}
|
||
$ journalctl /dev/sdc
|
||
\end{Verbatim}
|
||
Кошмар, ошибка ввода-вывода\footnote{Ну ладно, признаюсь, здесь я немножко
|
||
считерил. Индексирование сообщений ядра по блочным устройствам пока что
|
||
не~принято в апстрим, но Ганс
|
||
\href{http://www.spinics.net/lists/linux-scsi/msg62499.html}{проделал огромную
|
||
работу}, чтобы реализовать эту функциональность, и я надеюсь, что к релизу F18
|
||
все будет.}! Нужно срочно заменить диск, пока не~начались более серьезные
|
||
проблемы. Ладно, пойдем дальше. Что у нас там случилось с процессом vpnc?
|
||
\begin{Verbatim}
|
||
$ journalctl /usr/sbin/vpnc
|
||
\end{Verbatim}
|
||
Хм, ничего подозрительного. Но, кажется, проблема где-то во взаимодействии между
|
||
+vpnc+ и +dhclient+. Посмотрим объединенный и отсортированный по времени список
|
||
сообщений от этих процессов:
|
||
\begin{Verbatim}
|
||
$ journalctl /usr/sbin/vpnc /usr/sbin/dhclient
|
||
\end{Verbatim}
|
||
Отлично, мы нашли причину проблемы!
|
||
|
||
\subsection{Продвинутые методы выборки}
|
||
|
||
Да, это все, конечно, здорово, но попробуем подняться еще на ступеньку выше.
|
||
Чтобы понять описанные ниже приемы, нужно знать, что systemd добавляет к
|
||
каждой лог-записи ряд скрытых метаданных. Эти метаданные по структуре напоминают
|
||
набор переменных окружения, хотя на самом деле дают даже больше возможностей:
|
||
во-первых, метаданные могут включать большие бинарные блоки данных (впрочем, это
|
||
скорее исключение~--- обычно они содержат текст в кодировке UTF-8), и во-вторых,
|
||
в пределах одной записи поле метаданных может содержать сразу несколько
|
||
значений (и это тоже встречается нечасто~--- обычно поля содержат по одному
|
||
значению). Эти метаданные автоматически собираются и добавляются для каждой
|
||
лог-записи, безо всякого участия пользователя. И вы легко можете их использовать
|
||
для более тонкой выборки записей. Посмотрим, как они выглядят:
|
||
\begin{Verbatim}
|
||
$ journalctl -o verbose -n1
|
||
Tue, 2012-10-23 23:51:38 CEST [s=ac9e9c423355411d87bf0ba1a9b424e8;i=4301;b=5335e9cf5d954633bb99aefc0ec38c25;m=882ee28d2;t=4ccc0f98326e6;x=f21e8b1b0994d7ee]
|
||
PRIORITY=6
|
||
SYSLOG_FACILITY=3
|
||
_MACHINE_ID=a91663387a90b89f185d4e860000001a
|
||
_HOSTNAME=epsilon
|
||
_TRANSPORT=syslog
|
||
SYSLOG_IDENTIFIER=avahi-daemon
|
||
_COMM=avahi-daemon
|
||
_EXE=/usr/sbin/avahi-daemon
|
||
_SYSTEMD_CGROUP=/system/avahi-daemon.service
|
||
_SYSTEMD_UNIT=avahi-daemon.service
|
||
_SELINUX_CONTEXT=system_u:system_r:avahi_t:s0
|
||
_UID=70
|
||
_GID=70
|
||
_CMDLINE=avahi-daemon: registering [epsilon.local]
|
||
MESSAGE=Joining mDNS multicast group on interface wlan0.IPv4 with address 172.31.0.53.
|
||
_BOOT_ID=5335e9cf5d954633bb99aefc0ec38c25
|
||
_PID=27937
|
||
SYSLOG_PID=27937
|
||
_SOURCE_REALTIME_TIMESTAMP=1351029098747042
|
||
\end{Verbatim}
|
||
(Чтобы не~утомлять вас огромным количеством текста, ограничимся одной записью.
|
||
Ключ +-n+ позволяет задать число выводимых записей, в нашем случае 1. Если
|
||
указать его без параметра, будут показаны 10 последних записей.)
|
||
|
||
Задав параметр +-o verbose+, мы переключили формат вывода~--- теперь, вместо
|
||
скупых строчек, копирующих +/var/log/messages+, для каждой записи выводится
|
||
полный перечень всех метаданных. В том числе, информация о пользователе и
|
||
группе, контекст SELinux, идентификатор компьютера и т.д. Полный список
|
||
общеизвестных полей метаданных приведен на соответствующей
|
||
\href{http://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html}%
|
||
{странице руководства}.
|
||
|
||
База данных Journal индексируется по \emph{всем} этим полям! И мы можем
|
||
использовать любое из них в качестве критерия выборки:
|
||
\begin{Verbatim}
|
||
$ journalctl _UID=70
|
||
\end{Verbatim}
|
||
Как нетрудно догадаться, в результате будут выведены все сообщения от
|
||
процессов пользователя с UID 70. При необходимости, критерии можно
|
||
комбинировать:
|
||
\begin{Verbatim}
|
||
$ journalctl _UID=70 _UID=71
|
||
\end{Verbatim}
|
||
Указание нескольких значений для одного и того же поля эквивалентно логическому
|
||
ИЛИ. Таким образом, будут выведены записи как от процессов с UID 70, так и от
|
||
процессов с UID 71.
|
||
\begin{Verbatim}
|
||
$ journalctl _HOSTNAME=epsilon _COMM=avahi-daemon
|
||
\end{Verbatim}
|
||
А вот указание нескольких \emph{различных} полей дает эффект логического И. В
|
||
результате, будут выведены записи только от процесса +avahi-daemon+, работающего
|
||
на хосте с именем +epsilon+.
|
||
|
||
Но мы этим не~ограничимся! Мы же суровые компьютерщики, нам нужны сложные
|
||
логические выражения!
|
||
\begin{Verbatim}
|
||
$ journalctl _HOSTNAME=theta _UID=70 + _HOSTNAME=epsilon _COMM=avahi-daemon
|
||
\end{Verbatim}
|
||
При помощи плюса мы можем явно задать логическое ИЛИ, применяя его к разным
|
||
полям и даже И-выражениям. Поэтому наш пример выведет как записи с хоста
|
||
+theta+ от процессов с UID 70, так и с хоста +epsilon+ от процесса
|
||
+avahi-daemon+\footnote{Прим. перев.: Стоит отметить, что приоритет логических
|
||
операций стандартный: сначала выполняются операции И, и только потом~---
|
||
операции ИЛИ. Используемая в +journalctl+ система записи выражений аналогична
|
||
принятой в классической алгебре: умножение (имеющее более высокий приоритет)
|
||
не~указывается знаком операции, а обозначается просто последовательной
|
||
записью величин.}.
|
||
|
||
\subsection{И немного магии}
|
||
|
||
Уже неплохо, правда? Но есть одна проблема~--- мы же не~сможем запомнить все
|
||
возможные значения всех полей журнала! Для этого была бы нужна очень хорошая
|
||
память. Но +journalctl+ вновь приходит к нам на помощь:
|
||
\begin{Verbatim}
|
||
$ journalctl -F _SYSTEMD_UNIT
|
||
\end{Verbatim}
|
||
Эта команда выведет все значения поля +_SYSTEMD_UNIT+, зарегистрированные в базе
|
||
данных журнала на текущий момент. То есть, имена всех юнитов +systemd+, которые
|
||
писали что-либо в журнал. Аналогичный запрос работает для всех полей, так что
|
||
найти точное значение для выборки по нему~--- уже не~проблема. Но тут самое
|
||
время сообщить вам, что эта функциональность встроена в механизм автодополнения
|
||
оболочки\footnote{Прим. перев.: В исходной статье речь идет только о bash,
|
||
однако, начиная с релиза systemd 196, аналогичная функциональность доступна и
|
||
для zsh.}! Это же просто прекрасно~--- вы можете просмотреть перечень значений
|
||
поля и выбрать из него нужно прямо при вводе выражения. Возьмем для примера
|
||
метки SELinux. Помнится, имя поля начиналось с букв SE\ldots{}
|
||
\begin{Verbatim}[commandchars=\\\{\}]
|
||
$ journalctl _SE\textbf{<TAB>}
|
||
\end{Verbatim}
|
||
и оболочка сразу же дополнит:
|
||
\begin{Verbatim}
|
||
$ journalctl _SELINUX_CONTEXT=
|
||
\end{Verbatim}
|
||
Отлично, и какое там значение нам нужно?
|
||
\begin{Verbatim}[fontsize=\small]
|
||
$ journalctl _SELINUX_CONTEXT=<TAB><TAB>
|
||
kernel system_u:system_r:rtkit_daemon_t:s0
|
||
system_u:system_r:accountsd_t:s0 system_u:system_r:syslogd_t:s0
|
||
system_u:system_r:avahi_t:s0 system_u:system_r:system_cronjob_t:s0-s0:c0.c1023
|
||
system_u:system_r:bluetooth_t:s0 system_u:system_r:system_dbusd_t:s0-s0:c0.c1023
|
||
system_u:system_r:chkpwd_t:s0-s0:c0.c1023 system_u:system_r:systemd_logind_t:s0
|
||
system_u:system_r:chronyd_t:s0 system_u:system_r:systemd_tmpfiles_t:s0
|
||
system_u:system_r:crond_t:s0-s0:c0.c1023 system_u:system_r:udev_t:s0-s0:c0.c1023
|
||
system_u:system_r:devicekit_disk_t:s0 system_u:system_r:virtd_t:s0-s0:c0.c1023 c0.c1023
|
||
system_u:system_r:dhcpc_t:s0 system_u:system_r:vpnc_t:s0 sd_t:s0-s0:c0.c1023
|
||
system_u:system_r:dnsmasq_t:s0-s0:c0.c1023 system_u:system_r:xdm_t:s0-s0:c0.c1023
|
||
system_u:system_r:init_t:s0 unconfined_u:system_r:rpm_t:s0-s0:c0.c1023
|
||
system_u:system_r:local_login_t:s0-s0:c0.c1023 unconfined_u:system_r:unconfined_t:s0-s0:c0.c1023
|
||
system_u:system_r:lvm_t:s0 unconfined_u:system_r:useradd_t:s0-s0:c0.c1023
|
||
system_u:system_r:modemmanager_t:s0-s0:c0.c1023 unconfined_u:unconfined_r:unconfined_dbusd_t:s0-s0:c0.c1023
|
||
system_u:system_r:NetworkManager_t:s0 unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
|
||
system_u:system_r:policykit_t:s0
|
||
\end{Verbatim}
|
||
Ага, нас интересуют записи с меткой PolicyKit! Пользуясь дополнением, вводим:
|
||
\begin{Verbatim}
|
||
$ journalctl _SELINUX_CONTEXT=system_u:system_r:policykit_t:s0
|
||
\end{Verbatim}
|
||
Очень просто, не~правда ли! Пожалуй, самое простое из действий, связанных с
|
||
SELinux ;-) Разумеется, такое дополнение работает для всех полей Journal.
|
||
|
||
На сегодня все. Впрочем, на странице руководства
|
||
\href{http://www.freedesktop.org/software/systemd/man/journalctl.html}%
|
||
{journalctl(1)} можно узнать и о многих других возможностях, не~описанных здесь.
|
||
Например, о том, что +journalctl+ может выводить данные в формате JSON, или в
|
||
формате +/var/log/messages+, но с относительными метками времени, как в dmesg.
|
||
|
||
\section{Управление ресурсами с помощью cgroups}
|
||
|
||
Важную роль в современных компьютерных системах играют механизмы управления
|
||
использованием ресурсов: когда вы запускаете на одной системе несколько
|
||
программ, возникает необходимость распределять между ними ресурсы системы,
|
||
в соответствии с некоторыми правилами. В частности, это особенно актуально на
|
||
маломощных встраиваемых и мобильных системах, обладающих очень скудными
|
||
ресурсами. Но та же задача актуальна и для очень мощных вычислительных
|
||
кластеров, которые располагают огромными ресурсами, но при этом несут и огромную
|
||
вычислительную нагрузку.
|
||
|
||
Исторически, в Linux поддерживался только одна схема управления ресурсами: все
|
||
процессы получают примерно равные доли процессорного времени или потока
|
||
ввода-вывода. При необходимости соотношение этих долей можно изменить при
|
||
помощи значения \emph{nice}, задаваемого для каждого процесса. Такой подход
|
||
очень прост, и на протяжении долгих лет покрывал все нужды пользователей Linux.
|
||
Но у него есть существенный недостаток: он оперирует лишь отдельными процессами,
|
||
но не~их группами. В результате, например, веб-сервер Apache с множеством
|
||
CGI-процессов при прочих равных получает гораздо больше ресурсов, чем служба
|
||
syslog, у которой не~так много процессов.
|
||
|
||
В процессе проектирования архитектуры systemd, мы практически сразу поняли, что
|
||
управление ресурсов должно быть одной из базовых функций, заложенных в
|
||
основы его структуры. В современной системе~--- неважно, серверной или
|
||
встраиваемой~--- контроль использования процессора, памяти и ввода-вывода для
|
||
различных служб нельзя добавлять задним числом. Такая функциональность должна
|
||
быть доступна изначально, через базовые настройки запуска служб. При этом,
|
||
ресурсы должны распределяться на уровне служб, а не~процессов, как это делалось
|
||
при помощи значений nice или \href{http://linux.die.net/man/2/setrlimit}{POSIX
|
||
Resource Limits}.
|
||
|
||
В этой статье я попробую рассказать о методах управления механизмами
|
||
распределения ресурсов между службами systemd. Эта функциональность присутствует
|
||
в systemd уже долгое время, и давно пора рассказать о ней пользователям и
|
||
администраторам.
|
||
|
||
В свое время я
|
||
\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},
|
||
\href{http://www.kernel.org/doc/Documentation/cgroups/memory.txt}{memory} и
|
||
\href{http://www.kernel.org/doc/Documentation/cgroups/blkio-controller.txt}{blkio}.
|
||
Для их использования необходимо, чтобы они были включены на этапе сборки ядра;
|
||
большинство дистрибутивов (в том числе и Fedora), включают их в штатных ядрах.
|
||
systemd предоставляет ряд высокоуровневых настроек, позволяющих использовать эти
|
||
контроллеры, не~вникая в технические детали их работы.
|
||
|
||
\subsection{Процессор}
|
||
|
||
Если в ядре включен контроллер +cpu+, systemd по умолчанию создает контрольную
|
||
группу по этому ресурсу для каждой службы. Даже без каких-либо дополнительных
|
||
настроек это дает положительных эффект: на системе под управлением systemd все
|
||
службы получают равные доли процессорного времени, независимо от количества
|
||
процессов, запущенных в рамках службы. Например, на вашем веб-сервере MySQL с
|
||
несколькими рабочими процессами получит такую же долю процессорного времени,
|
||
как и Apache, даже если тот запустил 1000 CGI-процессов. Разумеется, такое
|
||
поведение при необходимости можно легко отключить~--- см. опцию
|
||
\hreftt{http://www.freedesktop.org/software/systemd/man/systemd-system.conf.html}%
|
||
{DefaultControllers=} в файле +/etc/systemd/system.conf+.
|
||
|
||
Если \emph{равномерное} распределение процессорного времени между службами вас
|
||
не~устраивает, и вы хотите выделить определенным службам больше или меньше
|
||
времени~--- используйте опцию
|
||
\hreftt{http://www.freedesktop.org/software/systemd/man/systemd.resource-control.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 или его сопровождающими в вашем
|
||
дистрибутиве (если это включение не~указать явно, данный файл будет
|
||
проигнорирован). Далее, мы указываем тот параметр, который хотим
|
||
изменить\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 имя_службы.service+, и выполнить
|
||
требуемые настройки напрямую через запись значений в псевдофайлы cgroupfs
|
||
(разумеется, при следующем запуске службы к ней будут применены параметры,
|
||
указанные в конфигурационном файле). В версиях systemd со 198 по 204,
|
||
программа +systemctl+ поддерживала команды +set-cgroup-attr+,
|
||
+unset-cgroup-attr+ и +get-cgroup-attr+, позволявшие манипулировать настройками
|
||
контрольных групп. Начиная с systemd 205, они были заменены командой
|
||
+set-property+, работающей с параметрами конфигурации юнитов. Установленные
|
||
через +systemctl+ настройки сохраняются во вспомогательных конфигурационных
|
||
файлах, которые размещаются в +.d+-каталогах (см. примечание выше).}:
|
||
\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}{файле
|
||
документации}. Отметим, что из-за планируемого прекращения поддержки параметра
|
||
+memory.soft_limit_in_bytes+ на уровне ядра, опция +MemorySoftLimit=+ была
|
||
\href{http://cgit.freedesktop.org/systemd/systemd/commit/?id=ddca82aca08712a302cfabdbe59f73ee9ed3f73a}%
|
||
{удалена} из systemd, начиная с версии 208.}. При этом поддерживаются суффиксы
|
||
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,
|
||
для которой мы (пока) не~добавили высокоуровневого аналога. На этот случай мы
|
||
предусмотрели универсальных механизм задания таких опций в конфигурационных
|
||
файлах юнитов\footnote{Прим. перев.: Описываемый здесь механизм управления
|
||
низкоуровневыми настройками контрольных групп (+ControlGroupAttribute=+)
|
||
присутствовал в systemd до 204 версии включительно. Начиная с версии 205, он был
|
||
удален по требованию разработчиков cgroups. Также были удалены возможности
|
||
напрямую задавать для юнита контрольную группу (+ControlGroup=+) и устанавливать
|
||
права доступа для управления группами (+ControlGroupModify=+). Взамен,
|
||
разработчики systemd планируют значительно расширить поддержку высокоуровневых
|
||
настроек для параметров cgroups~--- сразу после того, как разработчики
|
||
cgroups определятся, какие именно параметры они будут поддерживать. Наличие
|
||
некоторого хаоса в этой области обусловлено происходящей сейчас полной
|
||
переработкой реализации контрольных групп на уровне ядра и механизмов работы с
|
||
ними из пространства пользователя. Техническая сторона данного вопроса раскрыта
|
||
в статье
|
||
\href{http://www.freedesktop.org/wiki/Software/systemd/ControlGroupInterface}%
|
||
{The New Control Group Interfaces} (пока не~переведена).}. Рассмотрим,
|
||
например, задание для службы параметра \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://www.freedesktop.org/software/systemd/man/systemd.resource-control.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 (который
|
||
вновь запустит ее при поступлении очередного запроса). При этом, со стороны
|
||
клиента невозможно отличить, когда служба запущена, а когда~--- нет. Сокет
|
||
постоянно остается открытым для входящих соединений, и все они обрабатываются
|
||
быстро и корректно.
|
||
|
||
Такая конфигурация позволяет снизить потребление системных ресурсов: службы
|
||
работают и потребляют ресурсы только тогда, когда это действительно необходимо.
|
||
Многие интернет-сайты и службы могут использовать это с выгодой для себя.
|
||
Например, хостеры веб-сайтов знают, что из огромного количества существующих в
|
||
Интернете сайтов лишь малая часть получает непрерывный поток запросов.
|
||
Большинство же сайтов, хотя и должны постоянно оставаться доступными, получают
|
||
запросы очень редко. Используя сокет-активацию, вы можете воспользоваться этим:
|
||
разместив множество таких сайтов на одной системе и активируя их службы только
|
||
при необходимости, вы получаете возможность <<оверкоммита>>: ваша система будет
|
||
обслуживать сайтов больше, чем формально позволяют ее ресурсы\footnote{Прим.
|
||
перев.: Стоит отметить, что подобная схема работает только при условии, что для
|
||
каждого клиентского сайта запускаются отдельные процессы служб, хотя это и
|
||
происходит в рамках одного хоста. Не~очень распространенный в отчественном
|
||
хостинге вариант: обычно следующей опцией после shared-хостинга (одна служба на
|
||
всех клиентов) идет VPS (каждому клиенту по виртуальному хосту).}. Разумеется,
|
||
увлекаться оверкоммитом не~стоит, иначе в моменты пиковой нагрузки ресурсов
|
||
может действительно не~хватить.
|
||
|
||
С помощью systemd вы без труда можете организовать такую схему. Множество
|
||
современных сетевых служб уже поддерживают сокет-активацию <<из коробки>> (а в
|
||
те, которые пока не~поддерживают, ее
|
||
\href{http://0pointer.de/blog/projects/socket-activation.html}{не~так уж} и
|
||
\href{http://0pointer.de/blog/projects/socket-activation2.html}{сложно}
|
||
добавить\footnote{Прим. перев.: Начиная с версии 209, в состав systemd входит
|
||
утилита
|
||
\hreftt{http://www.freedesktop.org/software/systemd/man/systemd-socket-proxyd.html}%
|
||
{systemd-socket-proxyd}, позволяющая использовать сокет-активацию даже для
|
||
служб, которые ее не~поддерживают.}). Реализованный в 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}\footnote{Прим. перев.:
|
||
\href{https://www.redhat.com/archives/libvir-list/2013-July/msg00825.html}{Патчи},
|
||
реализующие эту возможность, были подготовлены и приняты разработчиками libvirt
|
||
во второй декаде июля 2013 г., и входят в libvirt начиная с версии~1.1.1.}. А
|
||
пока, за отсутствием альтернатив, рассмотрим использование этого механизма на
|
||
примере 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{Прим. перев.: До выхода 209 версии
|
||
systemd, возможности по виртуализации сети в +systemd-nspawn+ были весьма
|
||
ограниченными (поддерживалась только полная изоляция сети от хоста), что весьма
|
||
затрудняло использование описываемой технологии. Впрочем, опытные администраторы
|
||
легко могли найти обходные пути, например: присваивание хосту дополнительного
|
||
IP-адреса (безо всякой виртуализации, командой +ip addr add+) и привязка
|
||
слушающих сокетов к конкретным адресам (в параметре +ListenStream=+ сокет-файлов
|
||
и/или в директиве +ListenAddress+ файла +sshd_config+ хоста). Начиная с systemd
|
||
версии 209, +systemd-nspawn+ предоставляет довольно широкие возможности по
|
||
виртуализации сети: проброс сетевого интерфейса хоста в контейнер
|
||
(+--network-interface=+), а также создание в контейнере виртуального сетевого
|
||
интерфейса (+--network-veth+), связывающего его с хостом. Такой виртуальный
|
||
интерфейс при необходимости может быть объединен в мост с интерфейсами хоста
|
||
(+--network-bridge=+), что обеспечит доступ из контейнера к соответствующим
|
||
сегментам сети. Подробности см. на~странице руководства
|
||
\href{http://www.freedesktop.org/software/systemd/man/systemd-nspawn.html}{systemd-nspawn(1)}.}.
|
||
|
||
Пока что 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{FAQ (часто задаваемые вопросы)\sfnote{Перевод статьи
|
||
<<\href{http://www.freedesktop.org/wiki/Software/systemd/FrequentlyAskedQuestions}%
|
||
{Frequently Asked Questions}>> с официального сайта проекта, по состоянию на
|
||
2013-05-26 08:17:02 (коммит 8cf2b).}}
|
||
|
||
Также смотрите статью
|
||
\href{http://www.freedesktop.org/wiki/Software/systemd/TipsAndTricks}{Tips \&
|
||
Tricks}\footnote{Прим. перев.: На самом деле, смотреть там особо не~на что.
|
||
Б\'{о}льшая часть приведенного там материала вполне соответствует формату FAQ, и
|
||
поэтому была перенесена в текущую главу. Исключением являются только
|
||
<<cgroup tree>> и <<ps with cgroups>>, но они подробно рассмотрены в
|
||
главе~\ref{sec:cgls}.}.
|
||
|
||
\qna{Как изменить текущий уровень выполнения (runlevel)?}
|
||
В systemd уровни выполнения оформлены в виде целевых состояний (target units).
|
||
Команда перехода в заданное состояние выглядит так:
|
||
\begin{Verbatim}
|
||
# systemctl isolate runlevel5.target
|
||
\end{Verbatim}
|
||
|
||
Отметим, что концепция уровней исполнения уже порядком устарела, и вместо
|
||
номеров уровней удобнее оперировать более выразительными именами целевых
|
||
состояний:
|
||
\begin{Verbatim}
|
||
# systemctl isolate graphical.target
|
||
\end{Verbatim}
|
||
|
||
Подобные команды изменят только текущий уровень выполнения. Их действие никак
|
||
не~повлияет на последующие загрузки системы.
|
||
|
||
\qna{Как изменить уровень выполнения, в который система грузится по умолчанию?}
|
||
Соответствующее целевое состояние задается символьной ссылкой
|
||
+/etc/systemd/system/default.target+. Для его смены достаточно перезаписать эту
|
||
ссылку\footnote{Прим. перев.: Начиная с systemd 205, в программу +systemctl+
|
||
были добавлены команды +set-default+ и +get-default+, упрощающие подобные
|
||
действия, например, +systemctl set-default multi-user.target+.}. Например, так:
|
||
\begin{Verbatim}
|
||
# ln -sf /usr/lib/systemd/system/multi-user.target /etc/systemd/system/default.target
|
||
\end{Verbatim}
|
||
или так
|
||
\begin{Verbatim}
|
||
# ln -sf /usr/lib/systemd/system/graphical.target /etc/systemd/system/default.target
|
||
\end{Verbatim}
|
||
|
||
\qna{Как узнать текущий уровень выполнения?}
|
||
В один и тот же момент времени может быть активно несколько целевых состояний,
|
||
поэтому понятие текущего уровня выполнения (единственного и однозначно
|
||
определенного) применимо уже далеко не~всегда. Узнать, какие состояния сейчас
|
||
активны, вы можете при помощи команды
|
||
\begin{Verbatim}
|
||
$ systemctl list-units --type=target
|
||
\end{Verbatim}
|
||
|
||
Если вам нужна именно одна цифра, вы можете воспользоваться классической
|
||
командой +runlevel+, однако, по изложенным выше причинам, она далеко не~всегда
|
||
способна адекватно оценить текущую ситуацию.
|
||
|
||
\qna{Я изменил service-файл, но пакетный менеджер перезаписывает мои изменения
|
||
при каждом обновлении. Что делать?}
|
||
Наиболее разумным решением будет скопировать исходный файл из
|
||
+/usr/lib/systemd/system+ в +/etc/systemd/system+ и внести изменения в
|
||
полученную копию. Юнит-файлы из +/etc+ защищены от посягательств пакетного
|
||
менеджера, и при этом имеют приоритет над файлами из +/usr+. Если же вы захотите
|
||
вернуться к настройкам по умолчанию, достаточно будет просто удалить или
|
||
переименовать созданный вами файл\footnote{Прим. перев.: Начиная с версии
|
||
systemd 198, необязательно копировать файл целиком, если вы хотите
|
||
добавить/изменить отдельные настройки. Можно просто создать в
|
||
+/etc/systemd/system/+ подкаталог +имя_службы.service.d+, а в нем~--- файл с
|
||
именем, заканчивающимся на +.conf+, например, +mysettings.conf+, после чего
|
||
вписать в этот файл необходимые настройки. Они будут применены <<поверх>>
|
||
настроек из штатного файла (расположенного в +/usr+).}.
|
||
|
||
\qna{Служба \texttt{foo.service} по умолчанию запускается только при поступлении
|
||
входящего соединения (или подключении определенного оборудования, или при
|
||
появлении заданного файла). Как сделать так, чтобы она запускалась сразу при
|
||
загрузке?}
|
||
Достаточно поместить символьную ссылку на соответствующий service-файл в каталог
|
||
+multi-user.target.wants/+.
|
||
\begin{Verbatim}
|
||
# ln -sf /usr/lib/systemd/system/foobar.service \
|
||
> /etc/systemd/system/multi-user.target.wants/foobar.service
|
||
# systemctl daemon-reload
|
||
\end{Verbatim}
|
||
|
||
В это каталоге содержатся ссылки на все юниты, которые нужно запустить в
|
||
состоянии multi-user (эквивалент уровня выполнения 3, т.е. загрузка с запуском
|
||
всех служб, но не~графического интерфейса). Так как цель +graphical.target+
|
||
тянет по зависимости и +multi-user.target+, то ваша служба будет запущена также
|
||
и при <<графических>> загрузках.
|
||
|
||
\qna{Я хочу запустить еще один экземпляр getty, как это сделать?}
|
||
Для запуска getty на последовательном порту, достаточно запустить
|
||
соответствующий экземпляр службы +serial-getty@.service+:
|
||
\begin{Verbatim}
|
||
# systemctl start serial-getty@ttyS2.service
|
||
\end{Verbatim}
|
||
|
||
Чтобы обеспечить автоматически запуск getty на этом порту при каждой загрузке,
|
||
нужно поместить соответствующую символьную ссылку в каталог
|
||
+getty.target.wants/+\footnote{Прим. перев.: Приведенная в оригинале команда
|
||
+systemctl enable serial-getty@ttyS2.service+ в systemd версии 209 и ниже
|
||
работать не~будет. Подробнее см.~примечание~\ref{ftn:enableserial}.}:
|
||
\begin{Verbatim}
|
||
# ln -s /usr/lib/systemd/system/serial-getty@.service \
|
||
> /etc/systemd/system/getty.target.wants/serial-getty@ttyS2.service
|
||
# systemctl daemon-reload
|
||
\end{Verbatim}
|
||
|
||
Обратите внимание, что на виртуальных терминалах getty запускаются
|
||
автоматически, как только пользователь переключается на данный терминал.
|
||
Максимальное количество таких терминалов задается параметром +NAutoVTs=+ в файле
|
||
\href{http://www.freedesktop.org/software/systemd/man/logind.html}%
|
||
{logind.conf(7)}. Также см. главу~\ref{sec:getty}.
|
||
|
||
\qna{Как узнать, какой службе принадлежат вот эти процессы?}
|
||
Для этого можно воспользоваться командой +ps+
|
||
\begin{Verbatim}[fontsize=\small]
|
||
$ alias psc='ps xawf -eo pid,user,cgroup,args'
|
||
$ psc
|
||
PID USER CGROUP COMMAND
|
||
...
|
||
1 root name=systemd:/systemd-1 /bin/systemd systemd.log_target=kmsg systemd.log_level=debug selinux=0
|
||
415 root name=systemd:/systemd-1/sysinit.service /sbin/udevd -d
|
||
928 root name=systemd:/systemd-1/atd.service /usr/sbin/atd -f
|
||
930 root name=systemd:/systemd-1/ntpd.service /usr/sbin/ntpd -n
|
||
932 root name=systemd:/systemd-1/crond.service /usr/sbin/crond -n
|
||
935 root name=systemd:/systemd-1/auditd.service /sbin/auditd -n
|
||
943 root name=systemd:/systemd-1/auditd.service \_ /sbin/audispd
|
||
964 root name=systemd:/systemd-1/auditd.service \_ /usr/sbin/sedispatch
|
||
937 root name=systemd:/systemd-1/acpid.service /usr/sbin/acpid -f
|
||
941 rpc name=systemd:/systemd-1/rpcbind.service /sbin/rpcbind -f
|
||
944 root name=systemd:/systemd-1/rsyslog.service /sbin/rsyslogd -n -c 4
|
||
947 root name=systemd:/systemd-1/systemd-logger.service /lib/systemd/systemd-logger
|
||
950 root name=systemd:/systemd-1/cups.service /usr/sbin/cupsd -f
|
||
955 dbus name=systemd:/systemd-1/messagebus.service /bin/dbus-daemon --system --address=systemd: --nofork --systemd-activation
|
||
969 root name=systemd:/systemd-1/getty@.service/tty6 /sbin/mingetty tty6
|
||
970 root name=systemd:/systemd-1/getty@.service/tty5 /sbin/mingetty tty5
|
||
971 root name=systemd:/systemd-1/getty@.service/tty1 /sbin/mingetty tty1
|
||
973 root name=systemd:/systemd-1/getty@.service/tty4 /sbin/mingetty tty4
|
||
974 root name=systemd:/user/lennart/2 login -- lennart
|
||
1824 lennart name=systemd:/user/lennart/2 \_ -bash
|
||
975 root name=systemd:/systemd-1/getty@.service/tty3 /sbin/mingetty tty3
|
||
988 root name=systemd:/systemd-1/polkitd.service /usr/libexec/polkit-1/polkitd
|
||
994 rtkit name=systemd:/systemd-1/rtkit-daemon.service /usr/libexec/rtkit-daemon
|
||
...
|
||
\end{Verbatim}
|
||
или просто посмотреть \verb+/proc/$PID/cgroup+. Также см. главу~\ref{sec:cgls}.
|
||
|
||
\qna{Почему вы не~используете inotify для отслеживания изменений в настройках?}
|
||
К сожалению, реализовать это весьма проблематично. За подробностями вы можете
|
||
обратиться к обсуждению в
|
||
\href{https://bugzilla.redhat.com/show_bug.cgi?id=615527}{bugzilla}\footnote{Прим.
|
||
перев.: Вкратце, суть проблемы состоит в том, что перемещение/переименование
|
||
файла не~является атомарной операцией, а состоит из удаления одного файла и
|
||
создания другого. Эти операции могут следовать в произвольном порядке с
|
||
некоторым интервалом, что может привести к временному исчезновению службы, либо
|
||
к появлению двух конфликтующих служб.}.
|
||
|
||
\qna{У моей службы есть как штатный service-файл, так и init-скрипт (с тем же
|
||
именем). Какой из этих файлов главнее~---
|
||
\texttt{/usr/lib/systemd/system/foobar.service} или
|
||
\texttt{/etc/init.d/foobar}?}
|
||
При наличии обоих этих файлов, приоритет всегда отдается штатному service-файлу,
|
||
независимо от того, включен ли запуск соответствующего скрипта (например, через
|
||
+chkconfig+) или нет. Обратите внимание, что отключение юнит-файла (в частности,
|
||
через +systemctl disable+) приведет к полному отключению службы, даже если
|
||
init-скрипт при этом будет включен.
|
||
|
||
\qna{Как заставить \texttt{journalctl} выводить полные (не~сокращенные) строки?}
|
||
Используйте
|
||
\begin{Verbatim}
|
||
# journalctl --full
|
||
\end{Verbatim}
|
||
|
||
\qna{Моя служба пытается получить приоритет реального времени, но получает
|
||
ошибку EPERM, хотя обладает всеми необходимыми привилегиями. А без вашего
|
||
systemd все работало!}
|
||
По умолчанию, systemd помещает каждую системную службу в собственную контрольную
|
||
группу контроллера +cpu+. К сожалению, из-за существующего в реализации данного
|
||
контроллера ограничения, это приводит к невозможности получения приоритета
|
||
реального времени для служб. Подробное обсуждение этой проблемы и методы ее
|
||
решения смотрите в приложении~\ref{sec:realtime}.
|
||
|
||
\qna{Моя служба настроена на запуск после \texttt{network.target}, однако она
|
||
все равно запускается раньше, чем появляется сеть. Почему?}
|
||
Это длинная история. См. приложение~\ref{sec:networktarget}.
|
||
|
||
\qna{systemd монтирует в \texttt{/tmp} \texttt{tmpfs}. Как это отключить?}
|
||
Это тоже долгая история. См. приложения~\ref{sec:apifs}.
|
||
|
||
\qna{Как просмотреть список работающих служб?}
|
||
Запустите команду +systemctl+ без параметров\footnote{Прим. перев.: +systemctl+
|
||
без параметров выведет состояние всех активных юнитов (в т.ч. сокетов,
|
||
устройств, точек монтирования, целевых состояний, таймеров, и т.д.). Чтобы
|
||
ограничиться только службами, добавьте параметр +-t service+. Чтобы вывести все
|
||
службы/юниты, включая неактивные, добавьте параметр +-a+.}:
|
||
\begin{Verbatim}[fontsize=\small]
|
||
$ systemctl
|
||
UNIT LOAD ACTIVE SUB JOB DESCRIPTION
|
||
accounts-daemon.service loaded active running Accounts Service
|
||
atd.service loaded active running Job spooling tools
|
||
avahi-daemon.service loaded active running Avahi mDNS/DNS-SD Stack
|
||
bluetooth.service loaded active running Bluetooth Manager
|
||
colord-sane.service loaded active running Daemon for monitoring attached scanners and registering them with colord
|
||
colord.service loaded active running Manage, Install and Generate Color Profiles
|
||
crond.service loaded active running Command Scheduler
|
||
cups.service loaded active running CUPS Printing Service
|
||
dbus.service loaded active running D-Bus System Message Bus
|
||
...
|
||
\end{Verbatim}
|
||
|
||
\qna{Как узнать текущее состояние службы?}
|
||
Воспользуйтесь командой +systemctl status+:
|
||
\begin{Verbatim}[fontsize=\small]
|
||
$ systemctl status udisks2.service
|
||
udisks2.service - Storage Daemon
|
||
Loaded: loaded (/usr/lib/systemd/system/udisks2.service; static)
|
||
Active: active (running) since Wed, 27 Jun 2012 20:49:25 +0200; 1 day and 1h ago
|
||
Main PID: 615 (udisksd)
|
||
CGroup: name=systemd:/system/udisks2.service
|
||
└ 615 /usr/lib/udisks2/udisksd --no-debug
|
||
|
||
Jun 27 20:49:25 epsilon udisksd[615]: udisks daemon version 1.94.0 starting
|
||
Jun 27 20:49:25 epsilon udisksd[615]: Acquired the name org.freedesktop.UDisks2 on the system message bus
|
||
\end{Verbatim}
|
||
|
||
\qna{Как отследить зависимости между юнитами?}
|
||
Допустим, вы хотите узнать, какие юниты будут запущены при активации
|
||
+multi-user.target+:
|
||
\begin{Verbatim}
|
||
$ systemctl show -p "Wants" multi-user.target
|
||
Wants=rc-local.service avahi-daemon.service rpcbind.service
|
||
NetworkManager.service acpid.service dbus.service atd.service crond.service
|
||
auditd.service ntpd.service udisks.service bluetooth.service cups.service
|
||
wpa_supplicant.service getty.target modem-manager.service portreserve.service
|
||
abrtd.service yum-updatesd.service upowerd.service test-first.service
|
||
pcscd.service rsyslog.service haldaemon.service remote-fs.target
|
||
plymouth-quit.service systemd-update-utmp-runlevel.service sendmail.service
|
||
lvm2-monitor.service cpuspeed.service udev-post.service mdmonitor.service
|
||
iscsid.service livesys.service livesys-late.service irqbalance.service
|
||
iscsi.service netfs.service
|
||
\end{Verbatim}
|
||
|
||
Вместо +Wants+ вы можете использовать другие типы зависимостей: +WantedBy+,
|
||
+Requires+, +RequiredBy+, +Conflicts+, +ConflictedBy+, +Before+,
|
||
+After+\footnote{\label{ftn:wants}Прим. перев.: Разница между +*s+ и +*edBy+~---
|
||
в направлении зависимости. Например, если цель +multi-user.target+ запрашивает
|
||
запуск (+Wants+) службы +rc-local.service+, то она будет
|
||
указана в свойстве +WantedBy+ этой службы. Интересно, что свойство
|
||
+ConflictedBy+ существует, однако задать его напрямую нельзя~--- оно
|
||
формируется только на основании +Conflicts+ других служб. Что касается
|
||
разницы между +Wants+ и +Requires+, то первое является пожеланием, а второе
|
||
требованием. Если требуемый (required) юнит не~сможет запуститься, то
|
||
не~запустится и сам зависящий от него юнит. Пожелания (wants) не~накладывают
|
||
такого ограничения~--- будет сделана попытка запуска зависимого юнита, но
|
||
результат ее никак не~отразится на зависящем юните. А указания \emph{порядка}
|
||
запуска (+Before+, +After+) вообще не~формируют зависимостей и работают только
|
||
тогда, когда такие зависимости, прямо или косвенно, уже заданы.}\footnote{Прим.
|
||
перев. Отметим, что начиная с systemd 198, +systemctl+ поддерживает
|
||
специализированную команду +list-dependencies+, позволяющую отследить прямые и
|
||
косвенные (<<рекурсивные>>) зависимости заданного юнита.}.
|
||
|
||
\qna{Как узнать, что будет запущено при загрузке системы в заданное целевое
|
||
состояние?}
|
||
Вы можете заставить systemd рассчитать <<начальную>> транзакцию, определяющую
|
||
алгоритм загрузки в заданное состояние +foobar.target+:
|
||
\begin{Verbatim}
|
||
$ systemd --test --system --unit=foobar.target
|
||
\end{Verbatim}
|
||
|
||
Обратите внимание, что эта команда предназначена исключительно для отладки, и ее
|
||
работа на самом деле не~ограничивается расчетом транзакции, поэтому не~стоит
|
||
вызывать ее в скриптах.
|
||
|
||
\section{Диагностика неполадок\sfnote{Перевод статьи
|
||
<<\href{http://freedesktop.org/wiki/Software/systemd/Debugging}{Debugging
|
||
systemd Problems}>> с официального сайта проекта, по состоянию на 2013-12-20
|
||
10:44:01 (коммит abb5a).}}
|
||
|
||
\subsection{Диагностика проблем с загрузкой}
|
||
|
||
Если система зависает во время загрузки, прежде всего нужно разобраться, на
|
||
каком этапе возникает проблема~--- до запуска systemd, или после.
|
||
|
||
Для этого надо удалить из командной стоки ядра параметры +quiet+ и +rhgb+. При
|
||
работе systemd на экран выводятся примерно такие сообщения:
|
||
\begin{Verbatim}[commandchars=\\\{\}]
|
||
Welcome to \textcolor{blue}{Fedora \emph{ВЕРСИЯ} (\emph{имя релиза})}!
|
||
Starting \emph{название}...
|
||
[ \textcolor{dgreen}{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 выводить на эту
|
||
консоль подробную отладочную информацию о ходе загрузки, добавив к
|
||
параметрам ядра следующие аргументы\footnote{\label{ftn:netconsole}
|
||
Прим. перев.: Стоит упомянуть еще об одном отладочном инструменте~---
|
||
\hreftt{https://www.kernel.org/doc/Documentation/networking/netconsole.txt}%
|
||
{netconsole}. Это механизм ядра, позволяющий отправлять содержимое
|
||
буфера +kmsg+ (см.
|
||
\hreftt{http://linux.die.net/man/8/dmesg}{dmesg(8)}) по сети на заданный
|
||
компьютер. Обратите внимание, что его настройка через командную строку
|
||
ядра (параметр +netconsole=...+) работает только если он вкомпилирован в
|
||
ядро монолитно. Если же он собран модулем, его необходимо настраивать
|
||
через +/etc/modprobe.d/*.conf+ или +configfs+ (впрочем, некоторые версии
|
||
+modprobe+ поддерживают чтение параметров модулей из командной строки
|
||
ядра, так что можно попробовать добавить туда
|
||
+netconsole.netconsole=...+), а также задавать его принудительную
|
||
подгрузку через параметр ядра +modules-load=netconsole+. Кроме того, при
|
||
этом надо обеспечить перенаправление логов systemd в буфер ядра~---
|
||
соответствующие параметры см. в разделе~\ref{sssec:kmsg}.}:
|
||
\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 /usr/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{Если у вас есть доступ к оболочке}
|
||
\label{sssec:kmsg}
|
||
|
||
Если вам все-таки удалось получить доступ к оболочке системы, вы можете
|
||
воспользоваться ею для сбора диагностической информации. Загрузите систему со
|
||
следующими параметрами ядра:
|
||
\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 Создайте файл +/usr/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}{последовательную
|
||
консоль}\footnote{Прим. перев.: Здесь опять же стоит напомнить
|
||
про +netconsole+ (см. примечание~\ref{ftn:netconsole}).}.
|
||
\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+, полученный после выполнения команды
|
||
\begin{Verbatim}
|
||
dmesg > dmesg.txt
|
||
\end{Verbatim}
|
||
Перед ее выполнением лучше загрузиться с параметрами ядра
|
||
\begin{Verbatim}
|
||
systemd.log_level=debug systemd.log_target=kmsg log_buf_len=1M
|
||
\end{Verbatim}
|
||
\item Файл +systemd-dump.txt+, полученный в результате выполнения
|
||
команды\footnote{Прим. перев.: Начиная с systemd версии 207,
|
||
вместо +systemctl dump+ нужно вызывать +systemd-analyze dump+.}
|
||
\begin{Verbatim}
|
||
systemctl dump > systemd-dump.txt
|
||
\end{Verbatim}
|
||
\item Файл +systemd-test.txt+, полученный при помощи команды
|
||
\begin{Verbatim}
|
||
/usr/bin/systemd --test --system --log-level=debug > systemd-test.txt 2>&1
|
||
\end{Verbatim}
|
||
\end{itemize}
|
||
|
||
\section{Совместимость с SysV\sfnote{Перевод статьи
|
||
<<\href{http://www.freedesktop.org/wiki/Software/systemd/Incompatibilities}%
|
||
{Compatibility with SysV}>> с официального сайта проекта, по
|
||
состоянию на 2013-10-06 21:37:19 (коммит 4db1c).}}
|
||
|
||
systemd обеспечивает высокий уровень совместимости с поведением классической
|
||
системы инициализации SysV init, реализованной во многих дистрибутивах. Это
|
||
касается как непосредственного взаимодействия с пользователем, так и поддержки
|
||
API для скриптов. Тем не~менее, существует ряд ограничений, обусловленных
|
||
техническими соображениями, а также особенностями дизайна systemd и/или
|
||
дистрибутивов. Ниже приведен список таких ограничений. Б\'{о}льшая их часть
|
||
затрагивает дистрибутивно-специфичные расширения SysV init, и поэтому будет
|
||
ощутима только для пользователей отдельных дистрибутивов.
|
||
\begin{itemize}
|
||
\item Если разработчики вашего дистрибутива отказались от SysV
|
||
init-скрипта в пользу штатного юнит-файла, прямой вызов этого
|
||
скрипта (например, +/etc/init.d/foobar start+), очевидно,
|
||
работать не~будет. Лучше воспользоваться стандартной и
|
||
универсальной командой +/sbin/service foobar start+, которая
|
||
одинаково хорошо работает как с systemd, так и с SysV init.
|
||
Отметим, что прямое обращение к скрипту в любом случае не~вполне
|
||
корректно, так как запущенная служба унаследует часть контекста
|
||
выполнения (переменные окружения, umask, ограничения по
|
||
ресурсам, идентификаторы аудита, и т.д.) от пользовательской
|
||
оболочки, из которой был запущен скрипт. Вызов скрипта через
|
||
+/sbin/service+ исправляет этот недостаток, пусть и частично.
|
||
Кроме того, стоит заметить, что стандарт LSB предписывает
|
||
обращаться к службам только через +/sbin/service+.
|
||
(Вышеуказанное ограничение \emph{не}~распространяется на
|
||
дистрибутивы, которые одновременно поддерживают и init-скрипты,
|
||
и юнит-файлы. В таких дистрибутивах даже прямое обращение к
|
||
скрипту при необходимости будет преобразовано в соответствующий
|
||
запрос к systemd\footnote{Прим. перев.: Такое поведение
|
||
задают разработчики дистрибутива, при помощи
|
||
корректировки файла, содержащего стандартные функции для
|
||
init-скриптов (например, +/etc/rc.d/init.d/functions+ в
|
||
Fedora или +/etc/rc.status+ в openSUSE). Этот файл
|
||
вызывается практически из всех init-скриптов в самом начале их
|
||
работы.}.)
|
||
\item Информация о зависимостях служб, прописанная в LSB-заголовке
|
||
скрипта, играет существенную роль. Большинство реализаций SysV
|
||
в различных дистрибутивах практически не~используют эту
|
||
информацию, либо используют ее очень ограниченно. Вследствие
|
||
этого, информацию о зависимостях часто указывали некорректно
|
||
или не~полностью. В отличие от подобных реализаций, systemd
|
||
интерпретирует эти заголовки корректно, и использует
|
||
содержащуюся в них информацию при выполнении различных операций
|
||
со службами (а не~только в момент их установки, как это делают
|
||
некоторые другие реализации SysV\footnote{Прим. перев.:
|
||
Например, +insserv+, используемый как дополнение к SysV init в
|
||
Debian (а ранее и в openSUSE).}).
|
||
\item Все операции со скриптами ограничены по времени при помощи
|
||
тайм-аутов. В отличие от классических SysV-систем, где зависший
|
||
init-скрипт полностью останавливает загрузку, systemd
|
||
ограничивает максимальную длительность работы скрипта пятью
|
||
минутами.
|
||
\item Службы запускаются в совершенно чистом контексте исполнения.
|
||
Они уже не~наследуют контекст у оболочки, из которой был вызван
|
||
их скрипт. В частности, это касается специфических переменных,
|
||
таких как \verb+$HOME+. Как следствие, скрипты, их использующие,
|
||
не~смогут работать корректно.
|
||
\item Службы и их скрипты не~могут читать с STDIN~--- для них этот поток
|
||
подключен к +/dev/null+. Следовательно, интерактивные
|
||
init-скрипты не~поддерживаются (в частности, игнорируется
|
||
используемый в LSB-заголовках скриптов Debian флаг
|
||
+X-Interactive+). К счастью, большинство дистрибутивов все равно
|
||
не~поддерживало интерактивные init-скрипты. Если вашему скрипту
|
||
нужно запросить пароль для зашифрованного диска или
|
||
SSL-ключа, вы можете воспользоваться для этого специальным
|
||
механизмом, предоставляемым systemd (см.
|
||
\href{http://www.freedesktop.org/wiki/Software/systemd/PasswordAgents}%
|
||
{описание} и
|
||
\href{http://www.freedesktop.org/software/systemd/man/systemd-ask-password.html}%
|
||
{страницу руководства}).
|
||
\item Дополнительные команды для init-скриптов (кроме стандартных,
|
||
таких, как +start+, +stop+, +status+, +restart+, +reload+,
|
||
+try-restart+, +force-reload+\footnote{Прим. перев.: Точный
|
||
список <<стандартных>> команд для SysV init-скриптов зависит от
|
||
используемого дистрибутива. Так уж исторически сложилось, что
|
||
практически каждый дистрибутив использовал собственные стандарты
|
||
интерфейсов SysV init. Это отразилось и на <<заглушках>>,
|
||
используемых для обратной совместимости. Выше приведен список
|
||
команд, которые поддерживаются как в Fedora, так и в openSUSE.})
|
||
не~поддерживаются\footnote{Прим. перев.: В частности, это
|
||
ограничение касается специфичных для init-скрипта
|
||
+/etc/init.d/iptables+ команд +save+ и +panic+.}.
|
||
\item Также не~поддерживается возможность указывать после стандартных
|
||
команд скрипту дополнительные параметры. Такое расширение SysV
|
||
не~прописано ни~в каких стандартах, и реализация его поддержки
|
||
была бы весьма проблематичной.
|
||
\item systemd останавливает только те службы, которые выполняются.
|
||
Традиционный SysV init при завершении
|
||
работы системы запускает все скрипты, предписанные +K*+-ссылками
|
||
для текущего уровня исполнения. systemd в аналогичной ситуации
|
||
действует более последовательно, и не~пытается остановить те
|
||
службы, которые не~были запущены.
|
||
\item Поддержка уровней исполнения (runlevels) в systemd имеет некоторые
|
||
ограничения. Хотя все уровни исполнения SysV имеют
|
||
соответствующие им целевые состояния (target units), далеко
|
||
не~все целевые состояния имеют соответствующие им уровни
|
||
исполнения. Это обусловлено тем, что механизм целевых состояний
|
||
отличается гораздо б\'{о}льшей гибкостью и эффективностью,
|
||
чем система уровней исполнения. Как следствие, в некоторых
|
||
случаях попытка узнать текущий уровень исполнения (например,
|
||
при помощи +/sbin/runlevel+) может вернуть просто <<+N+>> (т.е.
|
||
<<уровень выполнения неизвестен>>), что приведет к нарушению
|
||
работы скриптов, использующих явную проверку текущего уровня
|
||
исполнения. Избегайте подобных проверок.
|
||
\item +/sbin/chkconfig+ и аналогичные программы могут выводить
|
||
некорректную информацию о состоянии службы (включена/отключена).
|
||
Они оперируют только init-скриптами, игнорируя штатные
|
||
юнит-файлы. Кроме того, они опираются на механизм уровней
|
||
выполнения, который поддерживается не~полностью (см. выше).
|
||
\item По умолчанию, уровни исполнения 2, 3 и 4 являются эквивалентами
|
||
целевого состояния +multi-user.target+. Если включить службу на
|
||
одном из них, то она будет включена и на остальных. Впрочем, это
|
||
лишь настройка по умолчанию, и ничто не~мешает вам определить их
|
||
независимо. Тем не~менее, мы рекомендуем отказаться от уровней
|
||
исполнения в пользу более гибкого механизма целевых состояний
|
||
systemd.
|
||
\item Дистрибутивно-специфичные квази-уровни исполнения, используемые
|
||
для организации запуска скриптов на ранних стадиях загрузки, больше
|
||
не~поддерживаются\footnote{Прим. перев.: Начиная с версии 196,
|
||
см. коммит
|
||
\href{http://cgit.freedesktop.org/systemd/systemd/commit/?id=3cdebc217c42c8529086f2965319b6a48eaaeabe}%
|
||
{3cdeb} от 16 ноября 2012 г.}. Примерами таких уровней являются
|
||
+S+ в Debian и +b+ в openSUSE. Разумеется, это никак
|
||
не~затрагивает поддержку обычных уровней исполнения, которую мы
|
||
намерены сохранять еще очень долго.
|
||
\item По умолчанию, SysV-службы не~могут получить приоритет реального
|
||
времени, даже если располагают полными привилегиями. За
|
||
подробностями и методами решения обратитесь к
|
||
приложению~\ref{sec:realtime}.
|
||
\end{itemize}
|
||
|
||
|
||
\section{Предсказуемые имена сетевых интерфейсов\sfnote{Перевод статьи
|
||
<<\href{http://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames}%
|
||
{Predictable Network Interface Names}>> с официального сайта проекта, по
|
||
состоянию на 2014-02-21 15:36:45 (коммит 5613f).}}
|
||
|
||
Начиная с версии 197, systemd/udev присваивает сетевым интерфейсам (Ethernet,
|
||
WLAN, WWAN\footnote{Прим. перев.: WWAN (Wireless Wide Area Network)~---
|
||
относительно новый термин, обозначающий технологии глобальных беспроводных
|
||
компьютерных сетей, такие, как GPRS, 3G, WiMAX и т.д.}) стабильные,
|
||
предсказуемые имена. Новая схема именования несколько отличается от классической
|
||
(+eth0+, +eth1+, +wlan0+, \ldots{}), однако при этом лишена и многих ее
|
||
недостатков.
|
||
|
||
\subsection{Зачем?}
|
||
|
||
Классическая схема именования сетевых интерфейсов, используемая ядром,
|
||
предполагает последовательное присвоение интерфейсам имен +eth0+, +eth1+ и т.д.,
|
||
по мере опроса оборудования соответствующими драйверами (device probing). На
|
||
современных системах такой опрос не~обеспечивает гарантированного соблюдения
|
||
одного и того же порядка поступления ответов. Вследствие этого, соответствие
|
||
имен реальным интерфейсам не~является чем-то фиксированным, и очень может быть,
|
||
что интерфейс, который сейчас называется +eth0+, при следующей загрузке
|
||
превратится в +eth1+. Такая ситуация приводит к целому ряду проблем, в
|
||
частности, к проблемам с безопасностью: в настройках брандмауэра интерфейсы
|
||
идентифицируются как раз по именам.
|
||
|
||
Существует несколько подходов к решению этой проблемы. В течение многих лет udev
|
||
поддерживал механизм постоянной привязки имен к интерфейсам на основе
|
||
MAC-адресов. Такой подход имел множество недостатков, в том числе: требование
|
||
доступности корневого каталога на запись (что возможно далеко не~всегда);
|
||
необходимость внесения изменений в образ системы после загрузки на новом
|
||
оборудовании; привязка к MAC-адресам, которые далеко не~всегда являются
|
||
фиксированными (в частности, это касается многих встраиваемых систем и
|
||
механизмов виртуализации). Тем не~менее, основная проблема такого подхода
|
||
состояла в том, что он использовал имена из того же адресного пространства
|
||
(+ethX+), что и ядро. Вследствие этого, возникали ситуации <<гонки>> между udev
|
||
и ядром, когда udev пытался присвоить интерфейсу имя, которое ядро уже выдало
|
||
другому интерфейсу, что приводило к сбою операции переименования. Вследствие
|
||
перечисленных недостатков, данный механизм был удален из
|
||
systemd/udev\footnote{Прим. перев.: См. коммит
|
||
\href{http://cgit.freedesktop.org/systemd/systemd/commit/?id=3e2147858f21943d5f4a781c60f33ac22c6096ed}%
|
||
{3e214} от 3 апреля 2012 года, в котором, среди прочего, был удален каталог
|
||
+src/udev/src/rule_generator+.}.
|
||
|
||
Другая попытка решения обсуждаемой проблемы~--- biosdevname, программа,
|
||
формирующая имена интерфейсов на основании их физического расположении на
|
||
материнской плате. Соответствующая информация запрашивается у BIOS. В чем-то
|
||
такая схема похожа на ту, которую udev уже давно использует для формирования
|
||
стабильных символьных ссылок на различные устройства (+/dev/*/by-path/*+), но,
|
||
тем не~менее, в ее основе лежат несколько иные алгоритмы (udev, формируя свои
|
||
символьные ссылки, опирается на идентификационные данные, предоставленные
|
||
ядром, в то время как biosdevname использует свои собственные схемы).
|
||
|
||
И наконец, многие дистрибутивы в своих сетевых скриптах поддерживают механизмы,
|
||
позволяющие присваивать интерфейсам имена, выбранные пользователем (например,
|
||
+internet0+, +dmz0+, \ldots). Если бы не~необходимость явного вмешательства
|
||
пользователя, это было бы замечательным решением.
|
||
|
||
Мы пришли к выводу, что наилучшим вариантом для настроек по умолчанию будет
|
||
схема, являющаяся обобщением и развитием механизма biosdevname. Присвоение
|
||
интерфейсам имен на основании информации об их физическом расположении имеет ряд
|
||
существенных достоинств: такие имена формируются автоматически, всегда
|
||
предсказуемы, а также остаются неизменными даже при добавлении и удалении
|
||
оборудования, что позволяет без лишних проблем заменять сломанные сетевые
|
||
устройства. Тем не~менее, такие выглядят немного сложнее, чем привычные +eth0+ и
|
||
+wlan0+, например: +enp5s0+.
|
||
|
||
\subsection{Что именно было изменено в версии 197?}
|
||
|
||
В версии 197 мы добавили в systemd/udevd встроенную поддержку нескольких
|
||
различных механизмов именования сетевых интерфейсов, получив в итоге схему,
|
||
похожую на biosdevname, но отличающуюся большей гибкостью и максимально
|
||
приближенную к алгоритмам идентификации устройств, используемым в ядре.
|
||
В частности, udev теперь штатно поддерживает следующие схемы именования
|
||
сетевых интерфейсов:
|
||
\begin{enumerate}
|
||
\item Имена устройств, встроенных в материнскую плату, формируются на
|
||
основании информации от прошивки\footnote{Прим. перев.:
|
||
BIOS, (U)EFI, SPARC Boot PROM, \ldots.}, например: +eno1+.
|
||
\item Имена устройств, подключенных в слоты PCI Express, формируются
|
||
также на основании информации от прошивки, например: +ens1+.
|
||
\item Имена устройств формируются исходя из физического расположении
|
||
точки подключения оборудования, например, +enp2s0+.
|
||
\item Имена устройств формируются на основе их MAC-адресов, например,
|
||
+enx78e7d1ea46da+.
|
||
\item Используются классические, непредсказуемые имена, присвоенные
|
||
ядром, например, +eth0+.
|
||
\end{enumerate}
|
||
|
||
По умолчанию, systemd 197 при именовании сетевых интерфейсов последовательно
|
||
пытается применить схемы 1--3 (для первых двух требуется информация от
|
||
прошивки). Если ни одна из них не~подходит, используется схема 5. Что касается
|
||
схемы 4~--- она не~задействована по умолчанию, однако ее можно включить вручную.
|
||
|
||
Вышеописанная комбинированная схема используется лишь \emph{в последнюю
|
||
очередь}. Если у вас установлена программа biosdevname, для именования сетевых
|
||
устройств будет использоваться именно она. Кроме того, приоритет предоставляется
|
||
любым правилам, добавленным пользователем или разработчиками дистрибутива.
|
||
|
||
\subsection{Еще раз, что здесь хорошего?}
|
||
|
||
Достоинства нашей новой схемы:
|
||
\begin{itemize}
|
||
\item Имена интерфейсов остаются неизменными после перезагрузок.
|
||
\item Имена интерфейсов остаются неизменными при добавлении или
|
||
удалении устройств.
|
||
\item Имена интерфейсов остаются неизменными при обновлении/изменении
|
||
ядра и драйверов\footnote{Прим. перев.: На самом деле, не~все
|
||
так просто. Если, в результате обновлении ядра/драйверов, в них
|
||
появится ранее отсутствовавшая поддержка вашей прошивки,
|
||
не~исключена вероятность, что имена некоторых интерфейсов
|
||
перейдут с третьей схемы на первую или вторую. Будьте готовы к
|
||
такому развитию событий.}.
|
||
\item Имена интерфейсов остаются неизменными при замене сломанных
|
||
сетевых карт новыми.
|
||
\item Имена формируются автоматически, безо всякого вмешательства
|
||
пользователя.
|
||
\item Имена являются предсказуемыми: достаточно просто взглянуть на
|
||
вывод +lspci+, чтобы узнать, как будет называться конкретное
|
||
устройство.
|
||
\item Изменения в аппаратной конфигурации не~приводят к необходимости
|
||
записи в каталог +/etc+.
|
||
\item Полная поддержка систем, корень которых доступен только
|
||
для чтения.
|
||
\item Логика именования аналогична той, которая используется
|
||
udev для формирования стабильных символьных ссылок в каталоге
|
||
+/dev+ (+by-path+).
|
||
\item Работает как на x86, так и на~других архитектурах.
|
||
\item Работает одинаково во всех дистрибутивах, использующих
|
||
systemd/udev.
|
||
\item От этой схемы очень легко отказаться (см. ниже).
|
||
\end{itemize}
|
||
|
||
Есть ли у нее недостатки? Да. Раньше, если на компьютере имелся всего один
|
||
сетевой интерфейс, можно было с высокой долей вероятности утверждать, что он
|
||
называется +eth0+. Теперь же, прежде чем администратор начнет настраивать сеть,
|
||
он должен как минимум просмотреть список сетевых интерфейсов.
|
||
|
||
\subsection{Мне не~нравится ваша схема. Как ее отключить?}
|
||
|
||
У вас есть три варианта:
|
||
\begin{enumerate}
|
||
\item Вы можете полностью отключить новую схему, вернувшись к
|
||
классическим непредсказуемым именам. Для этого достаточно
|
||
заблокировать (замаскировать) файл правил udev, отвечающий за
|
||
именование интерфейсов:
|
||
\begin{Verbatim}
|
||
ln -s /dev/null /etc/udev/rules.d/80-net-setup-link.rules
|
||
\end{Verbatim}
|
||
(Заметим, что в версиях systemd со 197 по 208 соответствующий
|
||
файл назывался +80-net-name-slot.rules+.)
|
||
\item Вы можете вручную назначить интерфейсам наиболее понятные для вас
|
||
имена (например, +internet0+, +dmz0+, +lan0+). Для этого,
|
||
подготовьте свои собственные правила, указав в них нужные имена
|
||
при помощи параметра +NAME+, после чего сохраните их в файл с
|
||
более высоким приоритетом, чем правила по умолчанию, например,
|
||
+/etc/udev/rules.d/70-my-net-names.rules+\footnote{Прим. перев.:
|
||
Начиная с systemd 209, существует более удобный способ настройки
|
||
имен и других параметров сетевых интерфейсов (MAC-адреса,
|
||
скорости, дуплекса, MTU, состояния Wake on LAN)~--- он описан в
|
||
секции <<Network Link Configuration>> на странице руководства
|
||
\href{http://www.freedesktop.org/software/systemd/man/udev.html#Network%20Link%20Configuration}{udev(7)}.}.
|
||
(Приоритет файлов определяется на основании алфавитной
|
||
сортировки их имен.)
|
||
\item Вы можете скорректировать правила, используемые по умолчанию,
|
||
например, задействовав схему именования интерфейсов по
|
||
MAC-адресам. Для, этого скопируйте соответствующий
|
||
конфигурационный файл в каталог +/etc+
|
||
\begin{Verbatim}
|
||
cp /usr/lib/systemd/network/99-default.link /etc/systemd/network/99-default.link
|
||
\end{Verbatim}
|
||
после чего измените значение параметра +NamePolicy=+ так, как
|
||
считаете нужным (подробнее см.~секцию <<Network Link
|
||
Configuration>> на странице руководства
|
||
\href{http://www.freedesktop.org/software/systemd/man/udev.html#Network%20Link%20Configuration}{udev(7)})%
|
||
\footnote{Прим. перев.: В systemd версий со 197 по 208, за
|
||
логику именования интерфейсов отвечал файл правил udev
|
||
+/usr/lib/udev/rules.d/80-net-name-slot.rules+, поэтому
|
||
копировать из +/usr/lib+ в +/etc+ и редактировать нужно было
|
||
именно его. Начиная с версии 209, управление именованием
|
||
интерфейсов вынесено в udev-утилиту (built-in) +net_setup_link+,
|
||
которая вызывается через файл правил
|
||
+/etc/udev/rules.d/80-net-setup-link.rules+ и настраивается
|
||
через файлы +*.link+, размещенные в каталогах
|
||
+{/etc,/run,/usr/lib}/systemd/network+. За подготовку исходных
|
||
данных, на основе которых формируются имена интерфейсов, как и
|
||
раньше, отвечает утилита +net_id+.}.
|
||
\end{enumerate}
|
||
|
||
Кроме того, начиная с systemd версии 199, поддерживается параметр загрузки
|
||
+net.ifnames=0+, позволяющий отключить механизм предсказуемых имен (указание его
|
||
в файле конфигурации загрузчика практически эквивалентно первому из
|
||
вышеперечисленных вариантов).
|
||
|
||
\subsection{Как именно работает новая схема?}
|
||
|
||
Подробности технической реализации описаны в блоке комментариев в
|
||
\href{http://cgit.freedesktop.org/systemd/systemd/tree/src/udev/udev-builtin-net_id.c#n20}%
|
||
{исходном коде net\_id built-in}. Ознакомьтесь с ним, если у вас возникают
|
||
вопросы, касающиеся расшифровки новых имен\footnote{Прим. перев.: Далее
|
||
приводится перевод упомянутого блока комментариев. Последним коммитом,
|
||
затронувшим данный файл, на момент перевода является 1cb5d от 11 августа
|
||
2014 г.}.
|
||
|
||
\begin{Verbatim}
|
||
Предсказуемые имена сетевых интерфейсов формируются на основании:
|
||
- индексов встроенных в материнскую плату устройств (по информации от прошивки)
|
||
- индексов hotplug-слотов PCI-E (по информации от прошивки)
|
||
- физического расположения точки подключения оборудования
|
||
- MAC-адресов
|
||
|
||
Первые два символа в имени определяют тип интерфейса:
|
||
en -- ethernet
|
||
sl -- SLIP (IP через последовательный порт)
|
||
wl -- WLAN
|
||
ww -- WWAN
|
||
|
||
Последующие символы определяеются используемой схемой:
|
||
b<number> -- для устройств, подключенных по шине BCMA
|
||
ccw<number> -- для устройств, подключенных по шине CCW
|
||
o<index> -- для устройств, встроенных в материнскую плату
|
||
s<slot>[f<function>][d<dev_port>] -- для hotplug-слотов
|
||
x<MAC> -- при именовании по MAC-адресу
|
||
[P<domain>]p<bus>s<slot>[f<function>][d<dev_port>] -- на основании физического
|
||
расположения PCI-устройства
|
||
[P<domain>]p<bus>s<slot>[f<function>][u<port>][..][c<config>][i<interface>]
|
||
-- идентификационная цепочка для USB-устройств
|
||
|
||
Все многофункциональные (multi-function) PCI-устройства содержат в своем имени
|
||
номер функции "f<function>" (отсчитываются от нуля).
|
||
|
||
При именовании по расположению устройства, индекс PCI-домена "P<domain>"
|
||
указывается только в том случае, если он отличен от нулевого.
|
||
|
||
Для USB-устройства формируется полная цепочка номеров портов хабов, через
|
||
которые оно подключено. Если итоговая строка будет длиннее 15 символов, она
|
||
не экспортируется.
|
||
Стандартные значения config == 1 и interface == 0 опускаются.
|
||
|
||
Примеры:
|
||
|
||
Подключенная к PCI сетевая карта, которая идентифицируется прошивкой
|
||
по индексу "1":
|
||
ID_NET_NAME_ONBOARD=eno1
|
||
ID_NET_NAME_ONBOARD_LABEL=Ethernet Port 1
|
||
|
||
Сетевая карта, включенная в hotplug PCI-слот, который идентифицируется
|
||
прошивкой:
|
||
/sys/devices/pci0000:00/0000:00:1c.3/0000:05:00.0/net/ens1
|
||
ID_NET_NAME_MAC=enx000000000466
|
||
ID_NET_NAME_PATH=enp5s0
|
||
ID_NET_NAME_SLOT=ens1
|
||
|
||
Multi-function PCI устройство с двумя портами:
|
||
/sys/devices/pci0000:00/0000:00:1c.0/0000:02:00.0/net/enp2s0f0
|
||
ID_NET_NAME_MAC=enx78e7d1ea46da
|
||
ID_NET_NAME_PATH=enp2s0f0
|
||
/sys/devices/pci0000:00/0000:00:1c.0/0000:02:00.1/net/enp2s0f1
|
||
ID_NET_NAME_MAC=enx78e7d1ea46dc
|
||
ID_NET_NAME_PATH=enp2s0f1
|
||
|
||
Подключенная к PCI WLAN-карта:
|
||
/sys/devices/pci0000:00/0000:00:1c.1/0000:03:00.0/net/wlp3s0
|
||
ID_NET_NAME_MAC=wlx0024d7e31130
|
||
ID_NET_NAME_PATH=wlp3s0
|
||
|
||
Встроенный USB 3G модем:
|
||
/sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.4/2-1.4:1.6/net/wwp0s29u1u4i6
|
||
ID_NET_NAME_MAC=wwx028037ec0200
|
||
ID_NET_NAME_PATH=wwp0s29u1u4i6
|
||
|
||
Подключенный по USB Android-смартфон:
|
||
/sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/net/enp0s29u1u2
|
||
ID_NET_NAME_MAC=enxd626b3450fb5
|
||
ID_NET_NAME_PATH=enp0s29u1u2
|
||
\end{Verbatim}
|
||
|
||
\section{Специальные файловые системы\sfnote{Перевод статьи
|
||
<<\href{http://www.freedesktop.org/wiki/Software/systemd/APIFileSystems}{API
|
||
File Systems}>> с официального сайта проекта, по состоянию на 2013-05-26
|
||
08:37:25 (коммит 6a93f).}}
|
||
\label{sec:apifs}
|
||
|
||
\yousaywtfsk{Итак, вы запустили программу mount(8), и увидели в ее выводе
|
||
множество странных файловых систем, не~указанных в /etc/fstab. У вас могут
|
||
возникнуть вопросы: <<Как их убрать?>> или <<Как задать для них параметры
|
||
монтирования?>>.}
|
||
|
||
В Linux предусмотрено несколько способов взаимодействия программ из пространства
|
||
пользователя с ядром. Наиболее популярными механизмами являются системные вызовы
|
||
(syscall), интерфейсы Netlink, а также виртуальные файловые системы (ФС), такие,
|
||
как +/proc+ и +/sys+. Подобные ФС являются лишь программными интерфейсами, и
|
||
не~обеспечивают долговременного хранения данных (в частности, между
|
||
перезагрузками системы). Они просто используют классические механизмы работы с
|
||
файлами для предоставления доступа к различным данным и настройкам. Кроме того,
|
||
существуют специальные ФС, используемые программами для собственных нужд, в
|
||
частности, для хранения сегментов разделяемой памяти (shared memory), временных
|
||
файлов и сокетов. В данной статье мы обсудим все эти разновидности
|
||
\emph{специальных файловых систем}. Ниже представлен список таких ФС,
|
||
поддерживаемых в типовых Linux-системах:
|
||
\begin{itemize}
|
||
\item +/sys+ предоставляет доступ к драйверам и устройствам, а также
|
||
некоторым другим параметрам ядра;
|
||
\item +/proc+ дает доступ к информации о выполняемых процессах,
|
||
настройкам ядра, а также другим параметрам;
|
||
\item +/dev+ отображает файлы устройств (device nodes);
|
||
\item +/run+ содержит файлы и сокеты, используемые программами;
|
||
\item +/tmp+ хранит временные и часто изменяемые объекты (X);
|
||
\item +/sys/fs/cgroup+ (и другие файловые системы, смонтированные в
|
||
подкаталогах этого каталога) позволяют работать с иерархией
|
||
контрольных групп;
|
||
\item +/sys/kernel/security+, +/sys/kernel/debug+ (X),
|
||
+/sys/kernel/config+ (X) предоставляют доступ к
|
||
специализированным механизмам ядра;
|
||
\item +/sys/fs/selinux+ используется для взаимодействия с SELinux;
|
||
\item +/dev/shm+ содержит объекты разделяемой памяти;
|
||
\item +/dev/pts+ обеспечивает доступ к псевдо-TTY устройствам;
|
||
\item +/proc/sys/fs/binfmt_misc+ используется для регистрации в ядре
|
||
дополнительных бинарных форматов (X);
|
||
\item +/dev/mqueue+ содержит объекты IPC-механизма mqueue (X);
|
||
\item +/dev/hugepages+ позволяет программам запрашивать выделение
|
||
<<гигантских>> страниц памяти (X);
|
||
\item +/sys/fs/fuse/connections+ обеспечивает доступ к
|
||
FUSE-соединениям (X);
|
||
\item +/sys/firmware/efi/efivars+ предоставляет доступ к переменным EFI;
|
||
\end{itemize}
|
||
|
||
systemd монтирует все эти файловые системы на ранних стадиях загрузки, даже если
|
||
они не~указаны явно в +/etc/fstab+. В зависимости от настроек вашего ядра,
|
||
некоторые из перечисленных выше ФС могут быть недоступны, и наоборот, могут
|
||
присутствовать другие специальные ФС, не~приведенные в этом списке. Все эти
|
||
механизмы играют важную роль во взаимодействии ядра с программами и программ
|
||
между собой. Именно поэтому они подключаются автоматически, безо всякого участия
|
||
пользователя. Необдуманное вмешательство в их работу (отключение или изменение
|
||
параметров) может нарушить нормальную работу ваших программ, так как они утратят
|
||
доступ к необходимым для них интерфейсам.
|
||
|
||
В абсолютном большинстве случаев достаточно настроек, используемых по умолчанию.
|
||
Однако, могут возникнуть ситуации, когда необходимо изменить параметры
|
||
монтирования специальных ФС, либо полностью отключить монтирование некоторых из
|
||
них.
|
||
|
||
Несмотря на то, что эти ФС по умолчанию отсутствуют в +/etc/fstab+, ничто
|
||
не~мешает вам их туда добавить. Параметры монтирования, которые вы при этом
|
||
укажете, будут автоматически применяться к соответствующим ФС. Проще говоря:
|
||
если вам нужно изменить параметры монтирования для специальных ФС, просто
|
||
добавьте их в +/etc/fstab+ с указанием соответствующих опций. Кроме параметров
|
||
монтирования, так можно изменить и сам тип файловой системы (в частности,
|
||
перенести +/tmp+ из +tmpfs+ на раздел физического диска).
|
||
|
||
Также вы можете полностью отключить монтирование некоторых (но не~всех)
|
||
специальных систем, если это действительно необходимо. Отключаемые ФС в списке
|
||
выше отмечены (X). Для их отключения, достаточно заблокировать (замаскировать)
|
||
соответствующий юнит:
|
||
\begin{Verbatim}
|
||
systemctl mask dev-hugepages.mount
|
||
\end{Verbatim}
|
||
|
||
В результате выполнения такой операции, соответствующая ФС больше не~будет
|
||
монтироваться по умолчанию, начиная со следующей загрузки системы. О том, что
|
||
такое блокировка юнита, вы можете прочитать в~главе~\ref{sec:off}.
|
||
|
||
На всякий случай отметим, что применение к специальным ФС параметров монтирования,
|
||
указанных в +/etc/fstab+, обеспечивается службой
|
||
\hreftt{http://www.freedesktop.org/software/systemd/man/systemd-remount-fs.service.html}%
|
||
{systemd-remount-fs.service}.
|
||
|
||
\subsection*{Зачем вы мне все это рассказываете? Я просто хочу убрать tmpfs из
|
||
/tmp!}
|
||
|
||
У вас есть три варианта:
|
||
\begin{enumerate}
|
||
\item Вы можете отключить любое монтирование в +/tmp+, в результате чего
|
||
содержимое данного каталога будет храниться на том же разделе, что
|
||
и корень. Для этого достаточно выполнить команду
|
||
\begin{Verbatim}
|
||
systemctl mask tmp.mount
|
||
\end{Verbatim}
|
||
\item Вы можете смонтировать в +/tmp+ обычную файловую систему,
|
||
размещенную на диске. Для этого достаточно создать
|
||
соответствующую запись в +/etc/fstab+.
|
||
\item Если вы хотите оставить в +/tmp+ +tmpfs+, но при этом задать для
|
||
нее другой размер~--- это тоже делается через внесение в
|
||
+/etc/fstab+ соответствующей записи, предписывающей монтирование
|
||
+tmpfs+ в +/tmp+ с нужным вам значением параметра +size=+.
|
||
\end{enumerate}
|
||
|
||
\section{Запуск служб после появления сети\sfnote{Перевод статьи
|
||
<<\href{http://www.freedesktop.org/wiki/Software/systemd/NetworkTarget}{Running
|
||
Services After the Network is up}>> с официального сайта проекта, по состоянию
|
||
на 2014-06-11 13:22:03 (коммит 0ff8f).}}
|
||
\label{sec:networktarget}
|
||
|
||
\yousaywtfsk{Итак, ваша служба настроена на запуск только после достижения цели
|
||
network.target, однако, несмотря на это, она все равно запускается до появления
|
||
сети. У вас возникают вопросы: <<Почему так происходит?>> и <<Как это
|
||
исправить?>>.}
|
||
|
||
В некоторых LSB-совместимых скриптах инициализации, при описания зависимостей
|
||
используется сущность (facility) \verb+$network+. Определение этой сущности в
|
||
стандарте
|
||
\href{http://refspecs.linuxbase.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic/facilname.html}%
|
||
{довольно расплывчато} и оставляет широкий простор для различных трактовок. Вот
|
||
примеры некоторых из них:
|
||
\begin{itemize}
|
||
\item Запущен демон управления сетью (например, NetworkManager или
|
||
systemd-networkd).
|
||
\item Все заданные в настройках сетевые интерфейсы переведены в состояние
|
||
UP и получили IP-адреса.
|
||
\item Все имеющиеся физические сетевые интерфейсы, на которых
|
||
зарегистрирована несущая (link beat), получили IP-адреса.
|
||
Наличие или отсутствие явных настроек роли не~играет.
|
||
\item Доступен DNS-сервер.
|
||
\item Доступна некоторая выбранная сетевая служба.
|
||
\item Доступен некий абстрактный <<интернет>>.
|
||
\item Все заданные в настройках ethernet-интерфейсы переведены в
|
||
состояние UP, однако процесс настройки PPP-интерфейсов еще
|
||
не~начался.
|
||
\item Активирован некий профиль сетевых настроек, задающий одно из
|
||
условий выше. В разных профилях могут использоваться разные
|
||
условия.
|
||
\item Выполняется некоторый набор условий (выбор условий определяется
|
||
физическим расположением системы).
|
||
\item Настроен как минимум один глобальный адрес IPv4.
|
||
\item Настроен как минимум один глобальный адрес IPv6.
|
||
\item Настроен как минимум один глобальный адрес IPv4 или IPv6.
|
||
\end{itemize}
|
||
|
||
Этот список можно продолжать и дальше. Каждый предложенный в нем вариант можно
|
||
использовать в качестве критерия появления сети, однако ни~один из них
|
||
не~подходит в качестве варианта по умолчанию, пригодного для абсолютного
|
||
большинства задач.
|
||
|
||
Современные компьютерные сети живут в очень динамичном ритме: компьютеры
|
||
перемещаются между сетями, сетевые настройки меняются, устройства подключаются и
|
||
отключаются, виртуальные сети настраиваются, перенастраиваются и выключаются.
|
||
Наличие сетевого соединения не~является чем-то постоянным и безусловным. В
|
||
разные моменты времени компьютер может быть подключен к разным сетям. И это
|
||
справедливо не~только для мобильных устройств (смартфонов, планшетов и
|
||
ноутбуков), но и, в определенной мере, для встраиваемых и серверных систем.
|
||
Программы, которые считают, что сеть является чем-то неизменным и непрерывно
|
||
доступным, не~могут нормально функционировать в таких условиях. Хорошо
|
||
написанная программа должна корректно реагировать на изменение состояния сети, и
|
||
не~унывать в беде. Если нужный ей сервер не~доступен в настоящий момент, она
|
||
должна не~зависать по тайм-ауту, а пытаться достучаться к нему снова и снова.
|
||
Если сетевое соединение утрачено, она должна отреагировать на это. Реализовать
|
||
такое реагирование в коде демона, на самом деле, не~так уж и сложно. Существует
|
||
множество хорошо известных сетевых служб, которые уже давно поддерживают такую
|
||
возможность. Подобные службы можно запускать в любой момент, они устойчивы к
|
||
сбоям, и работают корректно во всех возможных ситуациях.
|
||
|
||
\verb+$network+ предназначена для поддержки программ, созданных в
|
||
предположении, что сеть доступна постоянно (т.е. написанных не~очень аккуратно).
|
||
Конкретные требования таких программ могут сильно отличаться. Например,
|
||
IMAP-серверу будет достаточно наличия IP-адреса, на котором можно слушать. В то
|
||
время как для клиентов сетевых файловых систем требуется требуется доступность и
|
||
работоспособность сервера, а также, опционально, наличие работоспособного DNS.
|
||
Таким образом, конкретные требования к \verb+$network+ сильно зависят от
|
||
решаемой задачи.
|
||
|
||
По соображениям надежности, загрузка системы не~должна зависеть от второстепенных
|
||
служб. В частности, отсутствие ответа от DHCP-сервера не~должно завешивать
|
||
процесс загрузки системы (за исключением ситуаций, когда сетевое соединение
|
||
действительно необходимо для работы системы, например, при загрузке по сети).
|
||
|
||
\begin{comment}
|
||
% Настоящий фрагмент документации применим к systemd версий 212 и ниже.
|
||
% Начиная с 11.06.2014 он удален из официальной документации.
|
||
% Пока остается здесь в виде комментария, для особо дотошных пользователей
|
||
% старых версий systemd.
|
||
|
||
По умолчанию, +network.target+ не~несет какой-либо сакральной смысловой
|
||
нагрузки. Сама по себе настройка службы на запуск после достижения этой цели
|
||
не~дает видимого эффекта. Наполнить +network.target+ смыслом должен сам
|
||
администратор, в зависимости от задачи которую он решает, и от конкретных
|
||
потребностей тех программ, с которыми ему приходится работать (а эти
|
||
потребности, в свою очередь, могут зависеть от их настроек). По умолчанию, мы
|
||
оставили +network.target+ <<пустышкой>>, что позволяет обеспечить максимальную
|
||
скорость загрузки, и предоставили администратору возможность самостоятельно
|
||
определить список проверок, который он считает наиболее целесообразным для
|
||
данной системы.
|
||
|
||
\subsection*{Короче, как заставить network.target работать?}
|
||
|
||
Конкретный рецепт зависит от решаемой вами задачи и потребностей ваших служб
|
||
(см. выше). Если вы используете NetworkManager, вы можете задействовать
|
||
специальную службу +NetworkManager-wait-online.service+:
|
||
\begin{Verbatim}
|
||
systemctl enable NetworkManager-wait-online.service
|
||
\end{Verbatim}
|
||
|
||
Включение этой службы позволит гарантировать, что загрузка продолжится только
|
||
после того, как все заданные в настройках NM сетевые интерфейсы будут переведены
|
||
в состояние UP и получат IP-адреса. Максимальное время ожидания~--- 90
|
||
секунд\footnote{Прим. перев.: В апстримной конфигурации по умолчанию сейчас
|
||
используется значение 30 секунд. См. параметр +--timeout+ программы +nm-online+
|
||
в файле настроек службы
|
||
+/usr/lib/systemd/system/NetworkManager-wait-online.service+.}.
|
||
Обратите внимание, что включение данной службы может сильно замедлить загрузку.
|
||
|
||
Если же такое вариант вас не~устраивает, вы можете подготовить собственный
|
||
service-файл, запускающий любую заданную вами программу или скрипт. Не~забудьте
|
||
указать, что он должен быть запущен до достижения цели +network.target+ (при
|
||
помощи параметра +Before=+). Кроме того, стоит указать зависимость от
|
||
+network.target+ при помощи директивы +Wants=+\footnote{Прим. перев.: Также
|
||
не~стоит забывать, что ваша служба сама должна быть кем-то активирована. Это
|
||
определяется параметром +WantedBy=+ или +RequiredBy=+ секции +[Install]+. Проще
|
||
всего указать здесь ту же самую +network.target+: в результате, ваша служба и
|
||
+network.target+ будут взаимно зависеть друг от друга, но при этом
|
||
+network.target+ будет активирована только после того, как отработает ваша
|
||
служба (о разницы между +WantedBy=+ и +RequiredBy=+ см.
|
||
примечание~\ref{ftn:wants}). В качестве основы вы можете взять
|
||
\href{http://cgit.freedesktop.org/NetworkManager/NetworkManager/tree/data/NetworkManager-wait-online.service.in}%
|
||
{апстримный файл} +NetworkManager-wait-online.service+. В завершение,
|
||
не~забудьте выполнить для своей службы +systemctl enable+.}.
|
||
|
||
\end{comment}
|
||
|
||
% Далее приводится новая редакция (с 11.06.2014) соответствующего фрагмента
|
||
% документации, применимая к systemd версий 213 и выше.
|
||
|
||
\subsection{Как это реализовано в systemd}
|
||
|
||
В systemd существует сразу три юнита (целевых состояния, target units), в
|
||
совокупности берущих на себя роль LSB-сущности \verb+$network+\footnote{Прим.
|
||
перев.: Приведенные здесь сведения применимы только к systemd версий 213 и выше,
|
||
в которых появился юнит +network-online.target+. О том, как сущность
|
||
\verb+$network+ работала в предыдущих версиях systemd, желающие могут
|
||
прочитать в примечании~\ref{ftn:lsbnetwork}.}:
|
||
|
||
\begin{itemize}
|
||
\item +network.target+ не~играет существенной роли в процессе
|
||
\emph{загрузки} системы. Активация данного целевого состояния
|
||
лишь показывает, что программа управления сетью была запущена.
|
||
А вот будет ли к этому моменту настроена сеть~---
|
||
не~регламентируется. Основное назначение данного юнита~---
|
||
синхронизация операций при \emph{остановке} системы. Так как
|
||
порядок остановки юнитов обратен порядку их запуска, все юниты,
|
||
в описании которых указано +After=network.target+, при
|
||
выключении системы будут остановлены до того, как отключится
|
||
сеть. Это позволит программам корректно закрывать все сетевые
|
||
соединения, а не~оставлять их <<повисшими>>. Обратите внимание,
|
||
что +network.target+ является \emph{пассивным} юнитом: вы
|
||
не~можете запустить его ни~вручную (через +systemctl start+),
|
||
ни~через зависимости своих служб (+Wants+ и +Requires+).
|
||
Активацией и деактивацией данного юнита занимается программа,
|
||
управляющая сетью в вашей системе. В юнит-файлах служб,
|
||
использующих сеть, целесообразно указывать
|
||
+After=network.target+, но ни~в~коем случае
|
||
не~+Wants=network.target+, и уж тем более
|
||
не~+Requires=network.target+\footnote{Прим. перев.: Концепция
|
||
активных и пассивных юнитов более подробно пояснена на странице
|
||
руководства
|
||
\hreftt{http://www.freedesktop.org/software/systemd/man/systemd.special.html}{systemd.special(7)}.
|
||
Она описывает взаимосвязь двух классов служб: потребители
|
||
(consumers) и поставщики (providers). Например, служба
|
||
управления сетью является поставщиком (сети), а демон
|
||
торрент-клиента~--- потребителем. Синхронизация между ними может
|
||
осуществляться посредством как пассивных, так и активных целевых
|
||
состояний. Разница между активными и пассивными целевыми
|
||
юнитами состоит в том, что для активных целей
|
||
+Requires+/+Wants+ зависимости прописываются в юнит-файле
|
||
службы-потребителя, а для пассивных~--- в юнит-файле
|
||
службы-поставщика. Никакой магии здесь нет~--- это
|
||
просто соглашение между авторами юнит-файлов. В рамках этого же
|
||
принципа, в конфигурационные файлы пассивных юнитов
|
||
добавляется директива +RefuseManualStart=yes+, запрещающая их
|
||
активацию <<вручную>>.}.
|
||
\item +network-online.target+ активируется только после появления сети.
|
||
Трактовка понятия <<появилась сеть>> остается на совести
|
||
разработчиков программы, управляющей сетью\footnote{Прим.
|
||
перев.: Определением момента готовности сети обычно занимается
|
||
отдельная утилита. В~NetworkManager это +nm-online+, в
|
||
systemd-networkd~---
|
||
\hreftt{http://www.freedesktop.org/software/systemd/man/systemd-networkd-wait-online.html}{systemd-networkd-wait-online(8)}.
|
||
В~случае NM, сеть считается готовой после появления на любом из
|
||
управляемых им устройств корректного IP-адреса (глобального или
|
||
локального), в случае networkd~--- после того, как для всех
|
||
интерфейсов, прописанных в его конфигурации, завершится процесс
|
||
настройки (успешно или с ошибкой), причем как минимум для одного
|
||
интерфейса настройка должна завершиться успешно.}. Обычно оно
|
||
подразумевает, что одному или нескольким сетевым интерфейсам
|
||
присвоены маршрутизируемые IP-адреса. Основная задача
|
||
обсуждаемого юнита~--- обеспечить задержку запуска отдельных
|
||
служб до того момента, как появится сеть. Данный юнит является
|
||
\emph{активным}, т.е. его можно указывать в зависимостях служб,
|
||
которым необходима запущенная сеть (и, в то же время, нельзя
|
||
указывать в зависимостях у самой службы управления сетью). По
|
||
умолчанию, этот юнит автоматически прописывается в зависимости к
|
||
точкам монтирования всех удаленных файловых систем (например,
|
||
NFS, SMBFS/CIFS), приведенным в файле +/etc/fstab+. Таким
|
||
образом, монтирование этих файловых систем начнется только после
|
||
того, как появится сеть. Однако, если таких точек монтирования
|
||
не~указано, а также отсутствуют службы, явно требующие по
|
||
зависимостям данный юнит, он вообще не~активируется в процессе
|
||
загрузки системы, что позволяет избежать нежелательных задержек
|
||
в случае проблем с сетью. Настоятельно рекомендуется
|
||
не~злоупотреблять зависимостями от этого юнита. В частности, для
|
||
серверных приложений, как правило, такая зависимость избыточна
|
||
(обычно, сервер может работать даже в отсутствие внешней сети,
|
||
обслуживая локальные соединения). Основная задача обсуждаемого
|
||
юнита~--- своевременный запуск клиентских программ, которые
|
||
не~могут работать без сети.
|
||
\item +network-pre.target+\footnote{Прим. перев.: Поддержка данного
|
||
юнита добавлена в systemd-networkd начиная с systemd 214. Тогда
|
||
же в официальной документации появились первые упоминания о нем.
|
||
Однако, ничто не~мешает использовать этот юнит и в более ранних
|
||
версиях systemd~--- достаточно скопировать соответствующий
|
||
\href{http://cgit.freedesktop.org/systemd/systemd/tree/units/network-pre.target}%
|
||
{конфигурационный файл} в каталог +/etc/systemd/system+.}
|
||
активируется до того, как начнется настройка сетевых
|
||
интерфейсов. Основная функция этого юнита~--- своевременный
|
||
запуск служб, выполняющих настройку брандмауэра. Таким образом,
|
||
к моменту появления сети брандмауэр будет уже готов к отражению
|
||
возможных атак. Указанный юнит является \emph{пассивным}~--- вы
|
||
не~можете запустить его вручную, и его нельзя указывать в
|
||
качестве +Requires+/+Wants+-зависимости в юнит-файлах службы
|
||
управления сетью. Напротив, такие зависимости должны
|
||
прописываться у тех служб, которые должны быть запущены до
|
||
появления сети. В юнит-файле службы управления сетью
|
||
целесообразно указать +After=network-pre.target+, но
|
||
не~+Wants=network-pre.target+/+Requires=network-pre.target+. В
|
||
то же время, в юнит-файлах служб, которые должны быть запущены
|
||
до появления сети (например, уже обсуждавшиеся выше службы
|
||
брандмауэра), наоборот, рекомендуется указывать
|
||
+Before=network-pre.target+ и +Wants=network-pre.target+. Таким
|
||
образом, данное целевое состояние будет активироваться в нужный
|
||
момент только в том случае, если у какой-либо из ваших служб
|
||
действительно имеется такая зависимость. Если же подобных служб
|
||
нет, +network-pre.target+ не~будет активироваться вообще.
|
||
\end{itemize}
|
||
|
||
Когда systemd встречает в LSB-заголовках init-скриптов зависимость
|
||
\verb+$network+, он преобразовывает ее в зависимости
|
||
+Wants=network-online.target+ и +After=network-online.target+, что позволяет
|
||
обеспечить поведение, более или менее соответствующее требованиям
|
||
LSB\footnote{\label{ftn:lsbnetwork}Прим. перев.: Трансляция LSB-сущности
|
||
\verb+$network+ в +network-online.target+ введена в systemd начиная с systemd
|
||
213. В systemd версий до 212 включительно, \verb+$network+ транслировалась в
|
||
+network.target+. Что приводило к довольно неожиданным эффектам~--- как уже
|
||
упоминалось выше, активация +network.target+ вовсе не~означает, что сеть уже
|
||
настроена. В связи с этим, официальная документация рекомендовала привязывать
|
||
+network.target+ к моменту запуска сети, например, через
|
||
+NetworkManager-wait-online.service+. Соответствующие команды приведены чуть
|
||
ниже по тексту. Они действуют даже в новых версиях systemd, но особого смысла
|
||
уже не~несут~--- задача синхронизации решается юнитом +network-online.target+,
|
||
который в случае необходимости активируется автоматически (реализовано в
|
||
юнит-файлах systemd-networkd 213 и выше, NetworkManager 0.9.9.95 и выше).}.
|
||
|
||
За дальнейшими подробностями вы можете обратиться к странице руководства
|
||
\hreftt{http://www.freedesktop.org/software/systemd/man/systemd.special.html}{systemd.special(7)}.
|
||
|
||
\subsection{Короче, как заставить network.target работать?}
|
||
|
||
Это зависит от конфигурации вашей системы и конкретных требований ваших служб
|
||
(см. выше). Многие службы управления сетью предоставляют возможность
|
||
принудительной активации +network-online.target+, в результате чего
|
||
+network.target+ по своему эффекту становится практически эквивалентной
|
||
+network-online.target+.
|
||
|
||
Если вы используете NetworkManager, вы можете задействовать
|
||
специальную службу +NetworkManager-wait-online.service+:
|
||
\begin{Verbatim}
|
||
systemctl enable NetworkManager-wait-online.service
|
||
\end{Verbatim}
|
||
|
||
Если же вы используете systemd-networkd, соответствующая служба будет называться
|
||
+systemd-networkd-wait-online.service+:
|
||
\begin{Verbatim}
|
||
systemctl enable systemd-networkd-wait-online.service
|
||
\end{Verbatim}
|
||
|
||
Включение такой службы позволит гарантировать, что загрузка продолжится только
|
||
после того, как все заданные в настройках сетевые интерфейсы будут переведены
|
||
в состояние UP и получат IP-адреса. Максимальное время ожидания~--- 90
|
||
секунд\footnote{Прим. перев.: У NetworkManager в апстримной конфигурации по
|
||
умолчанию сейчас используется значение 30 секунд. См. параметр +--timeout+
|
||
программы +nm-online+ в файле настроек службы
|
||
+/usr/lib/systemd/system/NetworkManager-wait-online.service+. У systemd-networkd
|
||
время ожидания ограничивается тайм-аутом systemd для запуска служб, по умолчанию
|
||
90 секунд.}.
|
||
Обратите внимание, что включение подобных служб может сильно замедлить загрузку.
|
||
Обе эти службы по умолчанию отключены\footnote{Прим. перев.: У внимательного
|
||
читателя может возникнуть вопрос: как же определяется момент активации
|
||
+network-online.target+, если юниты, отвечающие за ожидание сети, отключены? На
|
||
самом деле все довольно просто: при установке NetworkManager/systemd-networkd
|
||
соответствующие +*-wait-online+-юниты прописываются в зависимости к
|
||
+network-online.target+ при помощи симлинков в
|
||
+/etc/systemd/system/network-online.target.wants+. В результате, когда
|
||
какая-нибудь служба укажет зависимость от +network-online.target+, будет
|
||
автоматически активирована и соответствующая служба +*-wait-online+. Если таких
|
||
служб нет, то и активации не~произойдет. Однако, если вы принудительно включите
|
||
службу +*-wait-online+ при помощи приведенных выше команд, она будет прописана
|
||
в зависимости уже к +multi-user.target+, а значит, будет активироваться при
|
||
каждой загрузке.}.
|
||
|
||
Как альтернативный вариант, вы можете поменять непосредственно юнит-файл той
|
||
службы, которая нуждается в сети, добавив туда опции
|
||
+After=network-online.target+ и +Wants=network-online.target+\footnote{Прим.
|
||
перев.: Собственно, этот путь и является единственно разумным в большинстве
|
||
случаев. Привязка +network.target+ к +network-online.target+, описанная выше,
|
||
фактически, является пережитком проблем старых версий systemd (212 и ниже),
|
||
когда, по воле его разработчиков, LSB-сущность \texttt{\$network}
|
||
транслировалась в +network.target+.}.
|
||
|
||
\subsection{А что делать нам, разработчикам?}
|
||
|
||
Если вы~--- не~администратор, а разработчик сетевой службы, то вам стоит
|
||
задуматься не~о том, что делать с +network.target+, а о том, как можно исправить
|
||
вашу службу, чтобы она нормально реагировала на изменение состояния сети. Это
|
||
позволит порадовать ваших пользователей (когда программа работает без
|
||
дополнительной возни~--- это не~может не~радовать), а также уменьшит количество
|
||
сообщений об ошибках (так как ваша программа уже не~будет паниковать по
|
||
пустякам). Кроме того, системы ваших пользователей будут грузиться быстрее,
|
||
потому что их уже не~будет задерживать необходимость ожидать появления сети (в
|
||
случае с медленным DHCP-сервером, прогресс может быть весьма ощутимым).
|
||
|
||
Можно предложить несколько подходов к решению этой задачи:
|
||
\begin{itemize}
|
||
\item Отслеживайте изменений конфигурации сети при помощи
|
||
\href{https://www.kernel.org/doc/man-pages/online/pages/man7/rtnetlink.7.html}%
|
||
{rtnetlink} и реагируйте соответствующим образом. Это наиболее
|
||
правильный, но далеко не~всегда самый простой способ.
|
||
\item Если вы разрабатываете серверное приложение: слушайте только
|
||
адреса [::], [::1], 0.0.0.0 и 127.0.0.1. Все эти псевдо-адреса
|
||
должны быть доступны постоянно. Если ваша программа будет
|
||
слушать только их, ей будут глубоко безразличны изменения
|
||
конфигурации сети.
|
||
\item Если вы разрабатываете серверное приложение и вам нужно слушать
|
||
некий заданный адрес: воспользуйтесь опцией
|
||
\href{https://www.kernel.org/doc/man-pages/online/pages/man7/ip.7.html}%
|
||
{IP\_FREEBIND}, доступной на Linux-системах. Она позволит вашей
|
||
программе слушать даже те адреса, которые не~присвоены локальным
|
||
сетевым интерфейсам (в данный момент или вообще), что также
|
||
сделает ваш код устойчивым к изменению сетевой конфигурации.
|
||
\end{itemize}
|
||
|
||
\section{Моя служба не~может получить приоритет realtime\sfnote{Перевод статьи
|
||
<<\href{http://www.freedesktop.org/wiki/Software/systemd/MyServiceCantGetRealtime}%
|
||
{My Service Can't Get Realtime!}>> с официального сайта проекта, по состоянию на
|
||
2013-05-18 08:20:36 (коммит 2f77b).}}
|
||
\label{sec:realtime}
|
||
|
||
\yousaywtf{Итак, у вас есть служба, которая требует приоритет реального времени
|
||
(realtime). Однако, когда вы пытаетесь запустить ее на системе, работающей под
|
||
управлением systemd, ваша служба не~может получить этот приоритет, хотя обладает
|
||
всеми необходимыми для этого привилегиями. Вы хотите понять, почему так
|
||
происходит, и как это можно исправить.}
|
||
|
||
\subsection*{В чем же дело?}
|
||
|
||
По умолчанию, systemd помещает каждую системную службу в свою контрольную группу
|
||
в иерархии контроллера +cpu+. Такое поведение дает очень полезный эффект: службы
|
||
с множеством рабочих потоков (например, Apache с сотнями CGI-процессов),
|
||
получают примерно такую же долю процессорного времени, как и службы с небольшим
|
||
количеством рабочих потоков (например, MySQL). Проще говоря, процессорное время
|
||
распределяется уже не~\emph{между процессами}, а \emph{между службами}.
|
||
|
||
К сожалению, в настоящее время реализация cpu-контроллера в Linux имеет один
|
||
существенный недостаток: она требует явного задания realtime-бюджета времени (RT
|
||
budget) для своих контрольных групп. Если же бюджет группы не~задан, то ее
|
||
процессы не~смогут получить приоритет реального времени (соответствующая
|
||
операция будет завершаться с ошибкой +EPERM+~--- <<недостаточно полномочий>>).
|
||
systemd не~может присваивать такой бюджет \emph{каждой} группе, просто потому,
|
||
что не~знает, как его правильно делить между ними. Бюджет выдается в абсолютных
|
||
единицах времени, и их общее количество ограничено. Мы не~можем предложить
|
||
механизм распределения бюджета, который бы подходил по умолчанию для большинства
|
||
случаев. Поэтому, в конфигурации по умолчанию, приоритет реального времени
|
||
недоступен для системных служб.
|
||
|
||
\subsection*{Как это исправить?}
|
||
|
||
Обойти это ограничение несложно. Существует несколько различных путей:
|
||
\begin{itemize}
|
||
\item Можно просто отключить использование cpu-контроллера по умолчанию
|
||
для системных служб. Для этого, задайте в файле
|
||
+/etc/systemd/system.conf+ параметр +DefaultControllers=+ равным
|
||
пустой строке, после чего перезагрузите систему. (Либо вы
|
||
можете отключить контроллер +cpu+ на этапе сборки ядра. systemd
|
||
не~пытается использовать контроллеры, которые не~поддерживаются
|
||
ядром.)
|
||
\item Также вы можете отключить группировку по +cpu+ только для
|
||
конкретных служб. Для этого отредактируйте конфигурацию службы,
|
||
добавив параметр
|
||
\begin{Verbatim}
|
||
ControlGroup=cpu:/
|
||
\end{Verbatim}
|
||
в секцию +[Service]+. В
|
||
результате, процессы данной службы будут помещены не~в
|
||
собственную контрольную группу (как это делалось по умолчанию),
|
||
а в корневую контрольную группу иерархии +cpu+. Процессы из
|
||
корневой группы располагают полным realtime-бюджетом.
|
||
\item И наконец, вы можете явно выделить своей службе некоторый бюджет.
|
||
Для этого добавьте в секцию +[Service]+ строку наподобие
|
||
\begin{Verbatim}
|
||
ControlGroupAttribute=cpu.rt_runtime_us 500000
|
||
\end{Verbatim}
|
||
Подробнее о правильном распределении бюджета читайте в
|
||
\href{http://www.kernel.org/doc/Documentation/scheduler/sched-rt-group.txt}%
|
||
{документации к ядру}.
|
||
\end{itemize}
|
||
|
||
Последние две опции неприменимы к SysV-службам. Тем не~менее, вы можете
|
||
подготовить для таких служб соответствующие service-файлы, которые, помимо
|
||
упомянутых выше параметров, содержат вызов соответствующего init-скрипта с
|
||
аргументом +start+ в +ExecStart=+, и с аргументом +stop+~--- в +ExecStop=+.
|
||
(Также имеет смысл задать для них параметры +RemainAfterExit=1+ и
|
||
+Type=forking+.)
|
||
|
||
Отметим, что все вышесказанное касается только системных служб, и не~затрагивает
|
||
пользовательские сеансы. По умолчанию, программы пользователя размещаются в
|
||
корневой группе контроллера +cpu+, и поэтому вышеописанные ограничения их
|
||
не~касаются.
|
||
|
||
Мы надеемся, что в будущем ядро будет исправлено таким образом, чтобы
|
||
не~требовать явного задания realtime-бюджета для получения приоритета реального
|
||
времени (а при получении такого приоритета, бюджет процесса должен автоматически
|
||
выделяться из бюджета ближайшей родительской группы). В идеале, мы хотели бы
|
||
распространить практику выделения cpu-групп не~только на системные службы, но и
|
||
на пользовательские сеансы, чтобы уравнять пользователей в правах на
|
||
процессорное время, вне зависимости от того, сколько процессов запускает каждый
|
||
конкретный пользователь.
|
||
|
||
\end{document}
|
||
|
||
vim:ft=tex:tw=80:spell:spelllang=ru
|