前回までに引き続き、今回もcgroup v2が持つ基本機能を紹介していきます。
Linuxでは、特権を持たないユーザでもユーザ名前空間を使ってコンテナを起動できます。その際、特権を持たないユーザがcgroupを操作できるように権限が委譲できるようになっています。今回は、権限を委譲する際の操作や動きについて、cgroup v1とv2それぞれの場合どのようになるのかを紹介していきましょう。
非特権ユーザに対する権限委譲
デフォルトではcgroupを操作する権限は特権ユーザにしかありません。一般ユーザ権限で起動する非特権コンテナを起動する場合などでは、一般ユーザにcgroupを操作できる権限を与える必要があります。
たとえば、図1のように一般ユーザ権限で"B"というコンテナを起動し、そのコンテナに対してリソース管理を行うためにcgroup Bを作成したとします。この場合、cgroup B以下に存在するツリーはコンテナ"B"を起動したユーザに管理させることになります。cgroup B以下のツリーを一般ユーザに管理させたい場合、権限を与えたいユーザに対して
- cgroup B以下にcgroupを作れる権限
- cgroup B以下でプロセスを自由に登録できる権限
を与えれば良いでしょう(図2)。
cgroup B上のコントローラを制御するファイルについては、一般ユーザに権限は与えないことが一般的でしょう。なぜなら、この制御ファイル群に書き込み権を与えると、コンテナ内の管理者が、コンテナにかけられた制限を自由に変更できることになります。
もちろん、コンテナの管理者にコンテナが使えるリソースを自由に変更させたいという場合もあるでしょうから、その場合はcgroup B内のファイルに書き込み権を与えます。
cgroup v1の場合
cgroup v1の場合、先に挙げた権限は表1のようになります。
表1 cgroup v1で委譲するのに必要な権限
|
権限 |
実際の書き込み権 |
1 |
cgroup B以下にcgroupを作れる権限 |
ディレクトリBへの書き込み権 |
2 |
cgroup B以下でプロセスを自由に登録できる権限 |
登録先cgroup内のcgroup.procs , tasks ファイルへの書き込み権 |
実際に試してみましょう。図1に対応させるためにcgroup_B
というcgroupを作成し、このcgroupの所有権をubuntu
ユーザにします。
これで子cgroupを作る準備はできましたので、実際にcgroup_C
とcgroup_D
を作ってみましょう。
通常のディレクトリを作成する際と同じで、cgroup_B
ディレクトリはubuntu
ユーザ所有ですので、その配下にディレクトリを作ってもエラーは出ません。
上の実行例のようにcgroup_C
、cgroup_D
以下にファイルは、完全にubuntu
ユーザ所有ですので自由にcgroup操作ができます。
次にプロセスの登録について見てみましょう。cgroup_B
のtasks
、cgroup.procs
ファイルの所有権をubuntu
に変更し、プロセスを登録してみましょう[1]。
cgroup_B
にプロセスが登録できました。cgroup_C
やcgroup_D
以下のファイルの所有者はubuntu
ですから、プロセスの移動は自由です。
cgroup v1でプロセスをcgroup間で移動させる場合は、上の例のようにユーザが管理するcgroupへ自身が所有するプロセスを移動できます。厳密には以下の条件を満たす必要があるようです。
- 移動先の
tasks
、cgroup.proc
ファイルへの書き込み権がある
- 移動(書き込み)しようとするプロセスの実効UIDが、ターゲットとなるプロセスの実UIDもしくは保存set-user-idと一致する
上のような条件ですので、たとえば図3のように独立したcgroupツリーの間でも、権限さえあればプロセスの移動は自由です。cgroup A配下とcgroup B配下のそれぞれはコンテナとみなせますので、所有者が同じであればコンテナ間のプロセスの移動も自由であるということです(実際のコンテナでは名前空間による隔離があるので、通常はコンテナ内のユーザが他のコンテナにプロセスの移動はできないのが普通でしょう)。
試してみましょう。
図3の通りのcgroupを作成して、cgroup_C
に登録したプロセスをcgroup_D
に移動できました。
cgroup v2の場合
cgroup v2の場合、プロセスを移動するための条件が少し厳しくなっています。cgroup Bへプロセスを登録するには次のような権限が必要です。
表2 cgroup v2で委譲するために必要な権限
|
権限 |
実際の書き込み権 |
1 |
cgroup B以下にcgroupを作れる権限 |
ディレクトリBへの書き込み権 |
2 |
cgroup B以下でプロセスを自由に登録できる権限 |
登録先cgroup内のcgroup.procs ファイルへの書き込み権 |
プロセスがもともと所属しているcgroupと登録先cgroupの共通の祖先となるcgroup内のcgroup.procs ファイルへの書き込み権 |
実効UIDの制限がなくなった理由は、表2の2つめの条件が加えられたためです。完全に自身がコントロールしているツリーでなければプロセスを移動できませんので、任意のプロセスは移動できません[2]。
文字で書くと条件が少しわかりづらいですね。実際に、cgroup v2で非特権の場合にプロセスを移動させてみましょう。
図4のような構成のツリーがあるとします。まずはcgroup_B
を作成し、ディレクトリとcgroup.procs
ファイルの所有権を変更します。
root cgroupからcgroup Bへのプロセスの移動
図4の(1)の操作を試してみましょう。ubuntu
ユーザでcgroup_B
のcgroup.procs
に登録を試みます。cgroup v1の場合はcgroup B内のcgroup.procs
に書き込み権があれば、プロセスが登録できました。
しかしcgroup v2では、cgroup.procs
ファイルはubuntu
ユーザの所有であり、ユーザ権限で実行されているプロセスを登録しようしたにも関わらずエラーになります。
これは表2で示した条件の2番目に引っかかったためです。この場合、「プロセスがもともと所属しているcgroupとcgroup Bの共通の祖先」はroot cgroupになります。ubuntu
ユーザはroot cgroupのcgroup.procs
ファイルに対する権限を持っていませんので、プロセスを移動させようとするとエラーになるわけです。
これは、図2で示した権限委譲のパターンのように、コンテナに所属させるプロセスを決めるのはroot cgroupの管理者であるべきという考え方でしょう。通常、コンテナを起動するのは、コンテナ内に権限を持つユーザではなくホスト上で権限を持つユーザですから、実際のユースケースに合っています。
この考え方に従い、ここはroot権限でプロセスをcgroup_B
に登録しましょう。
この操作はroot権限で行っていますので登録できます。
権限があるcgroup間でのプロセスの移動(共通のcgroup配下のcgroup間)
それではcgroup_B
配下にcgroup_D
とcgroup_E
を作成し、先に登録したプロセスを移動させてみましょう。
cgroup_B
はubuntu
ユーザ所有ですので、cgroup_D
とcgroup_E
は問題なく作成できました。
それでは、さきほどroot権限でroot cgroupからcgroup_B
へ移動させたプロセスをcgroup_D
に移動させましょう。図4の(2)のパターンです。
今度はエラーなく作成できました。これはcgroup_B
が「cgroup_B
とcgroup_D
の共通の祖先」という扱いになるためです。つまり、
cgroup_D
のcgroup.procs
はubuntu
ユーザが所有
cgroup_D
とcgroup_B
の共通の祖先であるcgroup_B
のcgroup.procs
ファイルはubuntu
ユーザが所有
となり、2つの条件を満たします。
次に、cgroup_D
からcgroup_E
へのプロセスの移動を見てみましょう。図4の(3)のパターンです。
cgroup_E
のcgroup.procs
はubuntu
ユーザが所有
cgroup_D
とcgroup_E
の共通の祖先であるcgroup_B
のcgroup.procs
ファイルはubuntu
ユーザが所有
ですので、両方の条件を満たします。試してみましょう。
移動できました。
権限があるcgroup間でのプロセスの移動(共通のcgroup配下でないcgroup間)
別の例を紹介しましょう。
図5のようにroot cgroup直下にcgroup_A
とcgroup_B
を作成し、それぞれに子cgroup cgroup_C
、cgroup_D
を作成します。
以上のようにcgroup_A〜Dまですべてubuntu
ユーザ所有のグループで、cgroup内のcgroup.procs
ファイルの所有権もubuntu
ユーザになっており、図5のような状態のcgroupツリーを準備しました。
ここでcgroup_C
からcgroup_D
へプロセスが移動できるでしょうか? 試してみましょう。
まずはcgroup_C
にプロセスを登録します。root cgroupからの移動になりますのでroot権限で移動させます。
これでcgroup_C
にプロセスが登録された状態になりました。つぎに、このプロセスをcgroup_C
からcgroup_D
に移動させてみましょう。
cgroup_D
のcgroup.procs
はubuntu
ユーザ所有であり、1番目の条件を満たしています。しかし、cgroup_C
とcgroup_D
の共通の祖先をたどるとroot cgroupになります。root cgroupのcgroup.procs
ファイルに対してubuntu
ユーザは書き込み権限を持っていませんので、2番目の条件を満たしませんので、プロセスは移動できません。
このような移動ができない理由を考えてみましょう。
図5の構造を見ると、cgroup_A
以下とcgroup_B
以下はそれぞれ独立したコンテナとみなせます。いくら同じユーザ権限で動いているコンテナとはいえ、通常はコンテナをまたいでプロセスを移動することは考えにくいので、実際のユースケースに合った条件になったと言えるでしょう。
このようにcgroup v2では、条件を複雑にすることなく、プロセスの移動をコンテナ内に制限できるようになりました。
まとめ
今回を含めて4回でcgroup v2コアの機能を紹介してきました。
今回は特権を持たないユーザに対して、cgroupツリーの一部を権限委譲する際の機能について紹介しました。cgroup v1に比べてcgroup v2は、よりコンテナで使う場合の実際的な条件や制限が設定されていることが、お分かりいただけたのではないでしょうか。
cgroup v2には、これまで紹介したプロセスをグループ分けする以外にも、v1と同様にコントローラによってリソース制限をする機能があります。また、コアの機能としても新たに追加された機能がありますので、この連載では引き続きcgroup v2の機能を紹介していきたいと思いますが、今回でいったん説明の区切りが付きましたので、次回以降は少し違う話題を扱う予定です。
次回以降はしばらく私ではなく、ご自身でmrubyによるコンテナ実装Haconiwaを開発していらっしゃるudzuraさんにお書きいただく予定です。テーマは、この連載の32回でも軽く扱ったCRIU(チェックポイント・リストア)の話題の予定です。
udzura さんは、Haconiwaの開発を通じてCRIUのいろいろな機能を調査されているようですので、私もCRIUの機能を詳細に説明していただけるのではないかと、今からワクワクしています。
お楽しみに。