LXCで学ぶコンテナ入門 -軽量仮想化環境を実現する技術

第37回 Linuxカーネルのコンテナ機能 ― cgroupの改良版cgroup v2[1]

この記事を読むのに必要な時間:およそ 5 分

cgroup v2の開発

先に紹介したような問題点を解決するため,cgroupメンテナのTejun Heo氏によりcgroup v2の開発が始まりました。

この機能は,開発時点では"Unified hierarchy"と呼ばれていました。この機能は3.16カーネル(2014年8月)でマージされました。この時点では開発中の機能ということで,マウントオプションとして__DEVEL__sane_behaviorというオプションを与える必要がありました。

# mount -n -t cgroup -o __DEVEL__sane_behavior cgroup /sys/fs/cgroup/

"sane_behavior"(まともなふるまい)という名前に,cgroupを改良したいという強い思いが感じられますね(笑)⁠

ちなみにcgroupのコード内で使われている変数でも,v1用の変数には"_legacy_"という文字列が,v2用の変数には"default"を表していると思われる"_dfl_"という文字列が入っており,ここにも同じような強い思いを感じます(笑)⁠

3.16でマージされたあとも開発は続き,正式な機能としてマージされたのは4.5カーネル(2016年3月)の時点でした。この時点で機能が"stable"であるとされ,名前も"cgroup v2"となりました。

4.5時点でcgroup v2で使えるサブシステムはio,memory,pidsでした。カーネル付属文書にはcpuサブシステムの記載もあるのですが,執筆時点の最新カーネルである4.14の時点でもcpuサブシステムの機能はcgroup v2にはマージされていません(カーネル付属文書cgroup-v2.txtには注意書きがあります)⁠

4.14カーネルの時点でcgroup v2で使えるサブシステムはio,memory,pids,rdma,perf_eventです。

cgroup v2の特徴

単一階層構造

開発時点から"Unified hierarchy"と呼ばれていた通り,cgroup v2は階層はひとつです。つまりマウントした時点でそのマウントポイント以下に作られるcgroupツリーがシステム上の唯一のcgroup v2のcgroupfsとなります。

サブシステム間の連携

単一階層構造になりましたので,かならずすべてのサブシステムが同じcgroupツリー内で管理されます。これにより,サブシステム間の連携ができるようになりました。

プロセス単位の管理

cgroupでコントロールする単位はプロセス単位です。これにより,メモリのようにリソースをメモリ単位で管理するサブシステムでも矛盾なくリソースの分配ができるようになり,全サブシステムで統一したポリシーでコントロールができるようなりました。

しかし,リソース管理がプロセス単位となったことが,cpuサブシステムがマージされない理由になっています。このため,cgroup v2でスレッド単位の管理ができるような機能が4.14カーネルでサポートされました。この機能については,筆者もまだちゃんと評価できていませんので,連載の後の回で紹介したいと思います。

スレッド単位の管理をサポートしたとはいえ,デフォルトではプロセス単位の管理です。スレッド単位で管理するためには,そのための機能を有効にする必要があります。

プロセスが所属できるのは末端のcgroupのみ

先にcgroup v1の問題点として,ツリー構造の途中のノードにもタスクが存在できることを挙げました。

この問題を解決するため,子cgroupにリソースを分配できるのは,自身にプロセスが所属していないときだけとなりました。つまり,コントローラがひとつでも有効になっている階層では,常にツリーの末端にあるcgroupにのみプロセスが所属しているということです。

ただし,root cgroupはこの制約を受けません。

サブシステムの制御はcgroupごと

cgroup v1はマウント時にその階層で使うサブシステムを指定しました。すると,その階層全体でそのサブシステムが使えました。サブシステムごとに階層の構造を変えたい場合は,別にマウントし,別の階層で管理しました。

cgroup v2では階層はひとつですが,cgroupごとに自身の子cgroupでどのサブシステムを有効化するかを選択できます。

図2 cgroup-v2のサブシステム制御

図2 cgroup-v2のサブシステム制御

図のように,cgroup v2全体ではcpu,io,memory,pidsのサブシステムが利用できるようになっているとします。

ここでroot cgroupで,子cgroupではcpu,memory,pidsサブシステムのみ使えるように設定すると,"cgroup A"と"cgroup D"にはcgroup自身の制御を行うファイルのほか,cpu,memory,pidsサブシステムに関係するファイルのみ表れます。

ここで"cgroup A"で,子cgroupではmemoryサブシステムのみ有効とする設定を行うと,"cgroup B"と"cgroup C"では,"cgroup A"からメモリリソースのみ分配を受けられ,cpuについては"cgroup A"で使えるCPU時間を特に制限を受けずに競争しながら使うことになります。

また,リソースはトップダウンで分配されます。つまり,親cgroupで有効に設定されているサブシステムだけを子cgroupに対して有効化できます。

図で説明すると,"cgroup B"と"cgroup C"では,"cgroup A"で有効なサブシステムだけしかリソース制御ができません。"cgroup A"はroot cgroupでcpu,memory,pidsのみ有効となるように設定されていますので,"cgroup B","cgroup C"に対していきなりioサブシステムを使えるように設定できません。

cgroup操作はcgroup v1と同じ

cgroupの操作はv1と同じです。

cgroupfsをマウントして,通常のファイルシステムの操作と同様に,ディレクトリ操作で階層の管理を行います。リソースの分配はファイルへの書き込みで行い,リソース分配の設定値や統計値の取得はファイルに対する読み取りで行います。

実はcgroup v1の問題点として,リソース分配をcgroupfsの持つファイル入出力で行うことも挙げられていました。しかし,このインターフェース部分についてはcgroup v1との互換性が保たれましたので,cgroupを利用する側のプログラムの基本的な動きを変更する必要はなく,影響が小さくて済みました。

cgroup v1との共存

cgroup v2は徐々に実装が進んでいますので,cgroup v2だけを使用した状態では,v1を使って実現していた機能をすべて実現できません。

そこで,cgroup v1とv2を同時に使用できます。これにより徐々にcgroup v2へ移行できます。systemdでもcgroup v1とv2の両方を使った"hybrid mode"という機能が実装されています。

cgroup v1とv2を同時に使った場合は,cgroup v2をマウントする時点で,cgroup v1で使われていないサブシステムのみがcgroup v2に現れます。

cgroup状態通知

自身または自分の子孫のcgroupにプロセスが参加した場合と,プロセスがなくなった場合の通知をpoll()inotify()dnotify()で受け取れます。

規約の整備

cgroup v1では,cgroupを作成したときに作成されるファイルに関する決まりを全くもっていませんでした。cgroup v2では,リソース分配のモデルが定義され,そのモデルに従ってファイル名やファイルのフォーマットなどの,cgroup操作に関わるインターフェースがきちんと定義されました。cgroupやサブシステムに関わる操作を行う場合に曖昧な点はなくなりましたし,ファイル名で機能がわかるようになりました。

まとめ

今回はcgroup v1で指摘されてきた問題点と,その問題点を解決するためにcgroup v2が持つことになった特徴について説明しました。

この連載では珍しく,今回は実行例がほとんどない記事になってしまったので,cgroup v2の特徴は少しわかりづらかったかもしれません。

次回は,実際にcgroup v2の操作を紹介しながら,今回説明したcgroup v2の特徴をおさらいしたいと思います。

著者プロフィール

加藤泰文(かとうやすふみ)

2009年頃にLinuxカーネルのcgroup機能に興味を持って以来,Linuxのコンテナ関連の最新情報を追っかけたり,コンテナの勉強会を開いたりして勉強しています。英語力のない自分用にLXCのmanページを日本語訳していたところ,あっさり本家にマージされてしまい,それ以来日本語訳のパッチを送り続けています。

Plamo Linuxメンテナ。ファーストサーバ株式会社所属。

Twitter:@ten_forward
技術系のブログ:http://tenforward.hatenablog.com/

コメント

コメントの記入