雑なメモ

学びを記す

いまさらだけどsystemdに入門した

そろそろCentOS7系の知識を身につけないといけないと思い、いまさらだけど入門した。 基本的には、Red Hat Enterprise Linux 7がやってきた[概要編] - Red Hat Enterprise Linux 7がやってきた:ITproに沿った内容である。 また、vagrantでbento/centos-7.1を元に確認・調査をした。

systemdとは

いくつか記事を読んでみたところ、新しいLinuxの起動処理を支える技術であるという印象を持った。従来のSysVinitやUpstartの仕組みに代わるもので、起動処理の他に、様々なサービス管理機能を持ち合わせている。ユーザープロセスを並列に起動することによって、システムの起動処理にかかる時間を短縮する等、メリットがいくつかある。


引用元 systemd - Wikipedia / "Tizenで使われているsystemdのアーキテクチャ"

systemdが作られた背景

従来の起動処理では、OSが立ち上がった際に、最初にユーザプロセスの「init」がPID=1として起動し、その後に他のプロセスが順次起動される仕組みになっている。
引用元 図1●SVR4に採用された、OSが立ち上がった際に各種ユーザープロセスを起動する...

この仕組みはとてもシンプルで、長きに渡って支持され利用されてきたが、昨今のCPUやメモリの性能進化を考えるとこの順次起動という仕組みはボトルネックになってくる。 また、このinitスクリプト自体のメンテナンス性も問題視されていた。
他にも課題はあったが、systemdは、主にこれらを解決するために開発されるに至った。
余談だが、systemdの導入には賛否両論である。しかしながら2015年現在、主要なLinuxディストリビューションのほとんどはsystemdをデフォルトのinitシステムとして採用している。

https://gyazo.com/45ff84427dbbd2605e447313f30a9347
引用元 systemd - Wikipedia / "採用および反響"

従来の仕組み

systemdについて深掘りする前に、従来の仕組みについておさらいしてみる。 起動処理は、CentOS5系では「SysVinit」、CentOS6系では「Upstart」という仕組みが採用されていた。どちらも、シェルスクリプトで起動処理を管理している。 SysVinitでは、Linuxカーネルの起動直後にPID=1として/sbin/init(SysVinitの本体)が実行され、設定ファイル/etc/inittabに従って、以下の流れで各サービスの起動処理が実行される。

  1. /etc/rc.d/rcsysintによって、ファイルシステムの整合性チェック・マウント処理・メモリスワップ領域の有効化等が行われる。
  2. /etc/rc.d/rcによって、/etc/init.d/<サービス名>が順番に実行され、chkconfigで設定されたサービス群が起動される。
  3. コンソールログインを受け付けるmingettyが起動される。


引用元 図1●SysVinitによる起動処理の流れ

抱えていた課題

昨今のサーバには、マルチコアCPUが搭載されマルチプロセスによる並列処理がますます得意になっていく中、前述の仕組みで用いられているシェルスクリプトでは並列処理が行えず、システム起動処理においてボトルネックになってしまう。

systemdのアプローチ

systemdは、これまでシェルスクリプトで行っていた処理を新しく作り直し、処理を分割して並列化することでシステム起動処理を高速化する。

systemdにおけるUnitという概念

systemdでは、様々な処理をそれぞれUnitとして定義して、実行時には各Unitを指定して実行する。つまり、従来、シェルスクリプトでシーケンシャルに実行されていた処理を複数のUnitに分解して並列実行する。


引用元 図2●システム起動処理を個別のUnitに分解

定義ファイルは、/etc/systemd/system/usr/lib/systemd/systemに配置されている。デフォルトの設定は/usr/lib/systemd/systemに、管理者が追加修正したものは/etc/systemd/systemに配置する。デフォルト値の変更時は、/usr/lib/systemd/systemのファイルをコピーして/etc/systemd/systemに保存するという具合に編集する。デフォルトに戻したい時は/etcのほうにあるファイルを削除すればよい。同名のファイルがあった場合は、/etcのほうが優先される。 Unitにはいくつか種類があり、拡張子によって判別される。管理者は主に、serviceタイプのUnitについて追加修正することが多い。

https://gyazo.com/e833b63447cfdf654488eda388680bf8
引用元 (3/3)Red Hat Enterprise Linux 7がやってきた[起動処理編] - 第1回 Linuxの起動プロセスとsystemd:ITpro

[vagrant@localhost ~]$ ls -1 /etc/systemd/system
basic.target.wants
dbus-org.freedesktop.NetworkManager.service
dbus-org.freedesktop.nm-dispatcher.service
default.target
default.target.wants
getty.target.wants
multi-user.target.wants
sockets.target.wants
sysinit.target.wants
system-update.target.wants
[vagrant@localhost ~]$ ls -1 /usr/lib/systemd/system | head
-.slice
NetworkManager-dispatcher.service
NetworkManager-wait-online.service
NetworkManager.service
arp-ethers.service
auditd.service
auth-rpcgss-module.service
autovt@.service
basic.target
basic.target.wants

Unitの依存関係と順序関係

systemdでは、Linuxカーネル起動後、PID=1として/usr/lib/systemd/systemd(systemdの本体)が起動し、その後各Unitの依存関係を検索後、設定された順序関係に基づいてUnitを有効化する。

  1. /usr/lib/systemd/systemd(systemdの本体) が起動する。
  2. systemdによって各種Unitの依存関係を検索し、有効化すべきUnitの一覧を作成する。
  3. 一覧から、Unitの順序関係に基づいてUnitを順番に有効化していく。順序関係を持たないUnitは並列に起動処理が行われる。

依存関係・順序関係はともにUnitの設定ファイルで定義されている。 依存関係は、以下のように[Unit]セクションでWants=またはRequires=に対して、当該Unitと一緒に有効化すべきUnitを指定する。 両者の違いについては、「Requires」は、有効化すべきUnitが起動に失敗すると、当該Unitの起動を取りやめる。一方、「Wants」は、有効化すべきUnitが起動に失敗しても、当該Unitの起動処理はそのまま実施する。 なお、ここでは順番は関係無く、「一緒に有効化すべきUnit群」を定義する。順序関係の定義は次に説明する。

[vagrant@localhost ~]$ sudo cat /usr/lib/systemd/system/sshd.service
[Unit]
Description=OpenSSH server daemon
After=network.target sshd-keygen.service
Wants=sshd-keygen.service

...省略
[vagrant@localhost ~]$ sudo cat /usr/lib/systemd/system/systemd-journald.service
#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

[Unit]
Description=Journal Service
Documentation=man:systemd-journald.service(8) man:journald.conf(5)
DefaultDependencies=no
Requires=systemd-journald.socket
After=systemd-journald.socket syslog.socket
Before=sysinit.target

...省略

順序関係は、[Unit]セクションでAfrer=Before=に対して、当該Unitを指定したUnitより後に起動するか前に起動するかを定義する。 以下の場合、実質なにもしないnetwork.targetをうまく使って順序関係を定義している。 これによって、sshd.servicefirewalld.serviceより後に起動されることが保証される。

[vagrant@localhost ~]$ sudo cat /usr/lib/systemd/system/sshd.service
[Unit]
Description=OpenSSH server daemon
After=network.target sshd-keygen.service
Wants=sshd-keygen.service

...省略
[vagrant@localhost ~]$ sudo cat /usr/lib/systemd/system/firewalld.service
[Unit]
Description=firewalld - dynamic firewall daemon
Before=network.target
Before=libvirtd.service
Before=NetworkManager.service
Conflicts=iptables.service ip6tables.service ebtables.service

...省略
[vagrant@localhost ~]$ sudo cat /usr/lib/systemd/system/network.target
#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

[Unit]
Description=Network
Documentation=man:systemd.special(7)
Documentation=http://www.freedesktop.org/wiki/Software/systemd/NetworkTarget

以下を見てみると、network.targetをうまく使ってることがわかる。 network.targetより前に起動されるUnitは「ネットワークを準備する系Unit」、後に起動されるサービスは「ネットワークを利用する系Unit」である。

[vagrant@localhost ~]$ sudo grep "Before=network.target" /usr/lib/systemd/system/*.service | cut -d":" -f1
/usr/lib/systemd/system/NetworkManager-wait-online.service
/usr/lib/systemd/system/NetworkManager.service
/usr/lib/systemd/system/arp-ethers.service
/usr/lib/systemd/system/firewalld.service
/usr/lib/systemd/system/rdma.service
/usr/lib/systemd/system/wpa_supplicant.service

[vagrant@localhost ~]$ sudo grep "After=network.target" /usr/lib/systemd/system/*.service | cut -d":" -f1
/usr/lib/systemd/system/dnsmasq.service
/usr/lib/systemd/system/httpd.service
/usr/lib/systemd/system/kdump.service
/usr/lib/systemd/system/nfs-lock.service
/usr/lib/systemd/system/nfs-mountd.service
/usr/lib/systemd/system/rc-local.service
/usr/lib/systemd/system/rpc-statd.service
/usr/lib/systemd/system/sshd.service

また、依存関係の定義方法は、Wants=Requires=に定義する他に、もう一つ方法がある。 「<Unit名>.wants」または「<Unit名>.requires」という名のディレクトリを作成して、配下にUnitの設定ファイルへのシンボリックリンクを作成する方法である。 以下に例を挙げる。

[vagrant@localhost ~]$ sudo ls -l /usr/lib/systemd/system/multi-user.target.wants/
合計 0
lrwxrwxrwx. 1 root root 16 10月  6 21:16 brandbot.path -> ../brandbot.path
lrwxrwxrwx. 1 root root 15 10月  6 21:16 dbus.service -> ../dbus.service
lrwxrwxrwx. 1 root root 15 10月  6 21:16 getty.target -> ../getty.target
lrwxrwxrwx. 1 root root 29 10月  6 21:16 plymouth-quit-wait.service -> ../plymouth-quit-wait.service
lrwxrwxrwx. 1 root root 24 10月  6 21:16 plymouth-quit.service -> ../plymouth-quit.service
lrwxrwxrwx. 1 root root 33 10月  6 21:16 systemd-ask-password-wall.path -> ../systemd-ask-password-wall.path
lrwxrwxrwx. 1 root root 25 10月  6 21:16 systemd-logind.service -> ../systemd-logind.service
lrwxrwxrwx. 1 root root 32 10月  6 21:16 systemd-user-sessions.service -> ../systemd-user-sessions.service

所感

systemdにおける概要とUnitについて入門できた。調べてみて、うまくできているなという印象を持ったが前述の通り、賛否両論らしいので反対派の意見にも目を通そうと思っている。
systemdについて調べたのは、もともとDockerについて調べてる中でCoreOSの話が出てきて、cloud-configについての説明を読んでるうちにunitという概念が出てきたところからyak shavingしてきたのがきっかけである。でも、いいきっかけだったと思う。まだもう少し潜ろうと思う。

今回説明しきれなかったところ

  • systemctlコマンドについて
  • systemdのログ管理機能であるjournaldについて
  • Unit設定ファイルの記述方法について

参考

理解したあとに読みたい