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

第34回 Linuxカーネルに実装されたcgroup名前空間 ─ コンテナ内のcgroup管理[2]

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

cgroup名前空間と/proc/[PID]/cgroupファイル

cgroupファイルの内容が理解できたところで,unshareコマンドを使って新たな名前空間を作成してみましょう。

なぜかUbuntu 16.04にインストールされるunshareコマンドはcgroup名前空間を扱えません。そこで別途util-linux 2.29をソースからコンパイルし,対応するunshareコマンドを作成しました。あとで使うために同時にマウント名前空間を作成しています。

# ./unshare --cgroup --mount bash (cgroupとマウント名前空間を作成)
# echo $$
1237
# cat /sys/fs/cgroup/memory/test01/tasks | egrep "(1237|1220)"
("test01"グループのtasksファイルにPIDが存在することを確認)
1220
1237

名前空間を作成してbashを実行すると,親プロセス(PID:1220)/test01グループに所属していたので,その子プロセス(PID:1237)/test01所属となりました。

ここで/proc/[PID]/cgroupファイルを確認してみましょう。

(作成した名前空間内で確認)
# cat /proc/1220/cgroup | grep memory
11:memory:/
# cat /proc/1237/cgroup | grep memory
11:memory:/

先ほどは/test01に所属していたように表示されていた親プロセスも,子プロセスも,名前空間内で見るとルートに属していると表示されます。

このようにcgroupファイルの中身が,名前空間が作成された時点に所属していたcgroupをルートとしたツリーで見えるようになるのが,cgroup名前空間の機能です。

cgroup名前空間とマウント名前空間の連携

cgroupファイルはcgroup名前空間の機能によって,名前空間内での見え方に変わっていることが確認できました。

しかし,新たなcgroup名前空間を作成しても,マウントは特に変更していませんのでそのままの状態です。cgroupfsを確認すると,

# ls -d /sys/fs/cgroup/memory/test01
/sys/fs/cgroup/memory/test01
# cat /proc/1220/mountinfo | grep memory (親プロセスのマウント情報の確認)
39 27 0:33 /.. /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:22 - cgroup cgroup rw,memory
# cat /proc/1237/mountinfo | grep memory (自身のマウント情報の確認)
181 160 0:33 /.. /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,memory

依然として/sys/fs/cgroup/memory/test01が存在しており,親環境と同じツリーが見えています。プロセスのマウント情報を格納しているmountinfoファイルでは,4つ目のマウントのルートを表すエントリが/..(ルートディレクトリの親ディレクトリ)とちょっと変な表示になっていますね。

それではcgroupfsをマウントしなおしてみましょう。

まずは,systemdが起動時にすべてのマウント操作を共有する設定を行っているので,マウント操作を行う前にそれを無効にします。これで,マウント名前空間ごとに独立したマウントが見えるようになりますinitがsystemd以外の場合は不要です⁠⁠。

# mount --make-rslave / (名前空間ごとに独立したマウントとする)

そして,アンマウントした後に,再度cgroupをマウントします。

# umount /sys/fs/cgroup/memory (cgroupfsをアンマウント)
# cat /proc/self/mountinfo | grep '/sys/fs/cgroup/memory'
(アンマウントされたことを確認)
# mount -t cgroup -o memory memory /sys/fs/cgroup/memory
(再度/sys/fs/cgroup/memoryにmemoryサブシステムをマウント)

これでマウント情報がどうなったか確認してみましょう。

# cat /proc/self/mountinfo | grep '/sys/fs/cgroup/memory'
115 143 0:33 / /sys/fs/cgroup/memory rw,relatime - cgroup memory rw,memory
(4つ目のエントリも"/"になった)

/proc/self/mountinfoの内容が変わっており,ルートは"/"と表示されていることがわかります。

# find /sys/fs/cgroup/memory/ -type d
/sys/fs/cgroup/memory/ (ルート以下にディレクトリは存在しない)

実際のcgroupfsを見ても,/sys/fs/cgroup/memory以下にはcgroupは存在せず,ルートグループのみが存在する状態になりました。

このようにマウント名前空間と連携して,実際のcgroupfsツリーも/proc/[PID]/cgroupファイルの記載と一致するツリーとなります。

cgroup名前空間内でグループを作成

このcgroup名前空間内でルート直下に新たにグループを作ってみましょう。

# mkdir /sys/fs/cgroup/memory/test02 (名前空間内でtest02グループを作成)

親環境からtest01グループ内を見てみると,

$ ls -F /sys/fs/cgroup/memory/test01 (親の名前空間からtest01内を見てみる)
  :(略)
memory.kmem.tcp.usage_in_bytes      tasks
memory.kmem.usage_in_bytes          test02/

test01グループ直下にtest02グループがありますね。つまり名前空間内では,親と同じcgroupfsツリーを,自身のcgroupをルートとしてそれ以下だけを見せていることがわかります。

cgroup間のプロセスの移動

今度は同じ階層にグループをふたつ作成し,その間を移動して動きを見てみましょう。

# mkdir /sys/fs/cgroup/memory/test0{1,2} (test01,test02グループを作成)
# echo $$
1227
# echo 1227 > /sys/fs/cgroup/memory/test01/tasks (自身をtest01に登録)
# ./unshare --cgroup --mount bash (名前空間を作成)
# echo $$
1254
# cat /proc/1254/cgroup | grep memory (名前空間内ではルートに所属することを確認)
5:memory:/

test01test02というグループを同じ深さの階層(ルート直下)作成し,先ほどと同様にtest01グループにプロセスを登録し,名前空間を作成しました。すると名前空間内ではルートに所属しています。ここまでは先ほどと同じです。

それでは,自身(PID:1254)test02に移動させてみましょう。自身はtest01にいますが,cgroupfsを再マウントしていませんので,まだtest02も見えているはずです。移動はPIDを新たに所属させたいグループのtasksファイルに登録しなおすだけでしたね。

# ls -dF /sys/fs/cgroup/memory/test*
/sys/fs/cgroup/memory/test01/  /sys/fs/cgroup/memory/test02/
# echo 1254 > /sys/fs/cgroup/memory/test02/tasks (test02グループへ移動)
# cat /proc/1254/cgroup  | grep memory (所属するグループを確認)
5:memory:/../test02

test02へ移動後に,cgroupの所属を確認してみると,/../test02となっています。つまりルートはtest01のままで,test01からの相対パスがcgroupファイルに現れます。一度ルートが定まると,名前空間内のツリーから外に移動してもルートは変わりません。

実際にはこのような操作をする意味はないでしょうし,カーネル文書にも推奨されないと書かれていますが,面白い動きですね。

まとめ

今回は新しい名前空間としてcgroup名前空間を紹介しました。

コンテナ内にコンテナ向けのcgroupfsツリーを見せるという少し地味な機能でした。

コンテナ内でも,普通にOSを起動したホスト上でマウントしたcgroupfsと同じような構造に見えるため,特にシステムコンテナ内で自然にcgroupfsを扱えます。cgroupfsを使うsystemdなどのソフトウェアがコンテナ内で実行されても問題ありませんし,コンテナ内でさらにコンテナを起動してもcgroupfsが自然な形で存在していますので,問題なく起動します。

cgroup名前空間がカーネルに実装されため,LXCFSは今後/proc関連の機能のみが使われるようになっていきます。

著者プロフィール

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

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

Plamo Linuxメンテナ

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