Ubuntuの次の長期サポート版のリリースが近づいています。それに合わせて、
さて、
今回も引き続き、
まずは、
なお、
今回の実行例は、
子cgroupを持つ場合の制約
第37回で紹介したように、
この動きを紹介しましょう。
プロセスが所属した状態で子cgroupで使うコントローラを登録しようとした場合の動き
![図1 root cgroupに子cgroupで使うコントローラを登録 図1 root cgroupに子cgroupで使うコントローラを登録](/assets/images/admin/serial/01/linux_containers/0039/thumb/TH320_001.jpg)
まずはroot cgroupで、
# echo "+io +memory +pids" > /sys/fs/cgroup/cgroup.subtree_control(子cgroupで使えるコントローラの登録)
上のようにio、
# mkdir /sys/fs/cgroup/test01(test01 cgroupの作成) # cat /sys/fs/cgroup/test01/cgroup.controllers(test01で使えるコントローラの確認) io memory pids
test01
cgroupを作成すると、
![図2 子cgroupを作成しプロセスを追加 図2 子cgroupを作成しプロセスを追加](/assets/images/admin/serial/01/linux_containers/0039/thumb/TH800_002.jpg)
このtest01
にプロセスを登録してみましょう
# echo $$ > /sys/fs/cgroup/test01/cgroup.procs(プロセスの追加) # cat /sys/fs/cgroup/test01/cgroup.procs(追加したプロセスの確認) 3129 3139
無事に登録できましたね。ここまではこれまで紹介してきたcgroup v2で行う操作と同じです。
この状態で、test01
の子cgroupで使うコントローラをcgroup.
ファイルに登録してみましょう。
# echo "+io +memory +pids" > /sys/fs/cgroup/test01/cgroup.subtree_control bash: echo: write error: Device or resource busy
エラーになりました
![図3 プロセスが登録されているので子cgroupで使うコントローラを登録しようとするとエラー 図3 プロセスが登録されているので子cgroupで使うコントローラを登録しようとするとエラー](/assets/images/admin/serial/01/linux_containers/0039/thumb/TH800_003.jpg)
このように、
それでは、test01
にプロセスが所属しない状態にして、
# echo $$ > /sys/fs/cgroup/cgroup.procs(プロセスをrootに登録しtest01から削除) # cat /sys/fs/cgroup/test01/cgroup.procs(test01にはプロセスがない状態)
test01
に登録したプロセスをroot cgroupに移動させました
![図4 cgroupからプロセスをroot cgroupに移動し削除 図4 cgroupからプロセスをroot cgroupに移動し削除](/assets/images/admin/serial/01/linux_containers/0039/thumb/TH320_004.jpg)
この状態で先ほどと同様にtest01
に、
# echo "+io +memory +pids" > /sys/fs/cgroup/test01/cgroup.subtree_control(登録してもエラーはでない) # cat /sys/fs/cgroup/test01/cgroup.subtree_control(コントローラは登録されている) io memory pids
無事、cgroup.
ファイルにコントローラが登録できました
![図5 プロセスが所属していないcgroupに子で使うコントローラを登録 図5 プロセスが所属していないcgroupに子で使うコントローラを登録](/assets/images/admin/serial/01/linux_containers/0039/thumb/TH320_005.jpg)
ここまでで、
子cgroupで使えるコントローラを登録した状態でプロセスを登録しようとした場合の動き
次は、
![図6 さらに子cgroupを作成 図6 さらに子cgroupを作成](/assets/images/admin/serial/01/linux_containers/0039/thumb/TH320_006.jpg)
先ほどの例で作成したtest01
に子cgroupを作ってみましょう。test01
のcgroup.
ファイルには、
# mkdir /sys/fs/cgroup/test01/test02(test02の作成) # cat /sys/fs/cgroup/test01/test02/cgroup.controllers (test02ではtest01で登録したコントローラが使えるようになっている) io memory pids
test02
という名前のcgroupを作りました。test02
で使えるコントローラを確認すると、test01
のcgroup.
で設定したコントローラが表示されています
つまりtest01
cgroupは子孫にリソースを分配している状態です。
この状態で、test01
にプロセスを登録してみます。
# echo $$ > /sys/fs/cgroup/test01/cgroup.procs bash: echo: write error: Device or resource busy
プロセスを登録しようとすると、test01
は子孫にリソースを分配しているからです。
![図7 リソースを分配しているcgroupにはプロセスを登録できない 図7 リソースを分配しているcgroupにはプロセスを登録できない](/assets/images/admin/serial/01/linux_containers/0039/thumb/TH800_007.jpg)
ここまでの実行例で、
- 自身にプロセスが登録された状態で、
子cgroupに対してコントローラが使えるように設定しようとするとエラーになる - 子cgroupに対してコントローラが使えるように設定した状態で、
自身にプロセスを登録しようとするとエラーになる
第37回で紹介したとおり、
子cgroupで使えるコントローラを登録していない場合の動き
それでは、cgroup.
にコントローラが登録されていない状態)
確認してみましょう。
# cat /sys/fs/cgroup/test01/cgroup.subtree_control(test01では子孫用にコントローラ登録なし)
このように、test01
ではcgroup.
は空で、test01
の子cgroup test02
にプロセスを登録します。
# echo 3129 > /sys/fs/cgroup/test01/test02/cgroup.procs(test01/test02にプロセスを登録) # cat /sys/fs/cgroup/test01/test02/cgroup.procs(test02に登録されている) 3129
子cgroupであるtest02
にプロセスが登録された状態で、test01
にプロセスを登録してみましょう。
# echo 3108 > /sys/fs/cgroup/test01/cgroup.procs(test01にもプロセスを登録) # cat /sys/fs/cgroup/test01/cgroup.procs(エラーなく登録されている) 3108
上の実行例のように、
![](39_cgroup2_leaf8.png)
![図8 子cgroupを持つがリソース分配をしていない場合はプロセスが所属可能 図8 子cgroupを持つがリソース分配をしていない場合はプロセスが所属可能](/assets/images/admin/serial/01/linux_containers/0039/thumb/TH800_008.jpg)
まとめると、
- 自身にプロセスが所属していないときだけ、
子孫にリソースを分配できる (子でコントローラが使えるように設定できる) - コントローラによるリソース制御を行わない場合は、
ツリー末端以外の任意のcgroupにもプロセスが所属できる
という制約がcgroup v2には存在します。ただし、
- root cgroupには以上の制約はない
となります。
cgroup状態通知
cgroupで、
プロセスの管理を行う場合、
cgroup v1、
cgroup v1での状態通知
cgroup v1では、
ファイル名 | notify_ |
release_ |
---|---|---|
説明 | cgroupにタスクが存在しなくなった場合にrelease_ |
cgroupにタスクが存在しなくなった場合に実行するプログラム |
値 | 0 or 1 | プログラムのフルパス |
デフォルト値 | 親cgroupの値を引き継ぐ。rootでは0 | なし |
ファイルの場所 | 全cgroup | root cgroup |
cgroupにタスクが存在しなくなったときに、release_
に登録したプログラムを実行する場合には、notify_
ファイルに"1"を書き込みます。デフォルトは上位cgroupの値がコピーされます。
# cat /sys/fs/cgroup/pids/notify_on_release 0 (root cgroupでは0が設定されている) # mkdir /sys/fs/cgroup/pids/test01 (test01を作成) # cat /sys/fs/cgroup/pids/test01/notify_on_release 0 (cgroup作成直後は親cgroupの値を継承)
root cgroupでは"0"がデフォルト値です。このまま子cgroup "test01"を作成したところ、notify_
の値も"0"に設定されました。
root cgroupの値を"1"に変更してみましょう。
# echo 1 > /sys/fs/cgroup/pids/notify_on_release (root cgroupのnotify_on_releaseを1に変更) # mkdir /sys/fs/cgroup/pids/test02 (test02を作成) # cat /sys/fs/cgroup/pids/test02/notify_on_release 1 (親の値を継承している) # cat /sys/fs/cgroup/pids/test01/notify_on_release 0 (親の値が変わっても既存の子孫には反映されない)
"1"に変更したあとに、notify_
は"1"になりました。ただし、notify_
の値は変わっていません。あくまで作成時に親の値を参照して設定するだけです。
それではrelease_
に登録するプログラムを準備しましょう。このプログラムが実行される場合は、
#!/bin/sh
rmdir /sys/fs/cgroup/pids/$1
logger "cgroup $1 is removed."
cgroupにタスクが存在しなくなった通知を受け取った場合には、
それでは試してみましょう。
さきほどのシェルスクリプトを/usr/
に置き、release_
に登録します。
# echo "/usr/local/bin/rmcg.sh" > /sys/fs/cgroup/pids/release_agent (release_agentにプログラムを登録) /usr/local/bin/rmcg.sh # cat /sys/fs/cgroup/pids/release_agent (登録された) /usr/local/bin/rmcg.sh
登録されたので、notify_
が有効な"test02"にプロセスを登録して、
# echo $$ > /sys/fs/cgroup/pids/test02/tasks (test02 cgroupにプロセスを登録) # grep $$ /sys/fs/cgroup/pids/test02/tasks 1729 (登録された) # echo $$ > /sys/fs/cgroup/pids/tasks (test02 cgroupからプロセスを削除=root cgroupに移動)
では"test02" cgroupがどうなったか確認してみましょう。
# ls -ld /sys/fs/cgroup/pids/test02 ls: cannot access '/sys/fs/cgroup/pids/test02': No such file or directory (test02 cgroupが存在しない) # tail -n1 /var/log/syslog Mar 19 20:35:35 xenial root: cgroup /test02 is removed. (loggerコマンドによりsyslogにログが書き込まれている)
"test02" cgroupにタスクが存在しなくなったので、release_
が呼び出され、
同じ操作を"test01"に対して行っても、notify_
が"0"でしたので、
# cat /sys/fs/cgroup/pids/test01/notify_on_release 0 (test01のnotify_on_releaseは0) # echo $$ > /sys/fs/cgroup/pids/test01/tasks (test01にプロセスを登録) # echo $$ > /sys/fs/cgroup/pids/tasks (test01からプロセスを削除) # ls -ld /sys/fs/cgroup/pids/test01 drwxr-xr-x 2 root root 0 Mar 19 20:25 /sys/fs/cgroup/pids/test01 (test01は消去されていない)
実際には、notify_
の値を変えて、release_
に登録するだけですので変えられません。
cgroup v1が持つ通知は以上のような仕組みです。v1では、
cgroup v2
それではcgroup v2の機能を見てみましょう。
cgroup v2ではrootではないcgroupには、cgroup.
というファイルが存在します。このファイルの中身を見れば、
ファイル名 | cgroup. |
---|---|
説明 | cgroup内のプロセスの有無を示すパラメータを含む |
値 | プロセスが存在するとき populated 0 、populated 1 |
デフォルト値 | populated 0 |
ファイルの場所 | root以外 |
このファイルにはpopulated
という項目の行だけが存在しており、
# ls /sys/fs/cgroup/test01/ (子cgroupが存在しない) cgroup.controllers cgroup.events cgroup.procs cgroup.subtree_control # cat /sys/fs/cgroup/test01/cgroup.procs (プロセスが所属していない) # cat /sys/fs/cgroup/test01/cgroup.events (populatedの値が0) populated 0
この例では"test01"は子cgroupを持たず、populated
の値は"0"です。この"test01"にプロセスを所属させて、cgroup.
の中身を見てみましょう。
# echo $$ > /sys/fs/cgroup/test01/cgroup.procs (プロセスを登録) # grep $$ /sys/fs/cgroup/test01/cgroup.procs (登録されたことを確認) 3081 # cat /sys/fs/cgroup/test01/cgroup.events (populatedの値が1になっている) populated 1
populated
の値が"1"になりましたね。
ここで別のシェルを開いてinotifywatch
コマンドでcgroup.
ファイルを監視し、
# inotifywait -m /sys/fs/cgroup/test01/cgroup.events
Setting up watches.
Watches established.
(ここでtest01からプロセスをrootに移動)
/sys/fs/cgroup/test01/cgroup.events MODIFY cgroup.events
"test01"からプロセスが削除されると、cgroup.
ファイルに"MODIFY"というinotifyイベントが発生しました。このイベントは"test01"の子cgroupに対する変化でも発生します。
# mkdir /sys/fs/cgroup/test01/test02 (test01配下にtest02を作成) # echo $$ > /sys/fs/cgroup/test01/test02/cgroup.procs (test02にプロセスを登録)
上の例のように"test01"の下に"test02"を作成し、cgroup.
にinotifyイベントが発生します。
# inotifywait -m /sys/fs/cgroup/test01/cgroup.events
Setting up watches.
Watches established.
/sys/fs/cgroup/test01/cgroup.events MODIFY cgroup.events
(test01自身ではなく子のtest02の変化もtest01で知れる)
もちろん、test02
内のcgroup.
ファイルにも同じイベントが発生しています。
このように、cgroup.
ファイルに発生するイベントを監視して何らかの処理を行うことができます。cgroupを監視するプログラムがcgroup.
を監視し、
まとめ
今回は、
また、
cgroup v1が持っていて、
その分cgroup v2は、
実際、
今回まででcgroup v2のコントローラに関わらない基本機能で、