前回は、
今回からは、
サブシステム
名前空間と同様に、
サブシステムには以下のようなものがあります。
| サブシステム | 機能の概要 | 実装されたバージョン | 
|---|---|---|
| cpu | CPUのスケジューリングを制御 | |
| 相対配分…グループ間のCPU時間の割当を割合で指定 | 2. | |
| 帯域制御…単位時間内にグループ内のタスクが実行できる合計時間を制限 | 3. | |
| cpuacct | グループ内のタスクが消費するCPU時間をレポート | 2. | 
| cpuset | グループへのCPU、 | 2. | 
| device | グループ内のタスクのデバイスへのアクセスの許可、 | 2. | 
| freezer | グループ内のプロセス全てを同時に一時停止・ | 2. | 
| memory | グループ内のタスクが消費するメモリリソースのレポートと制限 | 2. | 
| net_ | グループ内のプロセスが発信するパケットの制御。パケットに識別子を付与。 | |
| tcで制御 | 2. | |
| netfilterで制御 | 3. | |
| blkio | ブロックデバイスに対する制限 | |
| 重みづけ配分…グループ間のI/ | 2. | |
| 帯域制限…グループ内のタスクが各デバイスに対して行える操作数の制限 | 2. | |
| perf_ | cgroup単位でのperfツールの使用 | 2. | 
| net_ | グループ内のタスクのネットワークの優先度の制御 | 3. | 
| hugetlb | cgroupからのhugetlbの使用 | 3. | 
表1にある
お使いのシステムで使用可能なサブシステムは、/proc/で確認できます。
$ cat /proc/cgroups #subsys_name hierarchy num_cgroups enabled cpuset 0 1 1 cpu 0 1 1 cpuacct 0 1 1 memory 0 1 1 devices 0 1 1 freezer 0 1 1 blkio 0 1 1 perf_event 0 1 1
カーネルによってはサブシステムの一部がモジュールとなっていて、
$ sudo modprobe cls_cgroup $ cat /proc/cgroups | grep net_cls net_cls 0 1 1
前の実行例の環境で新たにnet_/proc/にnet_
サブシステムのマウント
cgroupfsをマウントする際にはサブシステムごとにマウントできます。前回の説明の際にオプションとして-o cpuという指定をしました。これはcpuサブシステムをマウントするという意味になります。同様に別のマウントポイントに-o memoryと指定してmemoryサブシステムをマウントしてみましょう。
$ sudo mount -t tmpfs cgroup /sys/fs/cgroup $ sudo mkdir /sys/fs/cgroup/cpu $ sudo mount -n -t cgroup -o cpu cgroup /sys/fs/cgroup/cpu(ここまでは前回の例と同じ) $ sudo mkdir /sys/fs/cgroup/memory (memoryサブシステム用のディレクトリ作成) $ sudo mount -n -t cgroup -o memory cgroup /sys/fs/cgroup/memory (memoryサブシステムを指定してマウント) $ ls /sys/fs/cgroup/memory/ cgroup.clone_children memory.move_charge_at_immigrate cgroup.event_control memory.numa_stat cgroup.procs memory.oom_control memory.failcnt memory.soft_limit_in_bytes memory.force_empty memory.stat :(略)
/sys/以下にmemoryサブシステムをマウントしました。cpuサブシステムとは作られているファイルが違いますね。ここで見たように複数の階層構造を持てるというのもcgroupの特徴のひとつです。
また、
$ sudo umount /sys/fs/cgroup/cpu
$ sudo umount /sys/fs/cgroup/memory
$ sudo mkdir /sys/fs/cgroup/cpu_memory
$ sudo mount -n -t cgroup -o cpu,memory cgroup /sys/fs/cgroup/cpu_memory
              (cpu、memoryサブシステムを同時にマウント)
$ ls /sys/fs/cgroup/cpu_memory/
cgroup.clone_children      memory.memsw.limit_in_bytes
cgroup.event_control       memory.memsw.max_usage_in_bytes
cgroup.procs               memory.memsw.usage_in_bytes
cpu.cfs_period_us          memory.move_charge_at_immigrate
cpu.cfs_quota_us           memory.numa_stat
  : (略)
ディレクトリ内を確認してみると、
ただし、
$ sudo mount -n -t cgroup -o memory cgroup /sys/fs/cgroup/memory Mount: cgroup already mounted or /sys/fs/cgroup/memory busy
別階層へのプロセスの登録
サブシステムの解説とは少し外れますが、
前回、
それでは、
$ sudo mount -n -t cgroup -o cpu cgroup /sys/fs/cgroup/cpu $ sudo mkdir /sys/fs/cgroup/cpu/test1 (cpu以下にtest1グループを作成) $ sudo mount -n -t cgroup -o memory cgroup /sys/fs/cgroup/memory $ sudo mkdir /sys/fs/cgroup/memory/test2 (memory以下にtest2グループを作成) $ echo $$ | sudo tee -a /sys/fs/cgroup/cpu/test1/tasks (PIDをtest1に登録) 1738 $ cat /sys/fs/cgroup/cpu/test1/tasks (test1のtasksを確認) 1738 2152 $ echo $$ | sudo tee -a /sys/fs/cgroup/memory/test2/tasks (PIDをtest2に登録) 1738 $ cat /sys/fs/cgroup/memory/test2/tasks (test2のtasksを確認) 1738 2171 $ cat /sys/fs/cgroup/cpu/test1/tasks (test1のtasksを確認) 1738 2188
memoryサブシステムに作成したtest2にプロセスを登録した後も、test1にはPID 1738は登録されたままになっています。つまりcgroupfsの別のマウントであれば、
実際のシステムでも、
サブシステムの説明と複数のcgroupfsへのプロセスの登録の話が済んだところで、
なお各サブシステムの説明は、
cpuサブシステム
cpuサブシステムでは、
- 帯域制限
 - 単位時間内にcgroup内のタスクがCPUを使用できる合計時間を制限
 - 相対配分
 - cgroup内のタスクが使えるcpu時間の割り当てを相対的に設定
 
最初に帯域制限を試してみましょう。まずは準備です。test1とtest2というcgroupを作成します。
sudo mkdir /sys/fs/cgroup/cpu/test1 /sys/fs/cgroup/cpu/test2
test1グループに現在のシェルを登録します。
$ echo $$ | sudo tee -a /sys/fs/cgroup/cpu/test1/tasks 2278
ここで別のシェルを実行し、test2グループに登録します。
$ echo $$ | sudo tee -a /sys/fs/cgroup/cpu/test2/tasks 2953
帯域制御を行うにはcpu.に実行できる時間をマイクロ秒単位で設定します。これでcpu.で指定されている単位時間内でどれだけCPUを使用できるかが設定できます。設定方法はtasksファイルへの書き込みと同様にcpu.に設定したい値を書き込みます。
ここではtest1グループには5ミリ秒、test2グループには10ミリ秒を設定してみましょう。
$ cat /sys/fs/cgroup/cpu/test1/cpu.cfs_period_us (単位時間は100ミリ秒) 100000 $ cat /sys/fs/cgroup/cpu/test2/cpu.cfs_period_us (単位時間は100ミリ秒) 100000 $ echo 5000 | sudo tee /sys/fs/cgroup/cpu/test1/cpu.cfs_quota_us (5ミリ秒の制限を設定) 5000 $ echo 10000 | sudo tee /sys/fs/cgroup/cpu/test2/cpu.cfs_quota_us (10ミリ秒の制限を設定) 10000 $ cat /sys/fs/cgroup/cpu/test1/cpu.cfs_quota_us (設定の確認) 5000 $ cat /sys/fs/cgroup/cpu/test2/cpu.cfs_quota_us (設定の確認) 10000
さて、
$ while :; do true ; done
これをtop -p 2278,2953としてCPU使用率を確認すると、
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 2953 karma 20 0 24768 7224 1632 R 10.0 0.7 0:54.35 bash 2278 karma 20 0 24860 7364 1680 R 5.0 0.7 0:27.72 bash
相対配分も簡単に説明しておきましょう。相対配分を行う場合は各cgroup以下のcpu.ファイルを使います。このファイルに書かれた数値が1024であるtest1グループと、test2グループがある場合、test1グループにはtest2グループの倍のCPU時間が割り当てられます。
帯域制御についてはカーネル付属文書のsched-bwc.
cpuacctサブシステム
cpuacctはcgroup内のプロセスが使用したCPU時間のレポートが生成されます。このサブシステムには設定するような項目はありません。詳しくはカーネル付属文書のcpuacct.
たとえば以下はcgroup内のプロセスが消費したCPU時間のレポートを表示しています
$ cat /sys/fs/cgroup/cpuacct/test1/cpuacct.usage 462998808
cpusetサブシステム
cpusetサブシステムは、cpuset.、cpuset.ファイルに設定します。ルート直下のcgroup中のこの2つのファイルはシステム上の全てのCPUとメモリノードが設定されています。
ルート以下に新たにcgroupを作成した場合はこの2つのファイルは空になっており、tasksファイルにプロセスを登録できませんので注意が必要です。ただしcgroupを作成した際、cgroup.ファイルに1を書き込んでおけば、
この2つのファイルには"-"
$ echo "0-1,5" | tee /sys/fs/cgroup/cpuset/test1/cpuset.cpus
といった風に設定できます。
詳しくはカーネル付属文書のcpuset.
devicesサブシステム
devicesサブシステムはこれまでに説明したような数値で表されるリソースを制限するサブシステムではなく、
Linuxカーネルの持つコンテナ機能では、
アクセス権の設定はcgroup内のdevices.devices.
- タイプ
 - ブロックデバイス
(b)、 キャラクタデバイス (c)、 両方 (a)  - デバイスノード番号
 - メジャー番号とマイナー番号をコロンで連結
 - アクセス権
 - 読み取り
(r)、 書き込み (w)、 デバイスファイルの作成 (m)  
以下はtest1グループに対して、
$ echo a | sudo tee /sys/fs/cgroup/devices/test1/devices.deny a $ echo 'c 1:3 rwm' | sudo tee /sys/fs/cgroup/devices/test1/devices.allow c 1:3 rwm $ cat /sys/fs/cgroup/devices/test1/devices.list c 1:3 rwm
devices.,devices.ファイルは設定専用のファイルですので、devices.ファイルで確認します。
詳しくはカーネル付属文書のdevices.
freezerサブシステム
freezerサブシステムはcgroupのサブシステムの中では少し変わった存在です。freezerはリソースを制御するのではなく、
実行中のコンテナの状態を保存して停止させ、docker pause/コマンドではこの機能を使っています。
ではfreezerサブシステムの使い方を簡単に見ておきましょう。今までの例と同様に、/sys/にマウントし、test1グループを作った上で試しています。freezerサブシステムには以下のようなファイルが存在します。
$ ls /sys/fs/cgroup/freezer/test1 cgroup.clone_children cgroup.procs notify_on_release cgroup.event_control freezer.state tasks
freezerサブシステムを使ってcgroup内のタスクの状態を変えたり、freezer.ファイルを使います。以下の3つの状態が存在し、freezer.ファイルに、
- FROZEN
 - タスクは一時停止中
 - FREEZING
 - タスクの一時停止処理を実行中
 - THAWED
 - タスクは実行中
 
状態を変更するにはfreezer.ファイルに変化させたい状態の文字列を書きます。一時停止させたいときはFROZENを、THAWEDを書きます。FREEZINGは状態の確認専用の値であり、
まずは他のサブシステムと同様にcgroupのtasksにシェルのPIDを登録します。
$ echo $$ | sudo tee /sys/fs/cgroup/freezer/test1/tasks 1732
ここで別のシェルを起動します。
$ ps u -p 1732 USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND karma 1732 0.0 0.7 24916 7436 pts/0 S+ 15:03 0:00 -bash $ cat /sys/fs/cgroup/freezer/test1/freezer.state THAWED
プロセスの状態の変化を見るためにpsコマンドを実行しています。freezer.ファイルで状態を確認するとTHAWEDとなっており、
この状態でfreezer.ファイルにFROZENを書き込み、
$ echo 'FROZEN' | sudo tee /sys/fs/cgroup/freezer/test1/freezer.state FROZEN $ cat /sys/fs/cgroup/freezer/test1/freezer.state FROZEN $ ps u -p 1732 USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND karma 1732 0.0 0.7 24916 7436 pts/0 D+ 15:03 0:00 -bash
書き込んだ後にfreezer.ファイルを確認するとFROZENとなっており、psコマンドのSTAT欄を見てもプロセスの状態が変化しているのがわかります。
再度実行状態にするにはfreezer.にTHAWEDを書き込みます。
$ echo 'THAWED' | sudo tee /sys/fs/cgroup/freezer/test1/freezer.state THAWED $ cat /sys/fs/cgroup/freezer/test1/freezer.state THAWED
状態がTHAWEDに変わっており、
なお、freezer.ファイルは存在しません。
freezerサブシステムもカーネル付属文書にfreezer-subsystem.
まとめ
今回は最初に現在カーネルに実装されているサブシステムの紹介を行いました。続くサブシステムのマウントやプロセスの登録について説明の部分で、
今回紹介した特徴をあげる前に、
- cgroupはcgroupfsという特別なファイルシステムで管理する
 - cgroup はディレクトリで表される
 - cgroupfsをマウントするとデフォルトでは全てのプロセスがトップディレクトリのグループにデフォルトで登録される子プロセスはデフォルトでは親のプロセスと同じcgroupに属する
 - cgroupは階層構造を取り、
グループ間の親子関係を構成できる  - システム上のプロセスは必ずcgroupfsのトップディレクトリ以下のcgroupのどれか1つだけに属する
 
ここに今回紹介した特徴が加わります。
- cgroupを使って制御を行いたい機能ごとに
『サブシステム』 が存在する  - cgroupfsには単独でサブシステムをマウントすることも、
任意の複数のサブシステムを同時にマウントこともできる  - cgroupfsは複数の階層を持つことができる
 - 同一のサブシステムを同時に別のマウントポイントにマウントすることはできない
 
最初に紹介した通り、
