Quantcast
Channel: ギークを目指して
Viewing all articles
Browse latest Browse all 10

「Systemd」を理解する ーシステム起動編ー

$
0
0

2014年6月10日、とうとうRHEL7が正式リリースを迎えた。RHEL7での変更点については、このスライドに詳しく記載されているが、今回の記事では特に大きな変更点である「Systemd」について解説していきたいと思う。

Systemdは従来のinit/Upstartの代替であり、非常に高速なシステム起動・終了や、様々なシステム管理機能を提供する。ただし、その仕組みや管理方法は従来のinit/Upstartとは大きく異なり、init/Upstartに慣れ親しんだ方は習熟に時間を要するだろう。

Systemdは様々な機能と役割を持っているが、今回の記事ではSystemdによるシステム起動に焦点を当てた内容とさせていただく。Systemdを利用したシステム管理については、後日別の記事を投稿する予定だ。なお、当記事はRHEL7のベースとなっているFedora19を元に動作確認させていただいた。

それでは、Systemdの世界に飛びだそう!

Systemdとは

Systemdとは従来のSysVinitやUpstartの代替となるデーモンだ。ただし、Systemdはプロセスの起動だけではなく、プロセスの管理や様々なトリガーによるプロセス起動など、その適用範囲は広範だ。Systemdは以下の特徴を持つ。

  1. 高速なシステム起動と終了
    (Upstartよりも)高い並列度でプロセスを扱うため素早い起動・終了ができる
  2. 設定ファイルによるシステム管理の共通化
    シェルスクリプトは利用しないため、一度使い方を覚えれば様々な場面に対応できる
  3. 柔軟なプロセス起動
    様々な方法でプロセス起動できる
    ・タイマーによる起動(cron代替)
    ・socketへの通信検出によるプロセス起動(xinetd代替)
    ・所定のパスへのファイル作成をトリガーとしたプロセス起動 etc…
  4. cgroupsによるプロセス管理
    cgroupsによるリソースの利用制限や、優先度設定によるプロセス管理を実現できる

 

それではSystemdによるシステム起動プロセスを紐解いていこう。

 

ブートプロセスにおけるSystemdの影響範囲

先ずはSystemdそのものが起動されるタイミングについて記載しておく。Systemdはinit/Upstartの代替だから、BIOS/UEFIから始まるブートプロセスでの立ち位置は旧来のinit/Upstartと同様である。すごく大雑把に書くと以下のようなイメージである。 BootProcess

Systemdはinitの代替であるから、init以前の起動プロセスに影響しない。 ちなみに、Fedora19上にも/sbin/initは存在するが、これは従来のinitではなく、systemdへのシンボリックリンクとなっている。

$ cat /etc/redhat-release 
Fedora release 19 (Schrödinger’s Cat)

$ #/sbin/initは存在するがSystemdへのシンボリックリンクとなっている
$ ls -l /sbin/init
lrwxrwxrwx. 1 root root 22  6月 16 22:40 /sbin/init -> ../lib/systemd/systemd

 

Unitを理解する

Systemdによる具体的な起動プロセスを見る前に、Systemdで重要な概念となるUnitについて解説する。

Systemdにおける処理の単位 – Unitとは?

システム起動がいかなる仕組みによって実現されようが、”システムを起動するためにやらなければいけないこと”(ネットワークの初期化や必要に応じたプロセス起動など)は変わらない。システム起動を実現する仕組みの違いは、これらの処理を”どうやって管理し、どのように実行するか”という点にある。
init/Upstartでは、これらの処理をrc.sysinitや、/etc/rc.dのスクリプトで定義していたのに対し、Systemdでは”Unit”という単位で管理する。Unitは以下のような特徴を持っている。

  1. スクリプトではなく設定ファイルである
    Unitの設定を元にSystemd自身が処理を実行する
  2. Unit同士の関係を定義できる
    Aプロセスを起動するためにはBプロセスが必要、というような関係を定義できる
    SystemdはUnit間の関係を読み取り、最適な起動順を選択する(詳細は後述)
  3. 用途別に様々な種類が存在する
    ”プロセスに関する設定”、”マウントに関する設定”等、様々な種類がある
    Unitの種類はUnitファイルの拡張子で判断する

 

主なUnitの種類は以下の通りだ。個々の種類は拡張子で見分けることができ、用途に応じた様々な設定ができるようになっている。

Unitの種類
(拡張子)
設定内容
.serviceプロセスの起動/停止に関する設定
ファイル名は「プロセス名.service」となる
(httpd.service、sshd.serviceなど)
.mountファイルシステムのマウント/アンマウントに関する設定
ファイル名は「マウントポイント.mount」となる
/etc/fstabの内容を元にSystemdが自動作成する
.socketソケットの監視設定
”ソケットへの接続を検出すると特定のプロセスを起動”といった動作を実現可
(xinet.dの代替にできる)
.deviceシステムが認識しているデバイス情報を保持する
udevデーモンによって自動作成される
.pathパスの監視設定
”監視ディレクトリにファイルが置かれたらサービス起動”といった動作を実現可
.target複数のUnitをとりまとめるUnit

 

例えば、apacheと対応するUnitであるhttpd.serviceは以下のように定義される。従来のinit/Upstartでは、このようなhttpdに関する起動設定はスクリプト(/etc/rc.d/init.d/httpd)として定義されていただろう。httpd.serviceでは単なる設定ファイルとなっていることが分かる。

$ cat /usr/lib/systemd/system/httpd.service
[Unit]
Description=The Apache HTTP Server
After=network.target remote-fs.target nss-lookup.target

[Service]
Type=notify
EnvironmentFile=/etc/sysconfig/httpd
ExecStart=/usr/sbin/httpd $OPTIONS -DFOREGROUND
ExecReload=/usr/sbin/httpd $OPTIONS -k graceful
ExecStop=/usr/sbin/httpd $OPTIONS -k graceful-stop
KillSignal=SIGCONT
PrivateTmp=true

[Install]
WantedBy=multi-user.target

httpd.serviceはプロセスに関するUnitに分類されるため、プロセス関連の設定項目を保持するが、.mountなどのその他Unitは、個々の用途に応じた設定項目を保持している。

Unitファイルの格納先

Unitファイルの格納先は以下の2カ所である。

ディレクトリ設定内容
/usr/lib/systemd/systemインストール時の初期設定
当ディレクトリ内のUnitは編集しない
/etc/systemd/systemユーザによる個別設定
デフォルトの設定を変更する場合は、当ディレクトリにUnitファイルをコピーして編集する
(Systemdは当ディレクトリのUnitを優先する)

インストール直後の設定と、個別の設定を格納するディレクトリが分かれている。個別の設定を行う場合は/etc/systemd/systemを利用しよう。

Unit間の関係定義

Systemdでは、Unit間の関係を定義できる。関係には”依存”と”起動順”の2種類がある。

  1. 依存
    プロセス同士が”同時に起動するべきか”を定義する。
    例えばプロセスAがBに依存(プロセスAは、プロセスBと同時に起動されるべき)と定義した場合、プロセスAが起動対象となった場合にプロセスBも同時に起動される。
    依存はあくまでも”同時起動されるべきか”を定義するものであり、プロセスの起動順(プロセスA、Bのどちらを先に起動するか)は表現しない。
    依存には”Wants”(可能な限り同時起動)、”Requires”(必ず同時起動)、”Comflict”(同時起動しない)などがある。
  2. 起動順
    プロセス起動の前後関係を定義する。
    例えば”プロセスAはプロセスBよりも先に起動するべき”といった関係を定義できる。
    順序関係は”After”、”Before”などがある。

 

Systemdでは、依存と起動順を別々に定義することによって、Upstartよりもプロセス起動の並列度を上げている。例えば、依存関係があるものの起動順の指定が無いプロセスA、Bが存在したとする。この場合、Systemdは”システム起動完了時点でプロセスA、Bが起動状態となっていれば良い”と判断できるため、プロセスA、Bを並行して起動するといった動作が実施可能となるのだ。

依存関係と順序関係の定義方法は以下の通りだ。

関係分類内容と設定方法
Wants依存強制力の無い依存関係。
Systemdはプロセスの同時起動を試みるが、依存先の起動に失敗した場合であっても、依存元の起動は引き続き実施する。

■「 AはBに依存する」の定義方法
方法1.AのUnit定義ファイルに直接設定を記述
A.service -------------------------------------------
[Unit]
Wants=B.service
---------------------------------------------------

方法2.AのwantsディレクトリにBのシンボリックリンクを作成
A.serviceファイルが存在するディレクトリ上に「A.service.wants」ディレクトリを作成し、このディレクトリ内にB.serviceへのシンボリックリンクを作成。
Requires依存強制力の有る依存関係。
Systemdはプロセスの同時起動を試み、依存先の起動に失敗した場合、依存元は起動しない。

■「AはBに依存する」の定義方法
Wantsと同様の定義方法。A.serviceファイル内に記載する場合の設定項目は「Requires=B.service」となる。また、シンボリックリンクを利用する場合、作成するディレクトリ名は「A.service.requires」となる。
Conflict依存競合する関係。
Systemdはこの関係が定義されたプロセスは同時起動しない。

■「AとBは競合する」の定義方法
A.service -------------------------------------------
[Unit]
conflicts=A.service
---------------------------------------------------
※ B.serviceに「conflicts=A.service」と設定しても良い。
After起動順自身よりも前に起動するプロセスを定義。

■ 「Aよりも前にBを起動する」の定義方法
A.service -------------------------------------------
[Unit]
After=B.service
---------------------------------------------------
※ ”B.serviceより後に自分を起動してね”という設定
Before起動順自身よりも後に起動するプロセスを定義。

■ 「Aの後にBを起動する」の定義方法
A.service -------------------------------------------
[Unit]
Before=B.service
---------------------------------------------------
※ ”自分よりも後にBserviceを起動してね”という設定

 

複数のUnitをまとめる「target」Unit

targetと呼ばれる種類のUnitを利用することで複数のUnitをグルーピングすることができる。
targetは他Unitとの関係を保持し、複数のUnitをとりまとめる。例えばネットワーク関連のUnitをとりまとめたnetwork.targetや、従来のrc.sysinitに相当する処理をとりまとめたsysinit.target等が存在する。targetを利用することで、Unit同士の依存や起動順定義が楽に行うことができる。

例えばsysinit.targetの場合、targetで取りまとめられているUnitはsysinit.target自身やsysinit.target.wantsディレクトリに保存されている。

$ pwd
/usr/lib/systemd/system

$ cat sysinit.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=System Initialization
Documentation=man:systemd.special(7)
Conflicts=emergency.service emergency.target
Wants=local-fs.target swap.target
After=local-fs.target swap.target emergency.service emergency.target
RefuseManualStart=yes

$ ls -l sysinit.target.wants/
合計 0
lrwxrwxrwx. 1 root root 20  6月 16 22:40 cryptsetup.target -> ../cryptsetup.target
lrwxrwxrwx. 1 root root 22  6月 16 22:40 dev-hugepages.mount -> ../dev-hugepages.mount
lrwxrwxrwx. 1 root root 19  6月 16 22:40 dev-mqueue.mount -> ../dev-mqueue.mount
lrwxrwxrwx. 1 root root 30  6月 16 22:42 plymouth-read-write.service -> ../plymouth-read-write.service
lrwxrwxrwx. 1 root root 25  6月 16 22:42 plymouth-start.service -> ../plymouth-start.service
lrwxrwxrwx. 1 root root 36  6月 16 22:40 proc-sys-fs-binfmt_misc.automount -> ../proc-sys-fs-binfmt_misc.automount
lrwxrwxrwx. 1 root root 32  6月 16 22:40 sys-fs-fuse-connections.mount -> ../sys-fs-fuse-connections.mount
lrwxrwxrwx. 1 root root 26  6月 16 22:40 sys-kernel-config.mount -> ../sys-kernel-config.mount
lrwxrwxrwx. 1 root root 25  6月 16 22:40 sys-kernel-debug.mount -> ../sys-kernel-debug.mount
lrwxrwxrwx. 1 root root 36  6月 16 22:40 systemd-ask-password-console.path -> ../systemd-ask-password-console.path
lrwxrwxrwx. 1 root root 25  6月 16 22:40 systemd-binfmt.service -> ../systemd-binfmt.service
lrwxrwxrwx. 1 root root 32  6月 16 22:40 systemd-journal-flush.service -> ../systemd-journal-flush.service
lrwxrwxrwx. 1 root root 27  6月 16 22:40 systemd-journald.service -> ../systemd-journald.service
lrwxrwxrwx. 1 root root 31  6月 16 22:40 systemd-modules-load.service -> ../systemd-modules-load.service
lrwxrwxrwx. 1 root root 35  6月 16 22:40 systemd-random-seed-load.service -> ../systemd-random-seed-load.service
lrwxrwxrwx. 1 root root 25  6月 16 22:40 systemd-sysctl.service -> ../systemd-sysctl.service
lrwxrwxrwx. 1 root root 37  6月 16 22:40 systemd-tmpfiles-setup-dev.service -> ../systemd-tmpfiles-setup-dev.service
lrwxrwxrwx. 1 root root 33  6月 16 22:40 systemd-tmpfiles-setup.service -> ../systemd-tmpfiles-setup.service
lrwxrwxrwx. 1 root root 31  6月 16 22:40 systemd-udev-trigger.service -> ../systemd-udev-trigger.service
lrwxrwxrwx. 1 root root 24  6月 16 22:40 systemd-udevd.service -> ../systemd-udevd.service
lrwxrwxrwx. 1 root root 33  6月 16 22:40 systemd-vconsole-setup.service -> ../systemd-vconsole-setup.service

例えばsysinit.targetファイルはAfter=local-fs.target swap.target emergency.service emergency.targetといった設定になっている。local-fs.targetswap.targetはwants指定しているため、”同時起動”対象としてSystemdに認識されるが、emergency.serviceemergency.targetは起動順の設定(After)のみとなっており、依存(Wants、Requires)の設定は行われていない。

これは、sysinit.targetの起動に伴ってemergency.serviceemergency.targetの起動は行わない事を指しているが、emergency.serviceemergency.targetが起動対象となった場合、これらのtarget/serviceはsysinit.targetよりも前に起動されるべきという関係を定義している。

Systemdにおけるランレベルの扱いと「default.target」

Unitの概念を理解した所で、Systemdにおけるランレベルの扱いを説明する。

ランレベルをUnitとして表現する

Systemdでは全てがUnit上で表現されるため、従来のランレベルというプロセスの管理体系は存在しない。Systemdではランレベルに相当する機能を先述のtarget Unitを利用して表現している。

ランレベルの目的は、ランレベル別に定義された状態でシステムを起動するために必要となるプロセスを管理することにあり、init/Upstartでは、/etc/rc.d/rc[runlevel].dにランレベルに応じたプロセス群を定義することでこれを実現していた。
Systemdでは、これらのプロセスをtargetとしてグルーピングすることでランレベルと同様のプロセス管理を実現している(※)。例えば、従来のランレベル3(マルチユーザ・コンソールログインのみ)に必要となるプロセス群はmulti-user.targetとして定義されている。

※ 従来のランレベルは0 – 6の7種類のみ定義されていたのに対し、targetは好きなだけ定義できるため、より柔軟になったと言える。

システムの起動後状態を表す定義済targetは以下の通りである。

target名内容従来のランレベル
(RedHat)
poweroff.targetシステム停止0
rescue.targetシングルユーザモード1
multi-user.targetマルチユーザモード
(コンソールログイン)
3
graphical.targetマルチユーザ+GUI5
reboot.target再起動6
emergency.target緊急シェル
rescue.targetよりも起動対象が少ない
ルートファイルシステムですら
マウントできない場合などに利用
無し

また、互換性?のために従来のランレベルを意図した名称のtargetが定義されている。ただし、これらのtargetは上表のtargetへのシンボリックリンクとなっている。

$ pwd
/usr/lib/systemd/system

$ ll runlevel*.target
lrwxrwxrwx. 1 root root 15  6月 16 22:40 runlevel0.target -> poweroff.target
lrwxrwxrwx. 1 root root 13  6月 16 22:40 runlevel1.target -> rescue.target
lrwxrwxrwx. 1 root root 17  6月 16 22:40 runlevel2.target -> multi-user.target
lrwxrwxrwx. 1 root root 17  6月 16 22:40 runlevel3.target -> multi-user.target
lrwxrwxrwx. 1 root root 17  6月 16 22:40 runlevel4.target -> multi-user.target
lrwxrwxrwx. 1 root root 16  6月 16 22:40 runlevel5.target -> graphical.target
lrwxrwxrwx. 1 root root 13  6月 16 22:40 runlevel6.target -> reboot.target

default.targetを切り替えてランレベルを制御する

詳細は後述するが、Systemdにおける起動の基点となるUnitはdefault.targetである。Systemdは起動の後、このdefault.targetを探し、default.targetに記載されたUnit間の関係を元に最終的に起動するプロセス群を把握する。

default.target自身は特に処理を持たず、任意のUnitへのシンボリックリンクとして利用するのが一般的だ。このシンボリックのリンク先を上述の起動後状態を表す.targetとすることで従来のランレベル変更に相当する操作を行う事ができる。

$ # multi-userモード(従来のRunlevel3)で起動する場合の設定
$ # default.targetのリンク先をmulti-user.targetとする
$ ll /etc/systemd/system/default.target
lrwxrwxrwx. 1 root root 37  6月 21 17:09 /etc/systemd/system/default.target -> /lib/systemd/system/multi-user.target

 

init、Systemdによるシステム起動の比較

次に従来のinitによるシステム起動とSystemdの起動を比較してみよう。
温故知新ということで、従来のinit起動後の動作について解説しておく。従来のinitの動作がSystemdではどのような振る舞いに置き換わるのか確認してみよう。

init/Upstartの起動プロセス

initは「1.init起動」した後、「2./etc/inittabを読み込み」、「3.rc.sysinitを実行」する。次に「4.inittabに記載されたランレベルに応じて、/etc/rc.d/rc[Runlevel].d内のスクリプトを実行」していく。各プロセスの起動が終了した後、ターミナルからの接続プロセスである「5.ログイン受付プロセスを起動」(mingettyなど)し、起動プロセスを完了する。まとめると以下のようになる。

initProcess

 

この起動プロセスはUpstartの場合であってもさほど変わりは無い。
initによる起動では、プロセスの起動順序を/etc/rc.d/rc[runlevel].d内のシンボリックリンク名によって制御していた。例えば/etc/rc.d/rc3.dは以下のようになっている。

$ pwd
/etc/rc.d/rc3.d

$ ls -l
lrwxrwxrwx. 1 root root 16  4月  8 20:02 2014 K01smartd -> ../init.d/smartd
lrwxrwxrwx. 1 root root 17  4月  8 20:00 2014 K02oddjobd -> ../init.d/oddjobd
lrwxrwxrwx. 1 root root 17  4月  8 20:03 2014 K05wdaemon -> ../init.d/wdaemon
lrwxrwxrwx. 1 root root 16  4月  8 20:02 2014 K10psacct -> ../init.d/psacct
lrwxrwxrwx. 1 root root 19  4月  8 20:00 2014 K10saslauthd -> ../init.d/saslauthd
lrwxrwxrwx. 1 root root 22  4月  8 20:00 2014 K15htcacheclean -> ../init.d/htcacheclean
# ============================= 一部抜粋 =============================
lrwxrwxrwx. 1 root root 17  4月  8 20:01 2014 S01sysstat -> ../init.d/sysstat
lrwxrwxrwx. 1 root root 22  4月  8 20:01 2014 S02lvm2-monitor -> ../init.d/lvm2-monitor
lrwxrwxrwx. 1 root root 19  4月  8 20:00 2014 S08ip6tables -> ../init.d/ip6tables
lrwxrwxrwx. 1 root root 18  4月  8 19:58 2014 S08iptables -> ../init.d/iptables
lrwxrwxrwx. 1 root root 17  4月  8 19:58 2014 S10network -> ../init.d/network
lrwxrwxrwx. 1 root root 16  4月  8 20:02 2014 S11auditd -> ../init.d/auditd
lrwxrwxrwx. 1 root root 21  4月  8 19:55 2014 S11portreserve -> ../init.d/portreserve
# ============================= 一部抜粋 =============================

ディレクトリ内の個々のファイルは、/etc/rc.d/init.d内にある起動スクリプトへのシンボリックリンクとなっている。また、シンボリックリンクの名称が「Kxxプロセス名」「Sxxプロセス名」となっており、この「xx」の部分で起動・停止順を定義している。(Kxxは該当ランレベルで停止するプロセス、Sxxは起動するプロセス)
先述したSystemdにおける、依存といった区分けは無く、単純に起動順のみが定義された状態である。

 Systemdの起動プロセス

Systemdの起動プロセスは以下の通りである。

SystemdProcess

 

initと比較するとプロセスの起動順をSystemd自体が決定していることが分かる。initの場合は/etc/rc.d/rc[runlevel].dディレクトリのシンボリックリンクの命名規則によって起動順を指定していたが、Systemdでは、Unitに定義されたUnit間の関係を元にSystemd側で起動順を決定する。
また、全てがUnitによって定義されているため、initのようなrc.sysinitやランレベルといった区分けは無く、システム起動に必要な処理を分け隔て無く管理できていることが分かる。

起動プロセスのゴール地点(default.target)からUnit間の関係を遡ることによって、システム起動に必要となる全てのUnitをSystemd側が把握することで、最適なシステム起動順によるシステム起動を実現している事が分かる。

 

まとめ

ご覧いただいたように、Systemdによるシステム起動のプロセスは従来のinit/Upstartとは大きく異なる。inittabやランレベルという考え方が無くなった事など、Systemdに初めて触れる方は戸惑ってしまう場面も多いかもしれない。

しかしSystemdは設定ファイルベースで様々な管理が行えるため、使い方を一度覚えてしまえば、様々な場面に対応することができるだろう。また、一度触れてみれば分かるが、Systemdを利用したシステム起動は非常に速い。RHEL7で最初に感動するポイントは起動時間かもしれない。

SystemdがRHELに正式採用されたことで、Systemdは更に重要な位置づけとなっていくだろう。この機会にSystemdに触れてみるのはいかがだろうか。

 

参考資料

Systemdに関する調査に、以下サイトの情報を参考とさせていただいた。どのサイトも非常に詳しい情報が記載されている。是非参考としていただければと思う。(皆様に感謝!)

Linux女子部 systemd徹底入門
http://www.slideshare.net/enakai/linux-27872553

systemd
http://www.slideshare.net/moriwaka/systemd

Systemd入門(1) – Unitの概念を理解する
http://d.hatena.ne.jp/enakai00/20130914/1379146157

ArchLinux – systemd(日本語)
https://wiki.archlinux.org/index.php/Systemd_(%E6%97%A5%E6%9C%AC%E8%AA%9E)

Systemd公式(英語)
http://www.freedesktop.org/wiki/Software/systemd/


Viewing all articles
Browse latest Browse all 10

Trending Articles