はじめに
こんにちは。クラウド事業部の佐藤です。
利用可能なリソースが限られているマシン、特にクラウド環境ですとAWS EC2やAzure VMなどでサイズが小さいインスタンスを使用している際に「あれもこれもプログラム(サービス)を動かしたいけれどリソースが少ないから難しい。。。スペック(インスタンスサイズ)を上げずにできるだけ頑張りたい。。。」という場面に遭遇する方は少なからずいらっしゃるかと思います。
そんなときのアイデアの1つとして、この記事ではsystemdとcgroupを利用したサービスに対するリソース制限を試してみたいと思います。
仕組み
Linuxにはcgroupというシステムリソースの管理・制限を行う機能があるので、これを利用します。 (この機能はDockerなどのコンテナ技術でも利用されているようです)
cgroup を使用することにより、システム管理者は、システムリソースの割り当て、優先順位付け、拒否、管理、および監視をきめ細かく制御できます。ハードウェアリソースをアプリケーションとユーザー間でスマートに分割できるため、全体的な効率が向上します。
実際にcgroupを利用する際にはcgroupを直接操作するのではなく、systemdにより管理されているサービスの設定ファイルを編集することで実現します。
整理すると、あるプログラムに対してリソース制限を行うには以下のような手順となります。
- プログラムをsystemdのサービスとして登録する
- サービスの定義ファイルを編集しリソース制限を適用する
- systemdはサービスの定義ファイルを読み込み、そのプログラムが実行されるプロセスに対してcgroupによる制限をかける
この手順により、プログラムはsystemdサービスとして利用可能( systemctlコマンドによる開始/終了などの操作が可能)となり、cgroupによるリソース制限を行うこともできるようになります。
検証環境
- OS: Ubuntu 24.04.3
- CPU: Intel Core-i5 6300U (2コア/4スレッド)
- RAM: 4GB
検証の準備
ここでは「CPUの制限」「メモリ使用量の制限」「ディスクの書き込み量の制限」の3つの観点に着目して、それぞれ検証用のスクリプトとサービスを作成してみます。
下準備
/opt/resource-control-test/のディレクトリを作成し、ここを今回の検証用スクリプトの置き場とします
sudo mkdir -p /opt/resource-control-test
CPU
下記のシェルスクリプトを用意しました。 これを実行することでCPU100%(1コア分)を使用させることができます。
#!/bin/bash while :; do :; done EOF
/opt/resource/control-test/cpu-test.shとして保存し、実行権限を渡しておきます。
sudo chmod +x /opt/resource/control-test/cpu-test.sh
メモリ
下記のPythonのスクリプトを用意しました。 これを実行することで10MBずつメモリが確保されていきます。 制限をかけていないとそのままメモリを使い切ってしまいますのでご注意ください。
import time
import sys
data = []
print("Starting memory allocation...")
try:
for i in range(1, 10000):
data.append(' ' * 10 * 1024 * 1024)
print(f"Allocated: {i * 10} MB")
except Exception as e:
print(f"Error: {e}")
/opt/resource/control-test/memory-test.pyとして保存しておきます。
ディスク
下記のシェルスクリプトを用意しました。 これを実行することで1GBのファイルの作成・削除を繰り返し行いディスクへの書き込み負荷をかけることができます。
oflag=directオプションをつけないとメモリ上のキャッシュが利用されリソース制限がうまく機能しないのでつけておきます。
#!/bin/bash while : ;do /usr/bin/dd if=/dev/zero of=/tmp/testfile bs=1M count=1000 oflag=direct rm /tmp/testfile done
/opt/resource/control-test/disk-test.shとして保存し、実行権限を渡しておきます。
sudo chmod +x /opt/resource/control-test/disk-test.sh
systemdサービスの作成
/etc/systemd/system/内にサービスの定義ファイルを作成します。
CPU
ファイル名: resource-control-test-cpu.service
[Unit] Description=Resource Control Test for CPU [Service] ExecStart=/opt/resource-control-test/cpu-test.sh [Install] WantedBy=multi-user.target
メモリ
ファイル名: resource-control-test-memory.service
[Unit] Description=Resource Control Test for Memory [Service] ExecStart=/usr/bin/python3 opt/resource-control-test/memory-test.py [Install] WantedBy=multi-user.target
ディスク
ファイル名: resource-control-test-disk.service
[Unit] Description=Resource Control Test for Disk [Service] ExecStart=/opt/resource-control-test/disk-test.sh [Install] WantedBy=multi-user.target
作成したファイルの確認
最終的に以下のようにファイルが作成できていることを確認します
$ ls -l /opt/resource-control-test/ total 12 -rwxrwxr-x 1 user user 36 Nov 29 03:48 cpu-test.sh -rwxrwxr-x 1 user user 123 Nov 29 04:00 disk-test.sh -rw-rw-r-- 1 user user 248 Nov 29 03:40 memory-test.py
$ ls -l /etc/systemd/system | grep resource-control-test- -rw-r--r-- 1 root root 147 Nov 29 04:11 resource-control-test-cpu.service -rw-r--r-- 1 root root 149 Nov 29 04:15 resource-control-test-disk.service -rw-r--r-- 1 root root 169 Nov 29 04:14 resource-control-test-memory.service
サービスの読み込み
すべてのファイルを作成したら、systemdのサービスとして読み込ませるためのコマンドを実行します
sudo systemctl daemon-reload
上記のコマンドを実行したら、実際にsystemdのサービスとして読み込めているか確認します。
CPUの場合
systemctl cat resource-control-test-cpu.service
下記のように作成したサービス定義のファイルが表示されればOKです。他のファイルも同様に確認できればOKです。
$ systemctl cat resource-control-test-cpu.service # /etc/systemd/system/resource-control-test-cpu.service [Unit] Description=Resource Control Test for CPU [Service] ExecStart=/opt/resource-control-test/cpu-test.sh [Install] WantedBy=multi-user.target
リソース制限なしで実行してみる
CPU
起動
sudo systemctl start resource-control-test-cpu.service
確認
systemd-cgtop
%CPUの列を確認します。検証用のサービスがCPUを100%使用していることが確認できます。
CGroup Tasks %CPU Memory Input/s Output/s / 234 100.6 550.1M - - system.slice 124 100.1 298.9M - - system.slice/resource-control-test-cpu.service 1 100.0 580.0K - -
終了
sudo systemctl stop resource-control-test-cpu.service
メモリ
注意:このコマンドは一時的に使用可能なメモリをすべて使用してしまうのでOSの動作に影響が出る可能性が高いです。もし実行される際は注意してください。
起動
sudo systemctl start resource-control-test-memory.service
syslogを見てログの確認
sudo less /var/log/syslog
使用可能なメモリを使い切ってしまい、OSのOOM killerにより強制終了されてしまいました。
anon-rss:3429504kBが物理メモリの使用量を示しており、約3.2GBとなっています。
025-11-29T05:09:56.070002+00:00 hostname1 kernel: Out of memory: Killed process 3328 (python3) total-vm:3449716kB, anon-rss:3429504kB, file-rss:2432kB, shmem-rss:0kB, UID:0 pgtables:6788kB oom_score_adj:0
終了
sudo systemctl stop resource-control-test-memory.service
ディスク
起動
sudo systemctl start resource-control-test-disk.service
確認
sudo less /var/log/syslog
syslogに残っているddコマンドのログを確認します。約130MB/sから140MB/sの速度でddコマンドが実行されたことが確認できました
2025-11-29T05:21:56.356744+00:00 hostname1 disk-test.sh[3650]: 1000+0 records in 2025-11-29T05:21:56.357355+00:00 hostname1 disk-test.sh[3650]: 1000+0 records out 2025-11-29T05:21:56.357626+00:00 hostname1 disk-test.sh[3650]: 1048576000 bytes (1.0 GB, 1000 MiB) copied, 7.68633 s, 136 MB/s 2025-11-29T05:22:03.974375+00:00 hostname1 disk-test.sh[3653]: 1000+0 records in 2025-11-29T05:22:03.974822+00:00 hostname1 disk-test.sh[3653]: 1000+0 records out 2025-11-29T05:22:03.975077+00:00 hostname1 disk-test.sh[3653]: 1048576000 bytes (1.0 GB, 1000 MiB) copied, 7.60413 s, 138 MB/s 2025-11-29T05:22:11.463149+00:00 hostname1 disk-test.sh[3658]: 1000+0 records in 2025-11-29T05:22:11.463627+00:00 hostname1 disk-test.sh[3658]: 1000+0 records out 2025-11-29T05:22:11.463908+00:00 hostname1 disk-test.sh[3658]: 1048576000 bytes (1.0 GB, 1000 MiB) copied, 7.47594 s, 140 MB/s
終了
sudo systemctl stop resource-control-test-disk.service
リソース制限をかけてみる
systemdのバージョンにより利用可能な制限は異なるようなのですが、実際にそのマシンで利用可能な制限の項目はmanコマンドでドキュメントを見ることで確認できます。
man systemd.resource-control
私の検証環境のバージョンは255でした。
$ systemd --version systemd 255 (255.4-1ubuntu8.11)
CPU
CPU使用率の制限のために CPUQuotaを利用してみます。
sudo mkdir /etc/systemd/system/resource-control-test-cpu.service.d/
/etc/systemd/system/resource-control-test-cpu.service.d/ディレクトリの中で制限をかけるための設定ファイルを作成します。
ここではファイル名を limit.confとします。下記の内容でCPU使用率が10%に制限されることを期待しています。
[Service] CPUQuota=10%
作業の結果以下のような状態でディレクトリとファイルが作成されます。
$ ls -l /etc/systemd/system/resource-control-test-cpu.service.d/ total 4 -rw-r--r-- 1 root root 23 Nov 29 04:49 limit.conf
$ cat limit.conf [Service] CPUQuota=10%
systemdサービスの再読み込みを実行します。
sudo systemctl daemon-reload
設定が読み込めたか確認します。
systemctl cat resource-control-test-cpu.service
下記の末尾3行を見てみると、追加した設定が正しく読み込めたことを確認できます。
$ systemctl cat resource-control-test-cpu.service # /etc/systemd/system/resource-control-test-cpu.service [Unit] Description=Resource Control Test for CPU [Service] ExecStart=/opt/resource-control-test/cpu-test.sh [Install] WantedBy=multi-user.target # /etc/systemd/system/resource-control-test-cpu.service.d/limit.conf [Service] CPUQuota=10%
この状態でサービスを起動し、CPU使用率を確認します。
sudo systemctl start resource-control-test-cpu.service systemd-cgtop
CGroup Tasks %CPU Memory Input/s Output/s / 234 10.9 560.5M - - system.slice 124 10.2 280.0M - - system.slice/resource-control-test-cpu.service 1 9.8 344.0K - -
CPU使用率が10%程度で収まっていることが確認できました。
メモリ
メモリ使用率の制限のために MemoryMaxを利用してみます。
基本的にはCPUと同じなので確認作業など一部省略しました。
sudo mkdir /etc/systemd/system/resource-control-test-memory.service.d/
/etc/systemd/system/resource-control-test-memory.service.d/ディレクトリの中で制限をかけるための設定ファイルを作成します。
ここではファイル名を limit.confとします。下記の内容でメモリの使用量が100MBに制限されることを期待しています。
[Service] MemoryMax=100M
systemdサービスの再読み込みを実行します。
sudo systemctl daemon-reload
設定が読み込めたか確認します。
systemctl cat resource-control-test-memory.service
この状態でサービスを起動します
sudo systemctl start resource-control-test-memory.service
syslogでログの確認をします
sudo less /var/log/syslog
2025-11-29T05:11:11.433558+00:00 hostname1 kernel: Memory cgroup out of memory: Killed process 3411 (python3) total-vm:120416kB, anon-rss:101760kB, file-rss:2944kB, shmem-rss:0kB, UID:0 pgtables:276kB oom_score_adj:0
anon-rss:101760kBとなっており物理メモリの使用量が約100MBで終了したことがわかります。
また、OSのOOM Killerではなく、cgroupの制限によりプロセスがKillされたことがメッセージから読み取れます。設定した制限がうまく機能したようです。
ディスク
ディスクも同様に確認します。
ディスクの書き込み量の制限のために IOWriteBandwidthMaxを利用してみます。
sudo mkdir /etc/systemd/system/resource-control-test-disk.service.d/
/etc/systemd/system/resource-control-test-disk.service.d/ディレクトリの中で制限をかけるための設定ファイルを作成します。
ここではファイル名を limit.confとします。下記の内容で/dev/sda3に対する書き込みが10MB/secに制限されることを期待しています。
[Service] IOWriteBandwidthMax=/dev/sda3 10M
systemdサービスの再読み込みを実行します。
sudo systemctl daemon-reload
設定が読み込めたか確認します。
systemctl cat resource-control-test-disk.service
この状態でサービスを起動し、syslogを確認します。
sudo systemctl start resource-control-test-disk.service
sudo less /var/log/syslog
2025-11-29T05:23:59.916796+00:00 hostname1 disk-test.sh[3722]: 1000+0 records in 2025-11-29T05:23:59.982810+00:00 hostname1 disk-test.sh[3722]: 1000+0 records out 2025-11-29T05:23:59.982971+00:00 hostname1 disk-test.sh[3722]: 1048576000 bytes (1.0 GB, 1000 MiB) copied, 104.866 s, 10.0 MB/s
制限が正しくかかり、10.0MB/sぴったりの速度でddコマンドが実行されたことを確認できました。
余談
cgroupの実体に適用されている設定は/sys/fs/cgroup/system.slice/以下のファイルを確認することで分かります
(以下のコマンドはディスクの制限を確認する場合)
cat /sys/fs/cgroup/system.slice/resource-control-test-disk.service/io.max
$ cat /sys/fs/cgroup/system.slice/resource-control-test-disk.service/io.max 8:0 rbps=max wbps=10000000 riops=max wiops=max
10000000 -> 10M
まとめ
systemd/cgroupによるリソース制限の手順を示し、簡単ではありますが効果を確認することができました。
リソースが少ないマシン/インスタンス上では、優先度の低いプログラムはsystemdのサービスとしてリソース制限をかけることで、システム全体の動作の改善が期待できるかもしれません。
各サービス間の調整を細かくすると管理の難易度が上がってしまうのかな?という気もしていますが、何かの役に立てばと思います。
おわりに
私達クラウド事業部はクラウド技術を活用したSI/SESのご支援をしております。
また、一緒に働いていただける仲間も募集中です! ご興味持っていただけましたらぜひお声がけください。