systemd-runを使いこなす:LinuxスクリプトのCPUとRAMを「即座に」制限する方法

Linux tutorial - IT technology blog
Linux tutorial - IT technology blog

サーバーをフリーズさせずに重いスクリプトを実行する:systemd-runによる解決策

数十GBのバックアップスクリプトやフォルダ圧縮を実行した際、突然サーバーが「フリーズ」してしまった経験はありませんか?CPU使用率が100%に跳ね上がり、RAMが枯渇し、Nginxが504エラーを返し始める。以前の私は nice を使っていましたが、これは優先度を調整するだけで、実際のリソースに「上限」を設けることはできませんでした。

私が管理しているUbuntu 22.04サーバー(4GB RAM)において、systemd-run は突発的なタスクに対する真の救世主です。このツールを使えば、「このスクリプトには最大500MBのRAMと20%のCPUしか使わせない」といった命令を出すことができます。上限を超えるとsystemdが自動的に介入し、コアサービスを安全に保ちます。

クイックスタート:30秒でリソースを制限する

データ処理を行うPythonスクリプトを実行する必要があるものの、RAMを使い果たしてしまうのが心配だとします。以下のコマンド構造を使用して、RAMを500MB、CPU1コアの30%に制限してみましょう:

sudo systemd-run --scope -p MemoryMax=500M -p CPUQuota=30% python3 heavy_script.py

重要なパラメータの解説:

  • --scope: 現在のターミナルで直接実行します。ターミナルを閉じるとコマンドも停止します。
  • -p MemoryMax=500M: RAMの最大しきい値。これを超えると、プロセスは強制終了(kill)されます。
  • -p CPUQuota=30%: プロセスがCPU時間の合計30%を超えないようにします。

最大の利点は、systemdが一過性のユニット(transient unit)を作成し、即座にcgroupsを適用することです。以前のように手動で .service ファイルを作成する手間は必要ありません。

なぜ systemd-run は従来のツールより優れているのか?

多くの古いドキュメントでは ulimit の使用が推奨されています。しかし、ulimit は子プロセスのグループ管理が非常に困難です。一方、systemd-runcgroups (Control Groups) を活用します。これは Docker がリソースを効率的に分離するために使用しているコア技術と同じものです。

–scope と –unit の違い

ニーズに合わせて実行モードを選択してください:

  • –scope: 現在のシェルで実行します。出力がすぐに画面に表示されます。
  • –unit: コマンドをバックグラウンドで実行されるサービスに変えます。数時間かかるタスクに最適です。
sudo systemd-run --unit=backup-job -p MemoryMax=1G /usr/local/bin/backup.sh

--unit を使えば、安心してPCを閉じて寝ることができます。翌朝、systemctl status backup-job コマンドで確認するだけです。

最も「価値のある」制限パラメータ

以下は、実際のプロジェクトで頻繁に適用しているフラグです:

1. メモリ(RAM)の制限

  • MemoryMax: ハードリミット。これを超えると、OOM Killerが即座に介入します。
  • MemoryHigh: ソフトリミット。このレベルに達すると、システムはプロセスにメモリを解放させるか、即座に殺す代わりに速度を低下させます。
sudo systemd-run --scope -p MemoryHigh=800M -p MemoryMax=1G ./my-app

2. CPUの制限

  • CPUQuota: パーセンテージで指定します。サーバーが4コアで200%に設定した場合、スクリプトは最大2コア分のパワーを使用できます。

3. ディスク読み書き(I/O)の制限

バックアップスクリプトがディスクの帯域幅を占有するのを防ぐために、書き込み速度を制限しましょう:

sudo systemd-run --scope -p "IOWriteBandwidthMax=/dev/sda 10M" ./heavy-write-script.sh

リアルタイムでのリソース監視

実行中のコマンドがどれくらいリソースを消費しているかを確認するには? top ではなく、systemd-cgtop を使いましょう。cgroupsの構造に従ってリソースを非常に直感的に表示してくれます。

systemd-cgtop

具体的なCPU/RAMの数値とともに、一過性のユニットが表示されます。バックグラウンドタスクを停止したい場合は、おなじみの停止コマンド sudo systemctl stop backup-job を使うだけです。

実践的な経験:いつ使うべきか?

運用を通じて、以下の3つのシナリオが最も効果的だと感じています:

  1. データクローリング: Pythonスクリプトはメモリリークを起こしやすいです。MemoryMax を設定しておくことで、サーバー全体をダウンさせる前にスクリプトを自動停止できます。
  2. 古いログの圧縮: 重いログファイルを gzip で圧縮すると、ロードアベレージが急上昇することがよくあります。CPUQuota=15% に制限することで、Webサイトの遅延を引き起こさずにバックグラウンドで着実に圧縮を進められます。
  3. SSH経由の操作: ネットワークが不安定な場合、systemd-run --unit を使うことで、接続が切れてもコマンドを安全に実行し続けることができます。

注意: cgroupsを操作するには sudo 権限が必要です。一般ユーザー権限で実行したい場合は、cgroup v2のデリゲーション(委譲)設定が必要でかなり複雑になるため、手っ取り早く sudo を使うのがおすすめです。

おわりに

systemd-run は単なるコマンドではなく、現代的なシステム管理の考え方そのものです。スクリプトを「野放し」にするのではなく、固定されたリソースの枠内に収めることで、サーバーの安定性を維持できます。ぜひ日々のスクリプトに適用して、その違いを実感してみてください。

Share: