朝から昼寝

(ブログ準備中) IT技術、健康、日常

Systemdのネットワーク関連ユニット解説(After=network.target等)

概要

Systemdのユニットファイルの中にAfter=network.targetといった記述をよく見かけますが、ややこしい仕様なので、誤解の無いようネットワーク関連ユニットの概要を抑えておくことが大事です。
本記事は、Systemdのネットワーク関連ユニットの概要をまとめたものです。対象は主にRHEL7、CentOS7以降です。
なお、Systemdの基本的なサービス管理については、こちらの記事にまとめてあります。

本記事の目的

  • After=network.targetの意味を把握する。
  • ネットワークを待ってサービス起動する設定(network-online.target)を把握する。
  • ネットワーク関連ユニットの概要を把握する。

目次

基本

After=network.target ≠ ネットワーク疎通可

サービスのユニットファイルにAfter=network.targetが記載されていても、それは「ネットワーク疎通できるようになった後にそのサービスを起動する」ということを保証するものではありません。
例えば、httpdはユニットファイルにAfter=network.targetが記載されていますが、httpd.confにて特定IPアドレスのみをListen指定していると、OS起動時に、そのIPアドレスが使用可能になる前にhttpdサービスを起動し始めてしまい、Cannot assign requested address: AH00072: make_sock: could not bind to address x.x.x.x:443といったエラーで起動失敗することがあります。postfixのinet_interfacesの指定でも同様の事象が起きることがあると思います。

ネットワーク疎通できるようになった後にそのサービスを起動したい場合は、 network-online.targetというユニットとの順序関係を指定します。

順に説明していきます。

ネットワークを待ってサービス起動する設定(network-online.target)

ネットワーク疎通(※)ができるようになってからサービスを起動する設定について説明します。
(※)本記事では、ネットワークインタフェースの設定(IPアドレス割り当てなど)が完了した状態を指します。そのIPアドレスに対するソケット割り当て(バインド)ができる状態です。

元のユニットファイルを直接編集せず、以下のようにdrop-in(別の設定ファイルを追加)を利用します。例としてhttpdの場合の手順を記載しますが、他のサービスでも同様です。drop-inの詳細はman systemd.unitで確認できます。

# systemctl edit httpd にて、以下を記載します。

[Unit]
After=network-online.target
Wants=network-online.target

その後、
# systemctl daemon-reload
を実行してsystemdに読み込ませると、設定完了です。
httpdはネットワーク疎通できるようになってからサービス起動するようになります。

詳細は、後述します。

詳細

drop-inの管理TIPS

drop-inを設定すると、以下のようにdrop-inファイルが作成されます。インストール時に配置された元のユニットファイルを変更せず、別ファイルに設定を追加する形です。
$ cat /etc/systemd/system/httpd.service.d/override.conf

[Unit]
After=network-online.target
Wants=network-online.target

drop-inが存在していることは、以下のようにsystemctl statusの結果にDrop-In:行が表示されることでも確認できます。 $ systemctl status httpd

● httpd.service - The Apache HTTP Server
   Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; vendor preset: disabled)
  Drop-In: /etc/systemd/system/httpd.service.d
           └override.conf

drop-inを修正したいときは、systemctl editで可能です。
なお、drop-inを削除したいときは、編集画面で中身を空にして保存してもキャンセルされてしまい削除できないので注意が必要です。
# systemctl edit httpd

(drop-inファイルを空にして保存しようとした場合)
Editing "/etc/systemd/system/httpd.service.d/override.conf" canceled: temporary file is empty.

drop-inを削除したいときは、以下のようにsystemctl revertを利用可能です。ただし、必要なファイルも削除されないか注意が必要です。
# systemctl revert httpd

Removed /etc/systemd/system/httpd.service.d/override.conf.
Removed /etc/systemd/system/httpd.service.d.

systemctl revertにより、指定したユニットのdrop-inファイルが削除されますが、他に手動で修正したユニットファイルも削除(※)されるようですので注意が必要です。systemctl revertではどうしても支障がある場合は、手動でdrop-inファイルを削除する方が無難でしょう。
(※)詳細な動作は確認していませんが、/usr/lib/配下のオリジナルのユニットファイル(vendor-supplied)を/etc/system/system配下等にコピーしてから修正した同名のユニットファイルがあればそれが削除され、オリジナルのユニットファイルのみが存在する状態に戻るようです。なお、オリジナルのユニットファイルが無く、自作したユニットファイルが1つあるだけであれば、それは削除されずに残るはずです。

drop-inで追加した設定が反映されていることは、systemctl showでも確認できます。 $ systemctl show httpd

…
After=remote-fs.target nss-lookup.target system.slice systemd-tmpfiles-setup.service httpd-init.service tmp.mount systemd-journald.socket basic.target sysinit.target -.mount network-online.target network.target
(改行されず見切れるので続きを見る場合は → キーで最後まで見られます)
…

ネットワーク関連ユニット

では、After=network.targetの意味や、関連する特殊ユニットについて説明します。

概要

システム起動時、以下のnetwork-pre.targetから順にユニットが起動されます。

  1. network-pre.target
    ↑After
  2. NetworkManager.service
    ↓Before(,Wants)
  3. network.target (After=network-pre.target含む)
    ↑ Requires
  4. NetworkManager-wait-online.service (After=NetworkManager.service含む)
    ↓ Before(,WantedBy)
  5. network-online.target (After=network.target含む)

※NetworkManager-dispatcher.serviceについては省略

グレーの矢印 (↑、↓) や補記は、矢印の元のユニットファイルにおいてBefore,After等の順序関係が矢印の先のユニットに対して指定されていることのメモです。依存関係(Wants,Requires等)も補記してあります。

「network~」はsystemdにおけるネットワーク関連の特殊ユニット(詳細はman systemd.special)、「NetworkManager~」はOS標準のネットワーク管理サービスであるNetworkManager関連のユニットです。

systemctl list-dependenciesでユニットの依存関係を確認してみると、システム起動時には、NetworkManager.serviceが起動するよう指定されていることが分かります(NetworkManager.serviceのユニットファイルでは、WantedBy=multi-user.targetが指定されています)。

$ systemctl list-dependencies

default.target
…
● ├NetworkManager.service
…

また参考までに、各ユニットは以下のように有効化(enabled)、もしくは特殊ユニットの場合はstaticになっていることが分かります。

# systemctl list-unit-files | grep NetworkManager

NetworkManager-dispatcher.service                                      enabled
NetworkManager-wait-online.service                                     enabled
NetworkManager.service                                                 enabled

# systemctl list-unit-files | grep network

network-online.target                                                  static
network-pre.target                                                     static
network.target                                                         static

次に、各ユニットの説明をします。

1. network-pre.target

特殊ユニットの1つで、サービスの起動順序の制御等に使用されます。
ネットワークインタフェースの設定前に実行されるユニットです。

なお、network-online.targetは、network.targetと同様にパッシブユニットです。パッシブユニットについては後述のnetwork.targetの箇所で説明します。

2. NetworkManager.service

特殊ユニットではなく、通常のユニットです。
NetworkManagerのメインのユニットです。ネットワークインタフェースの設定処理等を実行します。

3. network.target (重要)

特殊ユニットの1つで、サービスの起動順序の制御等に使用されます。

After=network.targetが指定されたユニット(サービス)の起動は、ネットワーク疎通可能になるまで遅延される訳ではありません。
systemdがnetwork.targetを起動すると、ネットワークの設定を行うサービスが開始した状態にはなりますが、それはネットワークデバイスの設定(IPアドレス設定等)までが完了した状態ではありません。
そのため、After=network.targetが指定されたユニットは、OS起動時にはnetwork.targetの起動後に自身のサービスを起動し始めるのですが、まだその時点ではIPアドレスが使用可能になっていないこともあるので、その場合にはソケット割り当て(バインド)が失敗してしまいます。

では、何のためにAfter=network.targetが指定されているサービスがあるのかと言うと、それは起動時でなく停止時の順序制御のためのようです。
After=network.targetが指定されたユニットが停止するまで、network.targetの停止処理が開始されることはありません。
つまり、自身が停止するまでネットワークが起動していないといけないサービスには、After=network.targetを指定しておくべきということになります。
このように、停止時の順序の話になると、「After」という単語の意味とは逆の働きをするので、ややこしいです。

なお、network.targetはパッシブユニットなので、通常のサービス(consumer)において、After=network.targetのように順序関係を指定しても良いですが、Wants=network.targetRequires=network.targetのように依存関係を指定してはいけません。
パッシブユニットに関する詳細な記述が見つけられませんでしたが、パッシブユニットのユニットファイルには、RefuseManualStart=yesが記載されているようです。

4. NetworkManager-wait-online.service

特殊ユニットではなく、通常のユニットです。
これもNetworkManager関連のユニットです。

NetworkManager-wait-online.serviceは、後述のnetwork-online.targetより先に起動します。After=network-online.targetを設定したときに実質的なネットワーク起動完了を判定するのが、このNetworkManager-wait-online.serviceであると思われます。

NetworkManager-wait-online.serviceのユニットファイルには、Before=network-online.targetが記載されています。
NetworkManager-wait-online.serviceが起動完了(=ネットワーク起動完了)してから、network-online.targetの起動が開始されるということです。

5. network-online.target (重要)

特殊ユニットの1つで、サービスの起動順序の制御等に使用されます。

network.targetの場合とは異なり、After=network-online.targetが指定されたユニットの起動は、ネットワーク疎通可能になるまで遅延されます。
前述のNetworkManager-wait-online.serviceがネットワーク起動(IPアドレス設定等)が完了したことを判定後、network-online.targetが起動完了となります。その後にAfter=network-online.targetが指定されたユニットが起動開始されますので、確かにネットワーク疎通可になってから起動できます。
network-online.target自体のタイムアウトは90秒のようです。

なお、補足事項として、systemd開発プロジェクトをホストしているらしいfreedesktop.orgの説明ページによると、ネットワークを利用するサーバソフトウェアのサービスにおいてAfter=network-online.targetを多用しないことが強く推奨されているようです。これは、例えばサーバソフトウェアはネットワーク起動前であってもローカルコネクションを受け付ける方が一般的には望ましく、そもそもAfter=network-online.targetの主目的がネットワーク無しでは動作できないクライアントソフトウェアのためにあると説明されています。
また、開発者向けに、ネットワーク設定が動的に変わっても動作するようプログラムを修正することを勧めています。
ただ、実際には例えばhttpd自動起動失敗を回避するためAfter=network-online.targetを指定したいケースもあるので、システム管理者からすると、なかなかこの思想の通りに運用するのは難しいところです。

以下、freedesktop.orgの説明ページより引用。

It is strongly recommended not to pull in this target too liberally: for example network server software should generally not pull this in (since server software generally is happy to accept local connections even before any routable network interface is up), its primary purpose is network client software that cannot operate without network.

If you are a developer, instead of wondering what to do about network.target, please just fix your program to be friendly to dynamically changing network configuration.
https://www.freedesktop.org/wiki/Software/systemd/NetworkTarget/

ちなみに、network-online.targetは、network.targetとは異なりパッシブユニットではありません。通常のサービス(consumer)において、After=network.targetのように順序関係を指定する以外に、Wants=network.targetRequires=network.targetのように依存関係を指定できます。
前述の設定例では、After=network.targetWants=network.targetの両方を指定しています(freedesktop.orgの説明ページの設定例と同等)。

(参考)NetworkManager-dispatcher.service

特殊ユニットではなく、通常のユニットです。
これもNetworkManager関連のユニットで、dispatcherはネットワークイベントが発生した場合に、スクリプトを実行するもののようです。
詳細は省略します。

参考URL等