LXF167:Грузиться быстрее
|
|
|
Управление системой. Знакомьтесь с заменой init и сопутствующим инструментарием
Содержание |
Systemd: Скорее загружаемся
Долой старое, даешь новое. Пора говорить «init, прощай». Джон Лэйн ускоряет загрузку, достигая большей степени контроля – благодаря systemd.
В течение многих лет в основе скромной ОС на базе Unix лежал процесс, называемый init – инициализация в ее сердце. Ядро вручает ему контроль за процессом загрузки, после чего он становится родителем всех процессов, что отражено в статусе его PID (идентификатора процесса), который равен 1. Но в скором времени это изменится, потому что уже есть systemd, который навсегда завоюет PID 1. Мы рассмотрим основы systemd, которые следует понимать, если ваша ОС переходит от init к systemd. По ходу дела мы также представим некоторые полезные средства администрирования, которые включает systemd.
Systemd, то есть «системный демон» – это новый менеджер системы и служб в Linux. Для конечных пользователей самое заметное его проявление – быстрая загрузка, что связано с его асинхронной природой, в корне отличной от синхронного процесса init, при котором задачи выполняются одна за другой.
При запуске компьютера его загрузчик (например, Grub) загружает ядро Linux в память и передает ему управление. Ядро выполняет свои собственные процессы инициализации, в том числе монтирование корневой файловой системы, и только после этого запускает /sbin/init. Это первый процесс, начатый в системе, и, следовательно, ему присваивается PID 1. А вот при использовании systemd /sbin/init является символической ссылкой на двоичный файл systemd, и вместо init ядро запускает systemd.
Таким образом, первое существенное различие состоит в отсутствии init. Оно означает, что /etc/inittab, а также весь набор скриптов /etc/rc.d и символических ссылок, использующихся для загрузки и поддержания системы, стал избыточным. Вы можете обнаружить, что некоторые скрипты rc.d все еще существуют, так как многие пакеты их включают для обеспечения работы в системах на базе init. Но systemd их игнорирует.
Вместо этого, в systemd используются юниты [units]. Это основные объекты, которыми управляет systemd, и они могут представлять собой службы, устройства, точки монтирования, таймеры и другие компоненты системы. Юниты определяются файлами настройки, которые находятся либо в /lib/systemd/system, либо в /etc/systemd/system, причем последний корректирует первый и предназначен для использования локальными администраторами – если вы пишете или изменяете файлы настройки юнитов, нужно размещать их там.
Первые виды юнитов, которые вы должны понимать – целевые объекты и службы. Целевые объекты – это systemd-эквиваленты концепции уровня выполнения sysvinit, а службы определяют, как система управляет процессами.
Уровни были группами процессов, которыми управлял демон init. Уровень 3 обеспечивал загрузку основной системы, тогда как 5-й уровень предоставлял графический рабочий стол, а уровень 0 останавливал систему. В случае systemd задачи имеют названия – что делает их более читаемыми (юниты, эквивалентные уровням 3 и 5, называются multi-user.target и graphical.target).
Целевой объект по умолчанию
Запуская систему, systemd активизирует свой целевой объект по умолчанию, который называется default.target. Это эквивалент уровня запуска по умолчанию init, который был бы указан в in/etc/inittab. При использовании systemd, default.target является символической ссылкой на необходимые целевые объекты, обычно multi-user.target или graphical. Для systemd, изменение уровня выполнения по умолчанию – это всего лишь вопрос замены символической ссылки:
$ ln -sf /lib/systemd/system/multi-user.target /etc/systemd/ system/default.target
Можно также переопределить целевые объекты по умолчанию, добавив в ядро опцию командной строки systemd.unit= перед загрузкой (нажав е в меню загрузочного экрана Grub).
Для тех, кто знаком с уровнями запуска, systemd предоставляет целевые объекты (это просто символические ссылки на эквивалентные целевые объекты systemd), а также поддерживает команду telinit для изменения уровней. Например, вы можете продолжать использовать telinit 0 для выключения. Но команда systemd, с помощью которой это делается, немного удобнее для чтения:
$ systemctl poweroff
Или же, для изменения уровня запуска:
$ systemctl isolate multi-user.target
Вы используете systemctl во взаимодействии с демоном systemd. Его команда isolate запускает данный целевой объект и останавливает все, что не является зависимостью этого нового целевого объекта (точно так же, как при изменении уровня запуска в sysvinit).
Задачей целевого объекта является группирование связанных единиц таким образом, чтобы они управлялись как единое целое. Соответствующие юниты задаются в виде зависимостей, чтобы после их активации все зависимости также были активизированы. Юнит можно настроить таким образом, чтобы до запуска он ждал готовности другого юнита. Однако одной из целей разработки systemd было снижение потребности в таких зависимостях, чтобы распараллелить процессы в как можно большей степени. Рассмотрим конфигурацию многопользовательского целевого объекта:
$ cat /lib/systemd/system/multi-user.target
[Unit]
Description=Multi-User
Documentation=man:systemd.special(7)
Requires=basic.target
Conflicts=rescue.service rescue.target
After=basic.target rescue.service rescue.target
AllowIsolate=yes
[Install]
Alias=default.target
Файл содержит именованные блоки: [unit] содержит параметры, действительные для всех типов юнитов, а [Install] сообщает systemd, каким образом установить целевые объекты (это мы рассмотрим позже). Файл в некоторой степени является самоочевидным: в нем говорится, что зависимостью многопользовательского целевого объекта является basic.target, что он конфликтует с целевыми объектами rescue.service и rescue.target (они будут остановлены, когда запустится этот юнит), и что он не запустится, пока не будет запущен basic.target. AllowIsolate сообщает systemd, что этот юнит может быть использован в команде systemctl isolate, описанной выше (т. е. наподобие уровня запуска sysvinit).
Юниты, перечисленные в зависимости Requires [Требуется], будут активизированы в результате активации целевого объекта. Если какие-либо из этих зависимостей не удовлетворены, целевой объект также не сработает. Есть и альтернативная форма зависимости, менее жесткая, чем Requires, под названием Wants [Желательно]. В связи с этой зависимостью, отказ не приводит к сбою целевого объекта. Зависимости могут быть указаны в файле настройки юнита, а также путем создания символических ссылок в каталоге по имени юнита и зависимости:
$ ls -l /{lib,etc}/systemd/system/multi-user.target.*
/etc/systemd/system/multi-user.target.wants/: total 0
fcron.service -> /usr/lib/systemd/system/fcron.service
lxc.target -> /etc/systemd/system/lxc.target
netcfg@bridge.service -> /usr/lib/systemd/system/netcfg@.service
ntpd.service -> /usr/lib/systemd/system/ntpd.service
remote-fs.target -> /usr/lib/systemd/system/remote-fs.target
sshdgenkeys.service -> /usr/lib/systemd/system/ sshdgenkeys.service
sshd.service -> /usr/lib/systemd/system/sshd.service /lib/systemd/system/multi-ser.target.wants/:
total 0
dbus.service -> ../dbus.service
getty.target -> ../getty.target
systemd-ask-password-wall.path -> ../systemd-ask-password- wall.path
systemd-logind.service -> ../systemd-logind.service
systemd-user-sessions.service -> ../systemd-user-sessions.service
В этом выводе два каталога, содержащих зависимости типа Wants. Каждая из этих зависимостей активизируется тогда, когда активизируется multi-user.target. Вы можете сами создать подобные символические ссылки командой ln -s, или же systemd может создать их для вас:
$ systemctl enable sshd.service
– это эквивалентно
ln -s ‘/usr/lib/systemd/system/sshd.socket’ ‘/etc/systemd/’
Команда systemctl enable поручает systemd выполнение задач, перечисленных в разделе [Install] файла настройки юнита. Например, конфигурация службы SSH содержит следующее:
[Install]
WantedBy=multi-user.target
Also=sshdgenkeys.service
Здесь от systemd требуются две вещи: создать символическую ссылку на зависимости Wants и установить дополнительные службы sshgenkeys (как это сделать, описано в его собственном файле настройки, sshgenkeys.service). Обратным процессом является systemctl disable, противоположный процессу systemctl enable. Учтите, что когда systemctl активизирует или останавливает юнит, он его не включает и не выключает. Вновь активированная служба будет включена при следующей загрузке системы. Чтобы выполнить это немедленно, есть команды start и stop:
$ systemctl start sshd.service
Если вам нужно нечто более действенное, чем команда stop, отправьте сигнал kill (или любой другой). Убьет службу команда
$ systemctl kill sshd.service
которая отправляет сигнал SIGTERM. Если нужен другой сигнал, укажите его (вы можете использовать имя с префиксом SIG или без него, или же номер сигнала; таким образом, SIGTERM, TERM и 15 эквивалентны):
$ systemctl kill -s HUP sshd.service
Эта команда kill посылает сигнал всем процессам службы. Однако вы можете выбрать только основной процесс:
$ systemctl kill -s HUP --kill-who=main sshd.service
Мы ознакомились с активацией/остановкой и включением/выключением служб. Существует и третий тип управления – маскировка [masking]. Замаскированная служба – почти то же, что и деактивированная, но ее нельзя запустить вручную. Маскировка делается созданием ссылки файла юнита на /dev/null:
$ ln -s /dev/null /etc/systemd/system/sshd.service
Systemd распознает такие юниты и показывает их как «замаскированные». Чтобы демаскировать службу, просто удалите ссылку. Как указывалось выше, служба является процессом, управляемым и контролируемым systemd. Целевые юниты обычно указывают службы как зависимости. Например, multi-user.target может указывать sshd.service в качестве зависимости. Файл настройки юнита службы устройства описывает, каким образом systemd должен запускать и останавливать службы:
[Unit]
Description=OpenSSH Daemon Wants=sshdgenkeys.service After=sshdgenkeys.service
[Service]
ExecStart=/usr/sbin/sshd -D
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=always
[Install]
WantedBy=multi-user.target
Also=sshdgenkeys.service
Здесь описывается служба sshd. Ей нужна служба sshgenkeys.service, и запускается она по ее готовности. Определена командная строка для запуска, остановки и уничтожения демона sshd, плюс указание на перезапуск демона, если тот прекратит работу.
Большинство служб, как правило, одноэлементны, то есть в системе работает только один экземпляр. Но есть службы, выполняемые несколько раз. Самый распространенный пример – getty: экземпляр getty порождается для каждого из устройств tty, которые используют подсказку для входа в систему. Systemd поддерживает такие экземпляры служб и в его условное именование включает имя экземпляра: getty@tty1.service является экземпляром getty@.service. Если подается команда запуска getty@tty1.service, а файла с таким именем нет, systemd отыщет службу getty@.service и запустит ее. Имя экземпляра, в данном случае tty1, указывается в конфигурации в виде заполнителя: %i.
Экземпляры можно указать в качестве зависимостей, путем определения их как символических ссылок:
$ ls -l /etc/systemd/system/getty.target.wants
getty@tty1.service -> /usr/lib/systemd/system/getty@.service
Или же они могут быть запущены вручную, без настройки конкретного экземпляра:
$ systemctl start getty@tty2.service
Если вы вносите в конфигурацию системы какие-либо изменения, надо, чтобы systemd перечитал свою конфигурацию:
$ systemctl daemon-reload
Иногда может требоваться, чтобы служба была доступной, но для эффективного использования ресурсов запускалась только при необходимости. Традиционный способ достижения этого состоял в использовании демона вроде inetd, который прослушивал порт от имени службы, а затем запускал службу, когда к порту был доступ. Используя тип юнита socket, systemd поддерживает это как сокет-активацию. В юнитах сокетов описывается сокет, который systemd должен прослушивать. Если сокет используется, то systemd запускает связанные с ним службы (должен существовать файл настройки его юнита). Таким образом, например, может быть настроен демон SSH; в дополнение к sshd.service, который мы рассмотрели ранее, он имеет также юнит sshd.socket. Файл sshd.socket содержит всего несколько строк:
[Unit]
Conflicts=sshd.service
Wants=sshdgenkeys.service
[Socket]
ListenStream=22
Accept=yes
[Install]
WantedBy=sockets.target
Раздел [Socket] поручает systemd прослушивать порт 22. Если на данный порт приходит пакет, systemd немедленно запустит юнит sshd.service, который после этого перехватывает сокет. Сокет-активация может быть использована для процессов, которые ее поддерживают, используя юнит сокета как зависимость целевого объекта вместо юнита службы. Процессы, которые работают с inetd, также должны работать с systemd. Таким образом, чтобы запустить sshd через сокет-активацию, наш целевой объект будет зависеть от sshd.socket вместо sshd.service (тем не менее, последний также должен существовать).
Systemd использует сокет-активацию для разрешения параллельного запуска зависимых служб. Если какой-либо службе требуется сокет другой службы, действительно важно только то, чтобы сокет существовал тогда, когда он необходим. При использовании сокет-активации для достижения этой цели systemd может обеспечивать параллельное выполнение многих других служб, что уменьшает время загрузки.
Systemd также поддерживает активацию по D-Bus. D-Bus – это система межпроцессного взаимодействия сообщений шины (www.freedesktop.org/wiki/Software/dbus), широко используемой в настольных средах, таких как Gnome и KDE, позволяя приложениям общаться. D-Bus имеет собственные файлы .service, расположенные в /usr/share/dbus-1/system-services, которые определяют, каким образом запустить службу, когда она понадобится. D-Bus может вызывать команды управления службами systemd. Это позволяет запустить неактивную службу, когда она требуется на шине.
Что происходит?
Systemd предоставляет ряд инструментов и средств, облегчающих слежение за тем, что происходит внутри вашей системы. Первое, что вы можете сделать, это использовать команду systemctl, чтобы получить общее представление о статусах всех юнитов. Сама по себе эта команда перечисляет все юниты, но вы можете ограничить вывод, скажем, всеми целевыми объектами:
$ systemctl --type target
Чтобы глубже изучить состояние конкретного юнита, запросите его статус:
$ systemctl status sshd.service
Экран состояния показывает состояние юнита (в этом примере – службы sshd), а также последних 10 сообщений ее журнала.
Systemd размещает каждый запускаемый процесс в контрольной группе, названной по своей службе. Контрольные группы – это функция ядра, позволяющая организовывать процессы иерархически, а также применять к этим группам ограничения на ресурсы. Systemd пользуется этой функцией группировки, и ему требуется активация CONFIG_CGROUPS в конфигурации ядра (она не использует ограничение ресурсов cgroup и не требует активации этих дополнительных параметров ядра). Можно увидеть организацию, обеспечивающую использование контрольных групп:
$ systemd-cgls
или просмотреть представление cgroup с помощью команды ps:
$ ps xawf -eo pid,user,cgroup,args
Еще одно важное изменение, пришедшее с systemd, состоит в том, что он предоставляет свои собственные средства ведения логов, который она называет журнал [journal]. Журнал может заменить традиционный syslog (но они могут и сосуществовать). Systemd использует службу systemd.journald вместо отдельного объекта syslog вроде syslog-ng.
Основное различие между journal и традиционным системным журналом является то, что journal – это двоичная база данных, тогда как системный лог записывается в текстовый файл в /var/log. Это означает, что для чтения журнала нужны особые средства, но они довольно просты: команда journalctl, использованная сама по себе, перечислит все журналы. Вместе с тем, полезные результаты можно получить с помощью фильтров:
$ journalctl /usr/bin/sshd
или по имени модуля:
$ journalctl _SYSTEMD_UNIT=sshd.service
По умолчанию, journal нестабилен: он записывается на временное хранение в /run/log/journal, если только не существует каталог /var/log/journal. Создав такой каталог, можно сделать journal постоянным. Разные варианты настройки для управления поведением journal указываются в файле /etc/systemd/journald.conf.
Journal – не просто замена системного журнала: он также собирает сообщения ядра, диска в оперативной памяти для начальной инициализации, ранних сообщений загрузки, а также сообщения, записанные в стандартный вывод об ошибках всех служб. journal можно также использовать вместо таких средств, как dmesg, чтобы просмотреть сообщения ядра, например:
$ journalctl _TRANSPORT=kernel
Разные варианты доступа к регистрационным данным указаны в man journalctl. Systemd также предоставляет инструменты для анализа информации, которую он собирает. Можно проверить время загрузки системы:
$ systemd-analyze
Startup finished in 4475ms (kernel) + 100491ms (userspace) = 104966ms
[Запуск завершен за 4475 мс (ядро) + 100491 мс (пользовательское пространство) = 104966 мс]
Этот вывод показывает время загрузки по частям, в ядре и в пользовательском пространстве. Если загрузка проходит не так быстро, как хотелось бы, вы здесь увидите, чья это вина:
$ systemd-analyze blame
$ systemd-analyze plot > plot.svg
Две формы вывода данных – на терминал и в виде графика. Третья форма вывода – график зависимостей. Он выводит данные в формате dot точек для дальнейшей обработки в GraphViz (открытое ПО для визуализации графов). Если пакет GraphViz установлен, вы можете создавать графические файлы SVG:
$ systemctl dot | dot -Tsvg > systemd.svg
Полученный график показывает упорядоченное расположение и требования зависимостей; в сыром виде он может быть довольно велик, но вывод можно ограничить конкретными зависимостями. Использование журнала и этих инструментов анализа позволяет легче установить причину задержек в процессе загрузки.
Новый вид chroot
Systemd включает два новых механизма, альтернативных использованию chroot. Первый – поддержка пространств имен файловой системы, относительно новая функция ядра. Они предоставляют более мощную альтернативу использованию chroot для ограничения сферы действия службы. Вы можете сделать каталоги доступными только для чтения или полностью недоступными для какой-либо службы. В раздел [Service] файла конфигурации юнита службы могут быть добавлены две директивы: ReadOnlyDirectories может быть использована для перечисления каталогов, которые служба может только читать, а InaccessibleDirectories сделает каталоги невидимыми для службы.
Например, чтобы не позволить службе трогать ваш домашний каталог /home, добавьте директиву в ее файл настройки:
[Service]
InaccessibleDirectories=/home
После этого служба не увидит домашний каталог /home и будет считать, что его нет.
Второй механизм, обеспечиваемый systemd, называется systemd-nspawn и позволяет запускать гостевую ОС внутри контейнера. Он находится между chroot и Linux Containers. Это более мощное и безопасное средство, чем chroot: оно виртуализирует иерархию файловой системы, дерево процессов, подсистемы IPC, а также хост и доменное имя. Однако оно, хотя и использует те же базовые технологии ядра, не является полноценным контейнерным решением, таким как Linux Containers – например, оно не обеспечивает виртуализацию сети. Зато создать контейнер systemd-nspawn очень просто.
Создать и загрузить гостевую ОС на Arch Linux можно двумя простыми командами:
$ mkarchroot base arch-container
$ systemd-nspawn -D arch-container/ /sbin/init
Это позволит загрузить контейнер в подсказку для входа в систему. Для контейнеров на основе Debian можно использовать debootstrap.
При использовании контейнеров иногда полезно знать, что вы находитесь в одном из них. Systemd поможет и здесь: команда systemd-detect-virt сообщит вам, работаете ли вы в виртуализированной среде. Она вернет “none” или имя используемой среды:
$ systemd-detect-virt lxc
systemd-detect-virt может обнаружить qemu, kvm, vmware, microsoft, oracle, xen, bochs, chroot, openvz, lxc, lxc-libvirt и systemd-nspawn. Используя systemd, вы можете изолировать службу от сети, задать ей собственный каталог /tmp или удалить из него функциональные возможности. Вы также можете применить ограничения ресурсов cgroup. Для получения дополнительной информации об этих полезных дополнениях обратитесь к справочной странице systemd.exec.
На нашем уроке мы рассмотрели достаточно возможностей systemd, чтобы дать вам хорошее представление о том, как его настроить и как он работает. Их гораздо больше, чем реально было бы рассмотреть в столь короткой статье, но все это очень хорошо документировано, и когда это будет применено в вашем любимом дистрибутиве, вы будете готовы. |