CentOS 7 が起動(Boot)する仕組み

メモ:  Category:centos

多くの方が残してくださっている情報を見ながら、なんとなく環境を構築することはできるのですが実際に運用していくにあたり、もう少し深い部分に関して知識を増やしていきたいと思います。Plamo Linuxの時も調べてみたのですが、CentOS 7が起動するまでの流れについて確認していきます。CentOS 7 で導入された Systemd を中心に見ていきます。

ざっくりになりますが、電源投入から起動までの流れは次のようなイメージとなります。

起動順のイメージ

Systemd とは

Systemd とは CentOS 7で採用されたinitシステム(プロセス)で、従来のSysVinitやUpstartの代替となるシステム管理デーモン、ライブラリおよびユーティリティの一式になります。Systemd は、プロセスの起動だけではなくプロセスの管理やトリガーによるプロセス起動など、多くのことを受け持ちます。Systemd には、主に次の特徴があります。

システム起動時間の高速化
従来の仕組みは、シェルスクリプトがベースになっておりシリアルに実行されるため効率が良いとは言えなかった。これを解決するため、並列で実行することを可能にし起動時間を短縮。
設定ファイルによるシステム管理の共通化
シェルスクリプトを利用しないで、設定ファイルによる管理
柔軟なプロセス起動
タイマーによるプロセス起動やsocketへの通信検出によるプロセス起動、所定のパスへのファイル作成をトリガーとしたプロセス起動などが可能
cgroupsによるプロセス管理
cgroupsによるリソースの利用制限や、優先度設定によるプロセス管理

/sbin/init はどうなった?

カーネルがロードされると最初のプロセスとして /sbin/initが実行されていたのですが、CentOS 7 ではどうなっているのでしょうか?Kernelまでの流れは、変わっていないので /sbin/init を確認してみると systemd へのシンボリックリンクとなっています。

$ ls -la /sbin/init 
lrwxrwxrwx. 1 root root 22  2月 14  2017 /sbin/init -> ../lib/systemd/systemd

従来の起動方法から変更された部分は、Systemd の部分であるため Systemd についてより詳しく見ていきます。

システムの初期化やサービスの実行

Systemd が実行されると、default.target というファイルが読み込まれ default.target から紐解ける定義に従ってサービスなどを起動します。 この default.target は、Unit と呼ばれるものの一つでこの Unit に定義された内容を解析し必要なプロセスを実行していきます。

Unit という考え

init/Upstartでは、システムを起動するために必要な処理は、rc.sysinit や /etc/rc.d のスクリプトで定義されておりサービスについての動作が一つのシェルスクリプトの中に連続した処理として書かれていました。 Systemdでは、サービスを1つのシェルスクリプトではなく Unit という単位で管理し、設定ファイルとして持ちます。Unitは、個別に実効することが可能で設定ファイルには順序や依存関係の情報を持ちます。設定ファイルに基づいて Unit 単位に実行出来ることから、並列での実行も可能になり。例えばAの処理の後にBとCの処理を並列に実行する、というような処理が可能になります。

例えば、ssh の Unit である sshd.service は次のように定義されており、スクリプトではないことが確認できます。

$ cat /usr/lib/systemd/system/sshd.service
[Unit]
Description=OpenSSH server daemon
Documentation=man:sshd(8) man:sshd_config(5)
After=network.target sshd-keygen.service
Wants=sshd-keygen.service

[Service]
Type=forking
PIDFile=/var/run/sshd.pid
EnvironmentFile=/etc/sysconfig/sshd
ExecStart=/usr/sbin/sshd $OPTIONS
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartSec=42s

[Install]
WantedBy=multi-user.target

Unit が保存されている場所と種類

設定ファイル(Unit)が保管されている場所は、次の2か所になります。

ディレクトリ 主な用途
/usr/lib/systemd/system インストール時の初期設定が保存されます。
/etc/systemd/system ユーザによる個別の設定を保存します。
Systemd では、このディレクトリが優先されるため、デフォルトの設定を変更する場合、 このディレクトリにUnitファイルをコピーし編集します。

「/usr/lib/systemd/system」にあるファイルを見てみると、様々な拡張子のファイルがあることがわかります。Unit は、用途に応じて様々な設定ができるようになっており拡張子によって区別されます。

Unitの種類 主な用途
.service プロセスの起動/停止に関する設定(ファイル名:プロセス名.service)
.mount ファイルシステムのマウント・アンマウントに関する設定
.socket ソケットの監視設定(ソケットへの接続を検出すると特定のプロセスに受け渡すといった設定ができます。)
.device システムが認識しているデバイス情報を保持(udevから通知されたデバイス)
.path パスの監視設定(指定のファイルが作成されたらサービス起動するといった設定ができます。)
.target 複数のUnitをとりまとめるUnit

ここまでをまとめると、

  • Systemd がUnit(設定ファイル)に応じて処理を実行する
  • Unit は、用途に応じて様々な種類が存在する
  • Unit の保管場所は2か所あり同じファイルがある場合は、/etc/systemd/systemが優先される
  • Systemd では、プロセスの実行を並行して実行できるらしい

Unit の依存関係

Systemd では、起動時間が短くなるよう処理を並列で実行できることが一つの特徴ですが、プロセスがどの順番に起動すべきなのかといった部分はどうなっているのでしょうか?

この問題を解決するためにSystemd では、Unitの関係に「依存」と「起動順」という設定があります。

依存関係を定義方法するには、.wantsディレクトリを使うか、設定ファイル内の[Unit]セクションにおいて、「Wants=」や「Requires=」オプションを指定する方法があります。.wantsディレクトリを使う場合、ある設定ファイルに対して、「設定ファイル名.wants」というディレクトリ内に前提となるUnitの設定ファイルへのシンボリックリンクを作成することで、依存関係が定義されます。

一般的な考えとしてシステム的に必須の依存関係は、設定ファイルの「Wnats/Requires=」オプションに定義しておき、システム管理者が環境に応じて設定したい依存関係は、.wantsディレクトリを使用するようです。特に、後述のsystemctlコマンドで、サーバ起動時に自動起動するサービスを設定した場合は、.wantsディレクトリにシンボリックリンクを作成することで、自動起動が設定されます。

依存とは

依存とは、プロセス同士が同時に起動するべきかどうか定義します。依存設定には、「Wants(可能な限り同時に起動)」、「Requires(必ず同時に起動)」、「Conflict(同時起動しない)」などがあります。

例えば、プロセスBはプロセスAと同時に起動されるべき(依存)プロセスの場合、プロセスAが起動対象となった場合にプロセスBも同時に起動するよう処理されます。

設定の種類 内容 Unit設定ファイルに設定する方法 ディレクトリを使う方法
Wants 可能な限り同時に起動。依存先の起動に失敗した場合であっても、依存元の起動は継続されます。 AのUnit設定ファイルに次のように記述
[Unit]
Wants=B.service
A.serviceファイルが存在するディレクトリ上に「A.service.wants」ディレクトリを作成し、このディレクトリ内にB.serviceへのシンボリックリンクを作成。
Requires 必ず同時に起動。依存先の起動に失敗した場合、依存元は起動しません。 AのUnit設定ファイルに次のように記述
[Unit]
Requires=B.service
A.serviceファイルが存在するディレクトリ上に「A.service.requires」ディレクトリを作成し、このディレクトリ内にB.serviceへのシンボリックリンクを作成。
Conflict 同時起動しない。この関係が定義されたプロセスは同時起動しません。 AのUnit設定ファイルに次のように記述
[Unit]
conflicts=A.service
-

起動順とは

起動順とは、プロセスが起動する順番を定義します。起動順の設定には、「After」、「Before」などがあります。

例えば、「プロセスAはプロセスBよりも先に起動しなければならないよ」といった順番を定義することができます。

設定の種類 内容 Unit設定ファイルに設定する方法
After 自身よりも前に起動するプロセスを定義。 AのUnit設定ファイルに次のように記述「B.serviceより後にA(自分)を起動してねという設定」
[Unit]
After=B.service
Before 自身よりも後に起動するプロセスを定義。 AのUnit設定ファイルに次のように記述
[Unit]
Before=B.service

複数のUnitをとりまとめる「target」(default.targetを見てみる)

なんとなく、 Unit にはシステムを起動するために必要な定義が用途別に用意され「依存関係」や「起動順」が指定できることがわかりました。次に Systemd が最初に読む default.target ファイルを見てみます。

target という Unit は、複数のUnitをとりまとめることができます。例えば、最初に読まれる default.target やネットワーク関連の Unit をとりまとめた network.target 等があります。

次の例は、「/etc/systemd/system/」ディレクトリにある default.target ファイルの内容になります。default.target は、「/usr/lib/systemd/system/multi-user.target」へシンボリックリンクが張られていますので、実体は multi-user.target になります。

$ less default.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=Multi-User System
Documentation=man:systemd.special(7)
Requires=basic.target
Conflicts=rescue.service rescue.target
After=basic.target rescue.service rescue.target
AllowIsolate=yes

default.target の例では、「Requires=basic.target」と設定されています。これにより、systemd は「/usr/lib/systemd/system/basic.target」に含まれるものをすべて開始します。その後、/etc/systemd/system/multi-user.target.wants および /usr/lib/systemd/system/multi-user.target.wants 内のすべてのユニット (サービス、ターゲット等) を開始します。

basic.target の中では、さらに sysinit.target を要求し定義に従って順に起動していきます。

CentOS が起動するまでのおさらい

ここまでをまとめると、次のような順序で実行されることが確認できました。

  1. 電源の投入
  2. BIOS
  3. ブートローダーが実行され
  4. kernelが読み込まれ
  5. Systemd(/sbin/init)が実行される
  6. default.target(multi-user.target)から順次解析され
  7. Unitの定義に従い処理が行われる

Unit に応じて処理されるイメージは、次のようになります。

SystemdがUnitに応じて処理するイメージ

bluenote by BBB