朝から昼寝

ITや家計、身近なことの整理

[お知らせ]
当サイトは、2024年1月1日に
新URL(happynap.net)に引っ越しました





このページの内容は、最新ではない場合がありますので、新URLをご確認ください。






当サイトには広告を含みます。"オススメ"として紹介している商品やサービスは、個人的にそう思えたものだけです。
共感、興味をもっていただけるものがあればご利用ください (広告掲載ポリシー)。

Systemdのネットワーク関連ユニット (After=network.targetあたり)

概要

Systemdのユニットファイルの中にAfter=network.targetという記述をよく見かけますが、ちょっとややこしい仕様です。

本記事は、Systemdのそのあたりのネットワーク関連ユニットの概要をまとめたものです。対象は主にRHEL7、CentOS7以降です。

Systemdに関する基本的な内容は、以下の記事にまとめてあります。

happy-nap.hatenablog.com

本記事の目的

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

基本

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

サービスのユニットファイルにAfter=network.targetが記載されていても、それは「ネットワーク疎通できるようになった後にそのサービスを起動する」ということを保証するものではありません。

httpdやpostfixが起動失敗するケース

例えば、httpd サービスのユニットファイル (httpd.service) には、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の指定でも同様の事象が起きることがあります (fatal: parameter inet_interfaces: no local interface found for x.x.x.x)。

network.target でなく network-online.target

ネットワークの起動が完了した後にそのサービスを起動したい場合は、network-online.targetというユニットとの順序関係を指定します。

順に説明していきます。

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

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

元のユニットファイルを直接編集しても良いですが、保守性を考慮し、以下のようにドロップインスニペット (drop-in file、別の設定ファイル) を利用します。ユニットファイルのカスタマイズの全般的なことについては、以下の記事にまとめてあります。

happy-nap.hatenablog.com

例としてhttpdの場合の手順を記載しますが、他のサービスでも同様です。

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

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

エディタはデフォルトでvimでなくnanoが起動します(参考1参考2)。
nanoでは、上記を記載後、Ctrl+"S"(上書き保存)→Ctrl+"X"(閉じる)で保存と終了ができます。もしくは、Ctrl+"X"(閉じる)→保存するか聞かれたら"Y"(はい)→ファイル名の確認でEnterでも同様です。

# systemctl edit httpdにて設定変更した際は、# systemctl daemon-reloadを実行しなくても自動的にsystemdが再読み込みしてくれるようです。
(ユニットファイルを手動変更した場合には、systemdに再読み込みさせるため# systemctl daemon-reloadが必要)

この設定により、httpdはネットワーク疎通できるようになってからサービス起動するようになります。

詳細は、後述します。

詳細

ドロップインスニペット (drop-in file) の管理TIPS

ドロップインスニペットを設定すると、以下のようにファイルが作成されます。インストール時に配置された元のユニットファイルを変更せず、別ファイルに設定を追加する形です。

$ cat /etc/systemd/system/httpd.service.d/override.conf

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

ドロップインスニペットの存在確認

あるユニットにドロップインスニペットが存在していることは、以下のように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

ドロップインスニペットの修正

ドロップインスニペットを修正したいときは、前述の手順と同様に、systemctl editで可能です。

ドロップインスニペットの削除

ドロップインスニペットを削除したいときは、編集画面で中身を空にして保存してもキャンセルされてしまい削除できないので注意が必要です。

# systemctl edit httpd

(ドロップインスニペットのファイルを空にして保存しようとした場合)
Editing "/etc/systemd/system/httpd.service.d/override.conf" canceled: temporary file is empty.

ドロップインスニペットを削除したいときは、以下のようにsystemctl revertを利用できます。ただし、必要なファイルも削除されないか注意が必要です。
# systemctl revert httpd

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

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

ドロップインスニペットによる設定の反映確認

ドロップインスニペットで追加した設定が反映されていることは、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-pre.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はパッシブユニットです。パッシブユニットについては後述します。

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秒のようです。

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

以下、開発元のページより引用。

It is strongly recommended not to make use of 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.

 
出典:systemd.io

ちなみに、network-online.targetは、network.targetとは異なりパッシブユニットではありません。

(参考)NetworkManager-dispatcher.service

特殊ユニットではなく、通常のユニットです。

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

(補足) Systemdの用語

※公式ドキュメントにハッキリとした定義が見当たらないので、私が理解している範囲でまとめています。

passive unit

パッシブユニットとアクティブユニットに関する簡単な説明です (開発元のドキュメント)。

パッシブユニットは、直接起動 (systemctl start) することはできません。他のサービス (provider) によって依存関係が指定される場合があります。通常、他のサービス (consumer) によって依存関係は指定されません。これには、パッシブユニットがトランザクションの一部でなく、順序のために存在するという理由があるようです。

例えば、パッシブユニットであるnetwork.targetは、consumer によって After=network.target のように順序関係を指定される場合がありますが、Wants=network.targetRequires=network.targetのように依存関係は通常指定されません。

一方、非パッシブユニット (通常のユニット?アクティブユニット?) については、そのようなことは無いようです。

例えば、非パッシブユニット (active target?) である network-online.targetは、consumer によって Wants=network.targetRequires=network.targetのように依存関係を指定される場合があります。順序関係を指定される場合もあります。
実際、前述の設定例では、httpd (consumer) において、After=network.targetWants=network.target の両方を指定しています(開発元の設定例と同等)。

なお、パッシブユニットのユニットファイルには、RefuseManualStart=yesが記載されているようです (明確な説明はありませんが)。

provider、consumer

前述のproviderとconsumerについても、公式ドキュメントに詳細な記述が見当たらないのですが、基本的には、あるユニットが他のユニットを利用する場合、利用する側のユニットがconsumer、利用される側のユニットがproviderという意味のようです (つまり文脈依存)。

例えば、systemd-resolved というユニットが、他のパッケージによって管理されている /etc/resolv.conf 内の設定を参照するケースでは、systemd-resolved は provider でなく consumer となるようです (開発元のドキュメント)。

参考URL等

関連記事

happy-nap.hatenablog.com

happy-nap.hatenablog.com

happy-nap.hatenablog.com

happy-nap.hatenablog.com