Linuxブート時間の高速化:Systemdユニット依存関係の分析と最適化
systemd-analyze、critical-chain、ユニット依存関係のクリーンアップを使用して、Linuxの遅いブートパスを見つけて修正します。
Linuxブート時間の高速化:Systemdユニット依存関係の分析と最適化
Linuxのブート時間最適化は、1つの質問から始まります。実際にマシンが使用可能になるのを妨げているものは何か?最新のディストリビューションのほとんどでは、systemdがサービスを並列に起動しますが、遅いユニットや不要な順序ルールがブートパスを妨げる可能性があります。
どのユニットが時間を要し、どのユニットがクリティカルチェーン上にあるかを確認することで、それらを調整、遅延、または無効にするかを決定できます。以下の例は、systemd-analyzeと、システムの予測可能性を維持する小さな依存関係の変更に焦点を当てています。
Systemdブートプロセスの理解
Systemdは、可能な限りサービスを並列に実行することで起動プロセスを管理します。ただし、サービスは、明示的および暗黙的な依存関係がすべて満たされた場合にのみ起動できます。ユニットAが処理を進める前にユニットBが完全にアクティブである必要がある場合、ユニットAはユニットBによってブロックされます。これらのブロックする依存関係を特定することが、高速化への第一歩です。
主要なSystemd分析ツール
Systemdは、ブートパフォーマンスを診断するためのいくつかの強力なコマンドラインユーティリティを提供します。以下のツールは、ボトルネックを特定するために不可欠です。
1. systemd-analyze(全体ビュー)
このコマンドは、カーネル、ユーザースペースの初期化、および利用可能なターゲットのロードにかかった合計時間の高レベルの概要を提供します。
systemd-analyze
出力例の解釈:
| コンポーネント | 所要時間 |
|---|---|
| カーネル | 1.234s |
| Initrd | 0.500s |
| ユーザースペース | 5.789s |
| 合計 | 7.523s |
これにより、ボトルネックがカーネルフェーズ(ファームウェア/ドライバーのロード)にあるのか、ユーザースペースフェーズ(サービスの起動)にあるのかがすぐにわかります。
2. systemd-analyze blame(遅いユニットの特定)
このコマンドは、アクティブ化にかかった時間でソートされたユニットを、最も長い時間が一番上に表示されるようにリストします。
systemd-analyze blame
焦点: 上位10エントリを確認します。これらは、起動中にアクティブに時間を消費しているサービスです。初期化時間が長いということは、単にそのサービスが多くの作業を行っていることを意味する場合があることに注意してください。目標は、この作業がブート中に行われる必要があるかどうかを確認することです。
3. systemd-analyze critical-chain(依存関係分析)
このコマンドは、ブートターゲット(通常はgraphical.targetまたはmulti-user.target)につながる依存関係チェーンを表示します。システムが完全に起動したと見なされる前に完了する必要があるユニットのシーケンスを強調表示します。
systemd-analyze critical-chain
クリティカルチェーンにリストされているユニットは、最適化の主要なターゲットです。それらを遅らせると、システム全体のブートが遅延するためです。
4. systemd-analyze plot(ブートシーケンスの可視化)
並列性とブロックのグラフィカルな表現については、SVGファイルを生成するplotコマンドを使用します。
systemd-analyze plot > boot_analysis.svg
# ウェブブラウザでboot_analysis.svgを開く
このグラフは、どのサービスが並列に実行され、どのサービスが他のサービスを待機しているかを視覚的に示し、依存関係の問題を即座に明らかにします。
最適化テクニック:ユニットファイルの変更
上記のツールを使用して遅いユニットやブロックするユニットを特定したら、最適化にはユニット自体の高速化またはいつ実行する必要があるかの変更が含まれます。
1. blameによって特定された遅いユニットへの対処
blame出力の上位にリストされているサービス(例:slow-database.serviceが10秒かかる)が、基本的なシステム操作(ログインや基本的なネットワーキングなど)にすぐに必要でない場合は、遅延させることを検討してください。
アクション: その起動依存関係レベルを変更します。
- 現在
multi-user.targetで起動する場合は、代わりにタイマー、ソケット、パスユニット、または手動コマンドから起動できるかどうかを確認します。 - サービスがオプションの場合は、コアの依存関係動作を変更するよりも、無効にする方が通常は安全です。
DefaultDependencies=noは、そのユニットタイプに対してsystemdが通常追加するデフォルトの順序を理解している場合にのみ使用してください。
2. Wants、Requires、Afterを使用した依存関係の最適化
ユニットファイルは、依存関係ディレクティブを使用して実行順序を制御します。ここでの設定ミスは、不要な逐次実行の一般的な原因です。
依存関係のタイプ:
Requires=:強い依存関係。必要なユニットが失敗した場合、このユニットも失敗します。Wants=:弱い依存関係。必要なユニットが利用可能な場合、このユニットは起動しますが、必要なユニットが失敗しても起動を試みます。After=:順序ディレクティブ。このユニットは、指定されたユニットが起動を完了した後にのみ起動します(成功/失敗に関係なく)。Before=:順序ディレクティブ。このユニットは、指定されたユニットの前に起動する必要があります。
ベストプラクティスのヒント:オプションの関係にはRequiresよりもWantsを優先します。 Wants=は、それ自体では順序ではなく、失敗動作を変更します。After=などの順序ルールも追加しない限り、必要なユニットは並列に起動できます。
不要なAfter=制約の削除
ブート時間を高速化する最も効果的な方法は、不要な順序制約を排除することです。ユニットAが機能的にユニットBが起動されることに依存していない場合は、ユニットAの定義からAfter=unit-b.service行を削除します。
変更例(概念):
カスタムアプリケーションユニットapp.serviceが、ネットワーク設定サービスを不必要に待機しているとします。
# /etc/systemd/system/app.service
[Unit]
Description=My Application
Requires=network.target
After=network.target <-- 潜在的に不要な待機!
[Service]
ExecStart=/usr/bin/myapp
アプリケーションがローカルループバックインターフェースのみを必要とする場合、またはローカルファイルロックを確立するだけでよい場合、完全なネットワークスタック(network.target)を待機すると、数秒を無駄にする可能性があります。アプリケーションが実際に外部ネットワークを必要としないことを確認したら、After=network.target行を削除します。systemdは、ネットワーク設定と並行して、可能な限り早くapp.serviceの起動を試みます。
3. 不要なサービスのマスキング
systemd-analyze blameが、絶対に必要のないサービス(サーバーでの不要なBluetoothサポートや特定のハードウェアモニターなど)の実行を示している場合、それを無効化またはマスキングすると、完全に起動しなくなります。
- 無効化:
systemctl disable <unit>(将来のブートで起動しないようにします)。 - マスキング(より強力):
systemctl mask <unit>(ユニットを/dev/nullにリンクし、手動での起動試行も防ぎます)。
# 例:セルラーモデムがない場合にModemManagerをマスキング
sudo systemctl mask ModemManager.service
変更のリロードと確認
ユニットファイル(特に/etc/systemd/system/に配置されたもの)を変更した後は、再起動してテストする前に、systemdに設定デーモンをリロードするように指示する必要があります。
sudo systemctl daemon-reload
# 次に、再起動する前に依存関係またはステータスを確認
systemctl list-dependencies myapp.service
最後に、常にシステムを再起動して、ブートシーケンスへの真の影響を測定します。
sudo reboot
再起動後、すぐにsystemd-analyzeを再度実行して、最適化によって達成された時間節約を定量化します。
要点
ブートチューニングを小さな変更のループとして扱います。systemd-analyzeで測定し、クリティカルパス上のユニットを見つけ、正当化できる順序ルールのみを削除し、再起動して再度測定します。最も安全な成果は、通常、不要なサービスの無効化、作業のタイマーやソケットアクティベーションへの変換、および独自のユニットからの不要なAfter=行の削除から得られます。