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

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

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

前回までに引き続き,今回もcgroup v2が持つ基本機能を紹介していきます。

Linuxでは,特権を持たないユーザでもユーザ名前空間を使ってコンテナを起動できます。その際,特権を持たないユーザがcgroupを操作できるように権限が委譲できるようになっています。今回は,権限を委譲する際の操作や動きについて,cgroup v1とv2それぞれの場合どのようになるのかを紹介していきましょう。

非特権ユーザに対する権限委譲

図1 非特権ユーザに対する権限委譲

図1 非特権ユーザに対する権限委譲

デフォルトではcgroupを操作する権限は特権ユーザにしかありません。一般ユーザ権限で起動する非特権コンテナを起動する場合などでは,一般ユーザにcgroupを操作できる権限を与える必要があります。

たとえば,図1のように一般ユーザ権限で"B"というコンテナを起動し,そのコンテナに対してリソース管理を行うためにcgroup Bを作成したとします。この場合,cgroup B以下に存在するツリーはコンテナ"B"を起動したユーザに管理させることになります。cgroup B以下のツリーを一般ユーザに管理させたい場合,権限を与えたいユーザに対して

  1. cgroup B以下にcgroupを作れる権限
  2. cgroup B以下でプロセスを自由に登録できる権限

を与えれば良いでしょう図2)⁠

図2 cgroupに対する権限委譲の一般的なパターン

図2 cgroupに対する権限委譲の一般的なパターン

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ユーザにします。

$ whoami
ubuntu
$ sudo mkdir /sys/fs/cgroup/cpu/cgroup_B (cgroup_B を作成)
$ sudo chown ubuntu /sys/fs/cgroup/cpu/cgroup_B/ (cgroup_Bの所有権をubuntuに)
$ ls -ld /sys/fs/cgroup/cpu/cgroup_B/ (ubuntuユーザ所有になっている)
drwxr-xr-x 2 ubuntu root 0 Feb 27 20:04 /sys/fs/cgroup/cpu/cgroup_B/

これで子cgroupを作る準備はできましたので,実際にcgroup_Ccgroup_Dを作ってみましょう。

$ mkdir /sys/fs/cgroup/cpu/cgroup_B/cgroup_C
$ mkdir /sys/fs/cgroup/cpu/cgroup_B/cgroup_D
$ $ ls -ld /sys/fs/cgroup/cpu/cgroup_B/cgroup_*
drwxrwxr-x 2 ubuntu ubuntu 0 Feb 27 20:07 /sys/fs/cgroup/cpu/cgroup_B/cgroup_C
drwxrwxr-x 2 ubuntu ubuntu 0 Feb 27 20:07 /sys/fs/cgroup/cpu/cgroup_B/cgroup_D

通常のディレクトリを作成する際と同じで,cgroup_Bディレクトリはubuntuユーザ所有ですので,その配下にディレクトリを作ってもエラーは出ません。

$ ls -l /sys/fs/cgroup/cpu/cgroup_B/cgroup_C
total 0
-rw-r--r-- 1 ubuntu ubuntu 0 Feb 27 20:07 cgroup.clone_children
-rw-r--r-- 1 ubuntu ubuntu 0 Feb 27 20:07 cgroup.procs
-r--r--r-- 1 ubuntu ubuntu 0 Feb 27 20:07 cpuacct.stat
-rw-r--r-- 1 ubuntu ubuntu 0 Feb 27 20:07 cpuacct.usage
-r--r--r-- 1 ubuntu ubuntu 0 Feb 27 20:07 cpuacct.usage_percpu
-rw-r--r-- 1 ubuntu ubuntu 0 Feb 27 20:07 cpu.cfs_period_us
-rw-r--r-- 1 ubuntu ubuntu 0 Feb 27 20:07 cpu.cfs_quota_us
-rw-r--r-- 1 ubuntu ubuntu 0 Feb 27 20:07 cpu.shares
-r--r--r-- 1 ubuntu ubuntu 0 Feb 27 20:07 cpu.stat
-rw-r--r-- 1 ubuntu ubuntu 0 Feb 27 20:07 notify_on_release
-rw-r--r-- 1 ubuntu ubuntu 0 Feb 27 20:07 tasks

上の実行例のようにcgroup_Ccgroup_D以下にファイルは,完全にubuntuユーザ所有ですので自由にcgroup操作ができます。

次にプロセスの登録について見てみましょう。cgroup_Btaskscgroup.procsファイルの所有権をubuntuに変更し,プロセスを登録してみましょう※1)⁠

※1)
実際は`tasks`か`cgroup.procs`のどちらかに権限を与え,与えたファイルに書き込めば,他方の所有権が`root`のままでもプロセスは登録できるようです。
$ sudo chown ubuntu /sys/fs/cgroup/cpu/cgroup_B/tasks
(cgroup_Bのtasksファイルの所有権をubuntuに)
$ sudo chown ubuntu /sys/fs/cgroup/cpu/cgroup_B/cgroup.procs
(cgroup_Bのcgroup.procsファイルの所有権をubuntuに)
$ echo $$
1690
$ echo $$ > /sys/fs/cgroup/cpu/cgroup_B/tasks (プロセスをcgroup_Bに登録)
$ cat /sys/fs/cgroup/cpu/cgroup_B/tasks (登録できている)
1690
2285

cgroup_Bにプロセスが登録できました。cgroup_Ccgroup_D以下のファイルの所有者はubuntuですから,プロセスの移動は自由です。

$ echo $$ > /sys/fs/cgroup/cpu/cgroup_B/cgroup_C/tasks
(上でcgroup_Bに登録したプロセスをcgroup_Cに移動)
$ cat /sys/fs/cgroup/cpu/cgroup_B/tasks (cgroup_Bにはプロセスはない)
$ cat /sys/fs/cgroup/cpu/cgroup_B/cgroup_C/tasks (cgroup_Cに移動した)
1690
2316
$ echo $$ > /sys/fs/cgroup/cpu/cgroup_B/cgroup_D/tasks 
$ cat /sys/fs/cgroup/cpu/cgroup_B/cgroup_D/tasks (cgroup_Dに移動した)
1690
2328
$ cat /sys/fs/cgroup/cpu/cgroup_B/tasks (再度cgroup_Bに移動した)
1690
2343

cgroup v1でプロセスをcgroup間で移動させる場合は,上の例のようにユーザが管理するcgroupへ自身が所有するプロセスを移動できます。厳密には以下の条件を満たす必要があるようです。

  • 移動先のtaskscgroup.procファイルへの書き込み権がある
  • 移動(書き込み)しようとするプロセスの実効UIDが,ターゲットとなるプロセスの実UIDもしくは保存set-user-idと一致する

上のような条件ですので,たとえば図3のように独立したcgroupツリーの間でも,権限さえあればプロセスの移動は自由です。cgroup A配下とcgroup B配下のそれぞれはコンテナとみなせますので,所有者が同じであればコンテナ間のプロセスの移動も自由であるということです(実際のコンテナでは名前空間による隔離があるので,通常はコンテナ内のユーザが他のコンテナにプロセスの移動はできないのが普通でしょう)⁠

図3 独立したコンテナ間のプロセスの移動(cgroup v1)

図3 独立したコンテナ間のプロセスの移動(cgroup v1)

試してみましょう。

$ sudo mkdir /sys/fs/cgroup/cpu/cgroup_{A,B} (cgroup_A, B作成)
$ sudo chown ubuntu /sys/fs/cgroup/cpu/cgroup_{A,B} (A, Bの所有権の変更)
$ mkdir /sys/fs/cgroup/cpu/cgroup_A/cgroup_C (cgroup_C作成)
$ mkdir /sys/fs/cgroup/cpu/cgroup_B/cgroup_D (cgroup_D作成)
$ echo $$ > /sys/fs/cgroup/cpu/cgroup_A/cgroup_C/tasks (Cにプロセスを登録)
$ cat /sys/fs/cgroup/cpu/cgroup_A/cgroup_C/tasks | grep $$
1622 (登録されている)
$ echo $$ > /sys/fs/cgroup/cpu/cgroup_B/cgroup_D/tasks (Dにプロセスを移動)
$ cat /sys/fs/cgroup/cpu/cgroup_B/cgroup_D/tasks | grep $$
1622 (移動している)

図3の通りのcgroupを作成して,cgroup_Cに登録したプロセスをcgroup_Dに移動できました。

著者プロフィール

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

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

Plamo Linuxメンテナ

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