Ubuntuの次の長期サポート版のリリースが近づいています。それに合わせて、LXC関連のプロダクトもメジャーバージョンアップし、新しい長期サポート版をリリースしました。筆者はlinuxcontainers.orgやLXC/LXD関連の翻訳をやっていますので、一気に翻訳対象が増えて大変です。一度には出来ないので毎日少しずつ進めています。
さて、前回は、cgroup v2で使うファイルや、基本操作、親cgroupから子cgroupで使うコントローラを指定する方法を紹介しました。
今回も引き続き、cgroup v2が持つ基本的な機能を紹介していきましょう。
まずは、cgroup v2の特徴のひとつである、プロセスが末端のcgroupにしか所属できないことを紹介します。そのあと、cgroupが持つ状態通知機能について紹介します。この機能については、これまで連載で紹介していなかったv1の機能を含めて紹介します。
なお、この連載ではこれまでcgroupで各種のリソース制限を行う仕組みを「サブシステム」と呼んでいました。しかし、cgroup v2の文書では "Controller" が使われており、最近は「コントローラ」と呼ばれることが多いので、今回から「コントローラ」に統一しました。呼び方だけの話です。
今回の実行例は、cgroup v1についてはUbuntu 16.04LTS上で、cgroup v2については自由にcgroupがマウントできるPlamo Linux 6.2(カーネル4.11.12)上で実行しています。
子cgroupを持つ場合の制約
第37回で紹介したように、cgroup v1が持つ問題点のひとつであった「子cgroupのタスクと親cgroupの内部タスクの競合」問題を解決するために、リソース分配を行う場合は末端のcgroupにしかプロセスが所属できなくなりました。
この動きを紹介しましょう。
プロセスが所属した状態で子cgroupで使うコントローラを登録しようとした場合の動き
まずはroot cgroupで、子cgroupで使うコントローラを登録します(図1)。
上のようにio、memory、pidsコントローラを登録しました。続いてcgroupを作成します。
test01
cgroupを作成すると、先ほど登録したio、memory、pidsコントローラが使えるようになっています。
このtest01
にプロセスを登録してみましょう(図2)。
無事に登録できましたね。ここまではこれまで紹介してきたcgroup v2で行う操作と同じです。
この状態で、test01
の子cgroupで使うコントローラをcgroup.subtree_control
ファイルに登録してみましょう。
エラーになりました(図3)。
このように、自身にプロセスが所属している状態で、子cgroupでコントローラが使えるように設定しようとしてもエラーになります。
それでは、test01
にプロセスが所属しない状態にして、再度コントローラを登録してみます。
test01
に登録したプロセスをroot cgroupに移動させました(図4)。
この状態で先ほどと同様にtest01
に、子cgroupで使うコントローラを登録してみましょう。
無事、cgroup.subtree_control
ファイルにコントローラが登録できました(図5)。
ここまでで、cgroupにプロセスが登録された状態では、子cgroupで使うコントローラを登録できないことが確認できました。
子cgroupで使えるコントローラを登録した状態でプロセスを登録しようとした場合の動き
次は、子cgroupでコントローラが使える状態では、そのcgroupにプロセスを登録できないことを確認してみましょう。
先ほどの例で作成したtest01
に子cgroupを作ってみましょう。test01
のcgroup.subtree_control
ファイルには、子cgroupで使えるコントローラが登録された状態のままです。
test02
という名前のcgroupを作りました。test02
で使えるコントローラを確認すると、test01
のcgroup.subtree_control
で設定したコントローラが表示されています(図6)。
つまりtest01
cgroupは子孫にリソースを分配している状態です。
この状態で、test01
にプロセスを登録してみます。
プロセスを登録しようとすると、上のようにエラーとなります(図7)。test01
は子孫にリソースを分配しているからです。
ここまでの実行例で、次のような制約があることがわかりました。
- 自身にプロセスが登録された状態で、子cgroupに対してコントローラが使えるように設定しようとするとエラーになる
- 子cgroupに対してコントローラが使えるように設定した状態で、自身にプロセスを登録しようとするとエラーになる
第37回で紹介したとおり、コントローラを使ってリソース制限をする場合は、ツリー末端のcgroupにしかプロセスが登録できないようになっていることが確認できました。
子cgroupで使えるコントローラを登録していない場合の動き
それでは、子孫でリソース制限ができるように設定していない状態(cgroup.subtree_control
にコントローラが登録されていない状態)で、ツリー末端以外のcgroupにプロセスを所属させようとしたらどうなるでしょう?
確認してみましょう。
このように、test01
ではcgroup.subtree_control
は空で、子孫ではリソース制限はできない状態になっています。この状態でtest01
の子cgroup test02
にプロセスを登録します。
子cgroupであるtest02
にプロセスが登録された状態で、その親であるtest01
にプロセスを登録してみましょう。
上の実行例のように、特に制約を受けることなく、プロセスが登録できました(図8)。つまり、子孫でコントローラが使えるように設定していない状態では、ツリー末端以外のcgroupにもプロセスが所属できます。
まとめると、
- 自身にプロセスが所属していないときだけ、子孫にリソースを分配できる(子でコントローラが使えるように設定できる)
- コントローラによるリソース制御を行わない場合は、ツリー末端以外の任意のcgroupにもプロセスが所属できる
という制約がcgroup v2には存在します。ただし、root cgroupにはデフォルトですべてのプロセスが所属しますし、rootでリソース制限をすることはありませんので制約はありません。つまり、
となります。
cgroup状態通知
cgroupで、コントローラを使ってリソース制限を行う以外に、プロセスを管理するためにcgroupを使う場合があります。最近のLinuxでは、systemdが広く使われていますので、どちらかというとプロセスの管理に使われることのほうが一般的ですね。
プロセスの管理を行う場合、cgroup内にタスクやプロセスがなくなったことを知ることができれば便利です。このような場合に使える通知を行う仕組みがcgroup v1にもv2にも存在します。
cgroup v1、v2それぞれで試してみましょう。
cgroup v1での状態通知
cgroup v1では、あるcgroupにタスクが存在しなくなった場合に、あらかじめ登録してあるプログラムを実行できます。
表1 cgroup v1の状態通知に関係するファイル
ファイル名 |
notify_on_release |
release_agent |
説明 |
cgroupにタスクが存在しなくなった場合にrelease_agentに登録したプログラムを実行するかどうか |
cgroupにタスクが存在しなくなった場合に実行するプログラム |
値 |
0 or 1 |
プログラムのフルパス |
デフォルト値 |
親cgroupの値を引き継ぐ。rootでは0 |
なし |
ファイルの場所 |
全cgroup |
root cgroup |
cgroupにタスクが存在しなくなったときに、release_agent
に登録したプログラムを実行する場合には、notify_on_release
ファイルに"1"を書き込みます。デフォルトは上位cgroupの値がコピーされます。
root cgroupでは"0"がデフォルト値です。このまま子cgroup "test01"を作成したところ、そのまま親であるroot cgroupの値を引き継ぎ、"test01"のnotify_on_release
の値も"0"に設定されました。
root cgroupの値を"1"に変更してみましょう。
"1"に変更したあとに、新たに子cgroup "test02"を作ると、"test02"のnotify_on_release
は"1"になりました。ただし、このとき、先に作成した"test01"のnotify_on_release
の値は変わっていません。あくまで作成時に親の値を参照して設定するだけです。
それではrelease_agent
に登録するプログラムを準備しましょう。このプログラムが実行される場合は、引数としてroot cgroupからの相対パスが渡されます。次のようなシェルスクリプトを準備しました。
cgroupにタスクが存在しなくなった通知を受け取った場合には、そのcgroupを削除し、syslogにログを出力します。
それでは試してみましょう。
さきほどのシェルスクリプトを/usr/local/bin/rmcg.sh
に置き、実行権限をつけました。このスクリプトをrelease_agent
に登録します。
登録されたので、さきほど作成したnotify_on_release
が有効な"test02"にプロセスを登録して、すぐに削除してみましょう。
では"test02" cgroupがどうなったか確認してみましょう。
"test02" cgroupにタスクが存在しなくなったので、release_agent
が呼び出され、"test02" cgroupが削除され、syslog出力されています。
同じ操作を"test01"に対して行っても、"test01"はnotify_on_release
が"0"でしたので、何も起こりません。
実際には、cgroupごとにnotify_on_release
の値を変えて、処理を変えることはないかもしれませんが、このようにcgroupごとにタスクがなくなったときの処理の有無を設定できます。実行するプログラムはroot cgroupのrelease_agent
に登録するだけですので変えられません。
cgroup v1が持つ通知は以上のような仕組みです。v1では、cgroupにタスクがいなくなるたびにプロセスを起動しますのでコストが高くなります。
cgroup v2
それではcgroup v2の機能を見てみましょう。
cgroup v2ではrootではないcgroupには、cgroup.events
というファイルが存在します。このファイルの中身を見れば、自身または子孫にプロセスが所属しているかどうかがわかります。
表2 cgroup v2の状態通知に関係するファイル
ファイル名 |
cgroup.events |
説明 |
cgroup内のプロセスの有無を示すパラメータを含む |
値 |
プロセスが存在するとき populated 0 、存在しないとき populated 1 |
デフォルト値 |
populated 0 |
ファイルの場所 |
root以外 |
このファイルにはpopulated
という項目の行だけが存在しており、この値はプロセスが存在していれば"1"、存在していなければ"0"です。
この例では"test01"は子cgroupを持たず、プロセスも所属していないので、populated
の値は"0"です。この"test01"にプロセスを所属させて、cgroup.events
の中身を見てみましょう。
populated
の値が"1"になりましたね。
ここで別のシェルを開いてinotifywatch
コマンドでcgroup.events
ファイルを監視し、今度は"test01"からプロセスを削除してみましょう。
"test01"からプロセスが削除されると、cgroup.events
ファイルに"MODIFY"というinotifyイベントが発生しました。このイベントは"test01"の子cgroupに対する変化でも発生します。
上の例のように"test01"の下に"test02"を作成し、"test02"にプロセスを登録すると、次のように"test01"のcgroup.events
にinotifyイベントが発生します。
もちろん、このときtest02
内のcgroup.events
ファイルにも同じイベントが発生しています。
このように、cgroup.events
ファイルに発生するイベントを監視して何らかの処理を行うことができます。cgroupを監視するプログラムがcgroup.events
を監視し、イベントを受信した処理を行えますので、v1に比べるとコストが低くなりました。
まとめ
今回は、親子cgroup間の内部タスクの競合を解決するためにcgroup v2で導入された制約を紹介しました。
また、cgroup内のタスク状態が変化した場合の通知機能について、cgroup v1、v2の両方を実際に試しながら紹介しました。
cgroup v1が持っていて、今でも使われている機能はそのままに、v1が持っていた問題点をうまく解決していることがおわかりいただけたのではないでしょうか。
その分cgroup v2は、いろいろな制約があり複雑になったと思われるかもしれません。この複雑さは、cgroupを管理するプログラムが理解して担当すれば良いという考え方なのだと思います。
実際、現在のほとんどのディストリビューションではsystemdがcgroupをすべて管理しています。cgroup v2もsystemdの存在を念頭に置いているのだろうと思います。cgroup v2の通知機能もinotifyを使っており、常時起動しているプログラムから監視をすることが前提となっています。
今回まででcgroup v2のコントローラに関わらない基本機能で、どのようなケースでも使う機能の紹介が済みました。次回は、基本機能ですが、特権を持たないプロセスがcgroupを扱う場合の機能を紹介する予定です。