めもめも

このブログに記載の内容は個人の見解であり、必ずしも所属組織の立場、戦略、意見を代表するものではありません。

Systemd入門(4) - serviceタイプUnitの設定ファイル

この連載では、Fedora 17での実装をベースとして、systemdの考え方や仕組み、利用方法を説明します。今後出てくる予定のRHEL7での実装とは異なる部分があるかも知れませんが、その点はご了承ください。

今回は、serviceタイプのUnitについて、設定ファイルの書き方を説明します。

Unit設定ファイル

参考資料
systemd.unitのmanページ:設定ファイルの一般的な説明
systemd.serviceのmanページ:serviceタイプUnitの設定オプションの説明

Systemd入門(1) - Unitの概念を理解する」で説明したように、各Unitの設定ファイルは、/usr/lib/systemd/system/以下と/etc/systemd/system/以下にあります。両方のディレクトリに同名の設定ファイルがある場合は、後者(/etc/systemd/system/)のファイルが優先されます。前者(/usr/lib/systemd/system/)には、RPMパッケージが提供するシステム標準の設定を配置しておき、システム管理者が設定変更する場合は、後者にファイルをコピーした上で修正を加えます。

Unitの設定ファイルは、[Unit]、[Install]、「Unitのタイプに固有のセクション(serviceタイプであれば[Service]など)」の3つのセクションに別れており、それぞれ、次のような役割を持ちます。

セクション 説明
[Unit] Unitの依存関係/順序関係など、Unitのタイプに依存しない設定項目
[Install] 「systemctl enable/disable」コマンドの動作に影響する設定項目
タイプ固有セクション そのタイプに固有の設定項目

ここでは、serviceタイプを前提として、各セクションの主なオプションを説明していきます。

[Unit]セクション

主なオプションは次のとおりです。

オプション 説明
Description Unitの説明文
Documentation ドキュメントのURI
Requires このUnitが必要とする前提Unit
Wants このUnitが必要とする前提Unit
After このUnitより先に起動するべきUnit
Before このUnitより後に起動するべきUnit

オプションに複数項目を指定する場合は、スペース区切りとします。同じオプションを複数回に分けて記載しても構いません。「Requires」と「Wants」は、このUnitの前提として必要となる他のUnitを指定します。「Requires」の場合は、指定のUnitが起動に失敗すると、このUnitも起動に失敗します。一方、「Wants」の場合は、指定のUnitが起動に失敗しても、このUnitの起動を継続します。

「After」と「Before」は、Unitの起動順序を指定します。「Systemd入門(1) - Unitの概念を理解する」で説明したように、Unitの依存関係と起動順序は別の概念なので注意が必要です。「Requires/Wants」は、このUnitと同時に起動されるべきUnitを指定しますが、どちらが先に起動するかは限定していません。systemdが起動すると、はじめに「default.target」から「Requires/Wants」の関係をたどって、起動するべきUnitの全体を把握します。その後、あらためて、起動対象の各Unitの「After/Before」の依存関係から起動順序を決定します。起動順序に依存関係がないUnitはすべて並列に起動処理が行われます。

「Requires/Wants」の指定は、設定ファイル内のオプションの他に、「<設定ファイル名>.reqruires」および「<設定ファイル名>.wants」というディレクトリを作成して、その中に他のUnitの設定ファイルへのシンボリックリンクを作成することでも行うことができます。これはとくに、「Systemd入門(2) - Serviceの操作方法」で説明したように、「systemctl enable/disable」コマンドで依存関係を変更する際に利用されます。

[Install]セクション

主なオプションは次のとおりです。

オプション 説明
WantedBy enable時にこのUnitの.wantsディレクトリにリンクを作成する
RequiredBy enable時にこのUnitの.requiredディレクトリにリンクを作成する
Also enable/disable時に同時にenable/disableするUnit
Alias enable時にこのUnitの別名を用意

「WantedBy/RequiredBy」は、「systemctl enable」コマンドで自動起動を有効化した際に、どのUnitの前提として設定されるべきかを指定します。たとえば、「WantedBy=multi-user.target」とした場合、「multi-user.target.wants」ディレクトリ内にこの設定ファイルへのシンボリックリンクが作成されます。一般的なサービスの場合は、multi-user.target(旧来のrunlevel 3に相当)、もしくは、graphical.target(runlevel 5に相当)を指定します。

「Alias」は、少し特殊な動きをします。たとえば、「hoge.service」において、「Alias=fuga.service」と指定すると、「hoge.service」の自動起動を有効化した際に、hoge.serviceの設定ファイルに対してシンボリックリンク「fuga.service」が作成されます。

# systemctl enable hoge.service
ln -s '/etc/systemd/system/hoge.service' '/etc/systemd/system/fuga.service'
ln -s '/etc/systemd/system/hoge.service' '/etc/systemd/system/multi-user.target.wants/hoge.service'

これにより、hoge.serviceは、fuga.serviceという別名でも参照できるようになります。ただし、hoge.serviceの自動起動を無効化すると、シンボリックリンクは削除されて、fuga.serviceという別名は無くなります。

# systemctl disable hoge.service
rm '/etc/systemd/system/fuga.service'
rm '/etc/systemd/system/multi-user.target.wants/hoge.service'
[Service]セクション

主なオプションは次のとおりです。

オプション 説明
ExecStart サービス起動コマンド
ExecReload サービスリロードコマンド
ExecStop サービス停止コマンド
ExecStartPre/ExecStartPost サービス起動前後の追加コマンド(サービス起動判定には関連させたくないコマンドを記載)
ExecStopPost サービス停止後に実行するコマンド(サービスが異常停止した際にも実行される)
EnvironmentFile 環境変数を読み込むファイル
Type サービスプロセスの起動完了の判定方法(デフォルトは「simple」)
PIDFile fork型サービスのメインプロセスのPIDファイル
BusName D-Bus型サービスのbus接続名
Restart サービスプロセス停止時の再起動条件(デフォルトは「no」)
PrivateTmp このサービス専用の/tmpと/var/tmpを用意する

「ExecStart, ExecReload, ExecStop, ExecStartPre/ExecStartPost, ExecStopPost」には、サービスを起動、リロード、停止する際の実行コマンドを指定します。「EnvironmentFile」で指定のファイルから読み込んだ環境変数に加えて、後述の特殊環境変数「$MAINPID」を利用することができます。

「Type」は、「ExecStart」で指定したコマンドでサービスのプロセスを起動した際に、起動完了をどのように判定するかを指定します。たとえば、フォアグラウウンドで実行を継続するコマンドであれば、「Type=simple」とします。これは、コマンドを実行したタイミングで起動完了と判断します。あるいは、子プロセスをフォークしてバックグラウンドにまわして、最初のコマンド自体は終了するタイプであれば、「Type=forking」とします。この場合は、実行したコマンドが終了した時点で起動完了と判断します。

フォアグラウンドで実行するタイプの場合、systemdから実行したコマンドのPIDが「メインプロセス」のPIDとして認識されます。メインプロセスのPIDは、設定ファイル内において環境変数「$MAINPID」として参照できます。「ExecReload」に対してメインプロセスにHUPシグナルを送るコマンドを記載する際になどに使用できます。

一方、フォークするタイプのコマンドの場合、フォークの形式によってどのプロセスがメインプロセスとなるかが変わるため、systemdはメインプロセスのPIDを自動判定することができません。多くの場合、このタイプのコマンドは、メインプロセスのPIDを記載したPIDファイルを生成しますので、それを「PIDFile」に指定すると、systemdはこのファイルからPIDを認識します。

デーモン型のプロセスではなく、一度だけ指定のコマンドを実行するタイプのサービスについては、「Type=oneshot」とすると、「ExecStart」で指定したコマンドを実行して、それが完了したタイミングで起動完了(かつサービス終了)と判断します。コマンドが完了したあともサービスとしては起動状態として認識させたい場合は、「RemainAfterExit=yes」を指定します。このタイプについては、「Type=simple」とすることも可能ですが、この場合、systemdは、コマンドの実行を開始したタイミングで起動完了と判断します。

最後に、フォアグラウンドで実行するタイプの中でも、D-Busを利用するサービスについては、特別な設定が可能です。D-Busは、systemdの環境で標準となるプロセス間通信用のメッセージバス機能です。D-Busを利用するサービスは、起動後に固有の「bus接続名」を指定して、D-Busに接続を行います。「Type=dbus」とした上で、「BusName」にこのサービスのbus接続名を指定すると、systemdは、D-Bus上にこの接続名の接続が現れたタイミングで、サービスが起動したものと判定します。

参考資料
IntroductionToDBus:D-Busの概要説明

「Restart」は、旧来の「respawn」にあたる設定で、サービスのメインプロセスが停止した際の動作を指定します。「Restart=always」は、常に再起動を試みます。「no」は、再起動を行いません。「on-success」は終了コード0で停止した際に再起動します。逆に「on-failure」は、0以外の終了コードで停止した際に再起動します。デフォルトでは、10秒間の間に5回以上再起動すると、次の10秒間は再起動を試みません。(このタイミングは、「StartLimitInterval」「StartLimiBurst」で設定変更が可能です。具体的には、「StartLimitIntervalの間にStartLimiBurst回以上再起動すると、次のStartLimitIntervalの間は再起動を試みません」となります。)

最後に、systemd特有の設定に「PrivateTmp」があります。サービスを構成するプロセス間で情報をやりとりするために、/tmpにファイルをおいて共有することがありますが、/tmpディレクトリは、サービスとは関係ないプロセスからも参照できるためセキュリティの観点で問題となる可能性があります。この場合、「PrivateTmp=true」とすると、「ファイルシステムのnamespace機能」を用いて、このサービス専用の独立した/tmpと/var/tmpが用意されます。イメージとしては、/tmpと/var/tmpだけがchrootされた感じでしょうか。

設定ファイルのサンプル

Fedora 17の環境から、典型的な設定ファイルのサンプルを紹介しておきます。まずは、sshd.serviceです。

/usr/lib/systemd/system/sshd.service

[Unit]
Description=OpenSSH server daemon
After=syslog.target network.target auditd.service

[Service]
EnvironmentFile=/etc/sysconfig/sshd
ExecStartPre=/usr/sbin/sshd-keygen
ExecStart=/usr/sbin/sshd -D $OPTIONS
ExecReload=/bin/kill -HUP $MAINPID

[Install]
WantedBy=multi-user.target

「ExecStartPre」でホストキーの生成を行っています。「ExecStart」では、「-D」オプションでフォアグラウンドモードを指定している点に注意してください。systemdの環境では、フォーク型のデーモンよりも(systemdがPIDを追跡できるという意味で)フォアグラウンド型のプロセスの使用が推奨されます。「Type」の指定がありませんが、これはデフォルトの「simple」となります。

次は、httpd.serviceです。

/usr/lib/systemd/system/httpd.service

[Unit]
Description=The Apache HTTP Server (prefork MPM)
After=syslog.target network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
PIDFile=/var/run/httpd/httpd.pid
EnvironmentFile=/etc/sysconfig/httpd
ExecStart=/usr/sbin/httpd $OPTIONS -k start
ExecReload=/usr/sbin/httpd $OPTIONS -t
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/usr/sbin/httpd $OPTIONS -k stop
PrivateTmp=true

[Install]
WantedBy=multi-user.target

こちらは、古典的なフォーク型のデーモンになります。「Type=forking」で、「PIDFile」には、httpdが生成するPIDファイルを指定しています。「PrivateTmp=true」で、/tmpのセキュリティ保護を図っています。