リソース制限と分離のためのsystemd Cgroupに関する包括ガイド
Linuxの最新の初期化システムおよびシステム・サービス・マネージャーであるsystemdは、システムリソースを管理するための強力なツールを提供します。その最も重要な機能の一つは、コントロールグループ(cgroup)との統合です。cgroupはLinuxカーネルの機能であり、プロセスの集合に対してリソース使用量(CPU、メモリ、ディスクI/O、ネットワークなど)の制限、アカウンティング、分離を可能にします。本ガイドでは、systemdがどのようにスライス、スコープ、サービスといったユニットタイプを介してcgroupを利用し、クリティカルなプロセスが必要なリソースを受け取ることを保証しつつ、暴走するアプリケーションがシステム安定性に影響を与えるのを防ぐために、正確なリソース制限と分離を実現するかを掘り下げます。
systemdのcgroup統合を理解し活用することは、システム管理者、開発者、およびLinuxシステムのパフォーマンスと信頼性の維持に責任を持つすべての人にとって不可欠です。適切なリソース制限を設定することにより、リソース枯渇を防ぎ、アプリケーションパフォーマンスの予測可能性を向上させ、システム全体の安定性を高めることができます。本ガイドでは、複雑なリソース管理をアクセスしやすく効果的にするための、これらの制限を設定するための実践的なアプローチを提供します。
コントロールグループ(cgroups)の理解
systemdの実装に深く入る前に、cgroupの基本概念を把握することが不可欠です。cgroupは、プロセスのグループ化を可能にし、その後これらのグループにリソース管理ポリシーを割り当てるためのLinuxカーネル内の階層的なメカニズムです。これらのポリシーには以下が含まれます。
- CPU: CPU時間の制限、CPUアクセス権の優先順位付け。
- Memory: メモリ使用量の設定、アウトオブメモリー(OOM)状態の防止。
- I/O: ディスク読み取り/書き込み操作のスロットリング。
- Network: ネットワーク帯域幅の制限。
- Device Access: 特定のデバイスへのアクセス制御。
The kernel exposes cgroup configurations through a virtual file system, typically mounted at /sys/fs/cgroup. Each controller (e.g., cpu, memory) has its own directory, and within these, hierarchies of directories represent groups and their associated resource limits.
systemdのCgroup管理アーキテクチャ
systemdは、構造化されたユニット管理システムを提供することにより、直接的なcgroup操作の複雑さを抽象化します。プロセスの階層的なユニットへの編成を行い、それらをcgroup階層にマッピングします。リソース管理に関連する主要なユニットタイプは次のとおりです。
- Slices: これらはサービスユニットのための抽象的なコンテナです。スライスは階層を形成し、リソースの委任を可能にします。例えば、ユーザーセッションのスライスには、個々のアプリケーションのスライスが含まれることがあります。systemdは、システムサービス、ユーザーセッション、仮想マシン/コンテナのためにスライスを自動的に作成します。
- Scopes: これらは通常、完全なサービスユニットとして管理されていないユーザーセッションやシステムサービスに関連付けられた、一時的または動的に作成されたプロセスのグループに使用されます。これらは一時的であり、内部のプロセスが実行されている限り存在します。
- Services: これらはデーモンやアプリケーションを管理するための基本的なユニットです。サービスユニットが開始されると、systemdはそのプロセスをcgroup階層に配置します。通常はスライス内に配置されます。リソース制限はサービスユニットに直接適用できます。
Systemdのデフォルトの階層は、多くの場合次のようになります。
-.slice (ルートスライス)
|- system.slice
| |- <service_name>.service
| |- another-service.service
| ...
|- user.slice
| |- user-1000.slice
| | |- session-c1.scope
| | | |- <application>.service (ユーザーによって開始された場合)
| | | ...
| | ...
| | ...
| ...
|- machine.slice (VM/コンテナ用)
...
systemdユニットファイルを使用したリソース制限の適用
systemdでは、リソース制限を.service、.slice、または.scopeユニットファイル内で直接指定できます。これらのディレクティブは、それぞれ[Service]、[Slice]、または[Scope]セクションの下に配置されます。
CPU制限
CPUリソース制御の主要なディレクティブは次のとおりです。
CPUQuota=: ユニットが使用できる合計CPU時間を制限します。これはパーセンテージ(例: CPUコアの半分なら50%)またはCPUコアの分数(例:0.5)として指定されます。1期間あたりのマイクロ秒単位で値を指定することも可能です。デフォルトの期間は100msです。CPUShares=: CPU時間に対する相対的な重み付けを設定します。CPUShares=1024のユニットよりもCPUShares=2048のユニットの方が、競合が発生した場合に2倍のCPU時間を取得します。CPUWeight=:CPUShares=のエイリアスですが、範囲が異なります(1~10000、デフォルト100)。CPUQuotaPeriodSec=:CPUQuotaの期間を設定します。デフォルトは100msです。
例: Webサーバーを1 CPUコアの75%に制限する:
サービスファイル(例: /etc/systemd/system/mywebapp.service)を作成または編集します。
[Unit]
Description=My Web Application
[Service]
ExecStart=/usr/bin/mywebapp
User=webappuser
Group=webappgroup
# 1 CPUコアの75%に制限
CPUQuota=75%
[Install]
WantedBy=multi-user.target
サービスファイルの作成または変更後、systemdデーモンをリロードし、サービスを再起動します。
sudo systemctl daemon-reload
sudo systemctl restart mywebapp.service
メモリ制限
メモリ制限は、次のようなディレクティブによって制御されます。
MemoryLimit=: ユニットのプロセスが消費できるRAMの量にハードリミットを設定します。これはバイト数またはK、M、G、Tなどの接尾辞(例:512M)で指定できます。MemoryMax=:MemoryLimitに似ていますが、メモリアカウンティングとの連携において、よりモダンで柔軟性があると見なされることが多いです。通常、MemoryLimitよりも推奨されます。MemoryHigh=: ソフトリミットを設定します。この制限に近づくと、メモリ回収(スワッピング)がより積極的にトリガーされますが、ハードリミットはまだ適用されません。MemorySwapMax=: ユニットが使用できるスワップ空間の量を制限します。
例: データベースに2GBのRAM制限を設定する:
サービスファイル(例: /etc/systemd/system/mydb.service)を作成または編集します。
[Unit]
Description=My Database Service
[Service]
ExecStart=/usr/bin/mydb
User=dbuser
Group=dbgroup
# メモリを2ギガバイトに制限
MemoryMax=2G
[Install]
WantedBy=multi-user.target
リロードと再起動:
sudo systemctl daemon-reload
sudo systemctl restart mydb.service
I/O制限
I/Oスロットリングは、次のようなディレクティブを使用して制御できます。
IOWeight=: I/O操作に対する相対的な重み付けを設定します。値が大きいほど、I/O優先度が高くなります。範囲は1から1000(デフォルト500)です。IOReadBandwidthMax=: 読み取りI/O帯域幅を制限します。指定方法は[<デバイス>] <1秒あたりのバイト数>です。例として、IOReadBandwidthMax=/dev/sda 100Mは/dev/sdaに対する読み取り操作を100MB/sに制限します。IOWriteBandwidthMax=: 書き込みI/O帯域幅を制限します。IOReadBandwidthMax=と同様の形式です。
例: バックグラウンド処理サービスを特定のディスクで50MB/sに制限する:
サービスファイル(例: /etc/systemd/system/batchproc.service)を作成または編集します。
[Unit]
Description=Batch Processing Service
[Service]
ExecStart=/usr/bin/batchproc
User=batchuser
Group=batchgroup
# /dev/sdbへの書き込み操作を50MB/sに制限
IOWriteBandwidthMax=/dev/sdb 50M
# 適度な読み取り優先度を与える
IOWeight=200
[Install]
WantedBy=multi-user.target
リロードと再起動:
sudo systemctl daemon-reload
sudo systemctl restart batchproc.service
Cgroupの管理と監視
systemdは、ユニットに関連付けられたcgroupを検査および管理するためのツールを提供します。
Cgroupステータスの検査
systemctl statusコマンドは、ユニットのcgroupメンバーシップとリソース使用量に関する情報を提供します。
systemctl status mywebapp.service
cgroupパスを示す行を探します。例えば次のようになります。
● mywebapp.service - My Web Application
Loaded: loaded (/etc/systemd/system/mywebapp.service; enabled; vendor preset: enabled)
Active: active (running) since Tue 2023-10-27 10:00:00 UTC; 1 day ago
Docs: man:mywebapp(8)
Main PID: 12345 (mywebapp)
Tasks: 5 (limit: 4915)
Memory: 15.5M
CPU: 2h 30m 15s
CGroup: /system.slice/mywebapp.service
└─12345 /usr/bin/mywebapp
cgroupファイルシステムを直接調べることもできます。
systemd-cgls # systemdによって管理されているcgroup階層を表示
systemd-cgtop # topに似ていますが、cgroup用です
サービスcgroupに適用されている特定の制限を表示するには、次のようにします。
# メモリ制限の場合
catsysfs /sys/fs/cgroup/memory/system.slice/mywebapp.service/memory.max
# CPU制限の場合
catsysfs /sys/fs/cgroup/cpu/system.slice/mywebapp.service/cpu.max
(注:正確なパスとファイル名は、cgroupのバージョンやシステム構成によって若干異なる場合があります。)
実行中のCgroup制限の変更
制限をユニットファイルで設定するのがベストプラクティスですが、systemctl set-propertyを使用して一時的に調整できます。
sudo systemctl set-property mywebapp.service CPUQuota=50%
これらの変更は再起動後も永続化されません。永続化するには、ユニットファイルを更新し、systemdデーモンをリロードする必要があります。
リソース委任のためのスライス
スライスは、サービスやアプリケーションのグループを管理するのに強力です。スライスにリソース制限を定義すると、そのスライス内のすべてのサービスやスコープがそれらの制限を継承または制約されます。
例: リソース集約型のバッチジョブ専用スライスの作成:
スライスファイル(例: /etc/systemd/system/batch.slice)を作成します。
[Unit]
Description=Batch Processing Slice
[Slice]
# このスライス内の全ジョブのCPU合計を1コアに制限
CPUQuota=100%
# メモリ合計を4GBに制限
MemoryMax=4G
次に、各サービスファイル内のSlice=ディレクティブを使用して、サービスがこのスライス内で実行されるように構成できます。
[Unit]
Description=Specific Batch Job
[Service]
ExecStart=/usr/bin/mybatchjob
# このサービスをbatch.sliceに配置
Slice=batch.slice
[Install]
WantedBy=multi-user.target
systemdをリロードし、必要に応じてスライスを有効化/開始し(多くの場合暗黙的にアクティブ化されます)、サービスを開始します。
sudo systemctl daemon-reload
sudo systemctl start mybatchjob.service
このアプローチにより、関連するプロセスをグループ化し、それらの集合的なリソース消費を管理できます。
ベストプラクティスと考慮事項
- 段階的な制限から始める: 制限を設定する際は、控えめな値から始め、必要に応じて徐々に増やします。過度な制限はアプリケーションを不安定にすることがあります。
- 監視: システムのリソース使用量とcgroup設定の影響を定期的に監視します。
systemd-cgtop、htop、top、iotopなどのツールは非常に有用です。 - Cgroup v1とv2の理解: systemdはcgroup v1とv2の両方をサポートしています。多くのディレクティブは似ていますが、v2は統一された階層といくつかの動作の違いを提供します。複雑な問題に遭遇した場合は、使用しているバージョンを認識していることを確認してください。
- 優先順位付けとハードリミット: リソースが不足している場合の優先順位付けには
CPUShares/CPUWeightを、厳密なハードリミットにはCPUQuotaを使用します。同様に、ソフトリミットにはMemoryHighを、ハードリミットにはMemoryMaxを使用します。 - サービス対スライス: 個々のアプリケーションにはサービスユニットを、関連するアプリケーションのグループやリソースプールの管理にはスライスを使用します。
- ドキュメント化: 特に本番環境では、クリティカルなサービスに適用されるリソース制限を明確に文書化します。
- OOM Killer: プロセスが
MemoryMax制限を超過した場合、cgroup内であってもカーネルのOut-Of-Memory (OOM) Killerによって終了させられる可能性があることに注意してください。systemdは、OOMPolicy=のようなディレクティブを使用して、特定のcgroupに対するOOM Killerの動作を管理できます。
結論
systemdとcgroupの統合は、システムリソースを制御および分離するための堅牢でユーザーフレンドリーなメカニズムを提供します。サービス、スコープ、スライスユニットの利用を習得することにより、管理者はCPU、メモリ、I/O制限を効果的に適用し、システムの安定性、予測可能なパフォーマンスを保証し、リソース枯渇を防ぐことができます。これらの制御の実装は、最新のLinuxシステム管理の基本的な側面であり、アプリケーション環境と基盤となるインフラストラクチャに対するより大きな制御を可能にします。