cgroupとsystemdによるLinuxリソース管理の仕組み

# 1 cgroupとは
cgroups[^1]はLinuxカーネルの機能の一つで、プロセスを階層的に管理し、リソースの配分を決められる仕組みである。(cgroupsにはv1とv2という２つのバージョンが存在するが、現在ではv2が主流なので、ここではv2についてのみ説明する)
cgroupsのインターフェースはファイルシステムとして提供されており、通常のファイルシステムと同様に、```mkdir```や```cat```のようなコマンドでアクセスすることができる。多くの環境ではcgroupsのファイルシステムは```/sys/fs/cgroup```にマウントされている。

## 1.1 cgroupsのインターフェース
cgroupsファイルシステム上にある、一つのディレクトリが一つのcgroupとして機能する(ここでの「cgroup」は仕組み全体のことではなく、一つのグループのことを指す)。
以下はcgroupの一例
```bash
$ tree /sys/fs/cgroup/system.slice/cron.service
/sys/fs/cgroup/system.slice/cron.service
├── cgroup.controllers
├── cgroup.events
├── cgroup.freeze
├── cgroup.kill
├── cgroup.max.depth
├── cgroup.max.descendants
├── cgroup.pressure
├── cgroup.procs
├── cgroup.stat
├── cgroup.subtree_control
├── cgroup.threads
├── cgroup.type
├── cpu.pressure
├── cpu.stat
├── io.pressure
├── memory.current
├── memory.events
├── memory.events.local
├── memory.high
├── memory.low
├── memory.max
├── memory.min
├── memory.numa_stat
├── memory.oom.group
├── memory.peak
├── memory.pressure
├── memory.reclaim
├── memory.stat
├── memory.swap.current
├── memory.swap.events
├── memory.swap.high
├── memory.swap.max
├── memory.zswap.current
├── memory.zswap.max
├── pids.current
├── pids.events
├── pids.max
└── pids.peak

1 directory, 38 files
```
上の例だと、「/sys/fs/cgroup/system.slice/cron.service」がcgroupを表すディレクトリになっている。
ディレクトリの下に大量のファイルが配置されており、これらがcgroupのインターフェースとして機能するファイルとなっている。たとえば、「memory.max」はメモリ使用量の上限、「pids.max」はプロセス数の上限を設定するファイルである。
ファイルに書き込むことでリソース管理の設定を変更でき、```cat```コマンドなどを使って現在の設定を出力することができる(読み込み専用や書き込み専用のインターフェースファイルもある)。
各cgroupに割り当てられているプロセスのPIDは、```cgroup.procs```という名前のファイルで管理されている。
```bash
$ cat /sys/fs/cgroup/system.slice/cron.service/cgroup.procs
704
```
```cgroup.procs```に直接PIDを書き込むと、プロセスをcgroupに割り当てることができる。
```
# echo 920 >> /sys/fs/cgroup/system.slice/cron.service/cgroup.procs
```
# 2 systemdによるcgroups管理
systemdを使用する環境の場合、一般的な設定では、systemdがシステム全体のcgroupsの設定を管理する。システムのリソース管理の設定を変更したい場合、通常はsystemdのインターフェースを操作することになる。
```systemd-cgls```コマンドを使うと、systemdが管理しているcgroupsの全体像を見ることができる。```systemd-cgtop```コマンドではcgroupが現在使用しているリソースを確認できる。

systemdのリソース管理について、詳しくは[^2][^3]を参照のこと。

## 2.1 systemdユニット
systemdのユニットの中で、リソース管理に関わるのは次の３つである。
### 2.1.1 service
serviceはsystemdの中でも最も一般的な、プロセスを管理するユニットである。apache、nginx、mariadbなど、```systemctl```コマンドで操作する機会の多いプログラムのほとんどはserviceとして管理されている。
systemdでは、基本的に<strong>一つのserviceに対して一つのcgroupが割り当てられる。</strong>これにより、各serviceに対して個別にリソース管理の設定をすることが可能になっている。

### 2.1.2 slice
sliceは階層的にリソースを管理するためのユニットである。<strong>一つのsliceに対して一つのcgroupが割り当てられる</strong>が、serviceとは違って、slice自身はプロセスを管理しない。
下のように、serviceやsliceをまとめて全体を管理するのが主な役目である。
```
     CGroup: /system.slice
             ├─ModemManager.service
             ├─NetworkManager.service
             ├─avahi-daemon.service
             ├─bluetooth.service
```
### 2.1.3 scope
scopeはserviceと同様にプロセスを管理するユニットである。<strong>一つのscopeに対して一つのcgroupが割り当てられる。</strong>
serviceとの主な相違点は以下の2つ。
- systemdがプロセスを開始しない(ユーザーセッションから開始される)
- メインプロセスがなく、複数プロセスを管理する
 
serviceユニットの場合、systemdがプロセスの開始と終了を担当する(そのため、serviceとして開始されたプロセスの親プロセスは必ずsystemdになる)のだが、scopeの場合はそうではなく、外で開始されたプロセスをscopeに登録し、リソース等を管理する。
また、serviceでは基本的に一つのメインプロセスを管理する(そのプロセスの子プロセスも管理下に入るが、あくまでメインは一つ)が、scopeは複数のプロセスを管理する。



[^1]: https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html (公式ドキュメント)
[^2]: https://www.freedesktop.org/software/systemd/man/latest/systemd.resource-control.html (```man systemd.resource-control```で閲覧できるマニュアルと同じ内容)
[^3]: https://systemd.io/CONTROL_GROUP_INTERFACE/  (systemd公式サイトのリソース管理に関する解説)