さて、
そのエントリしたカレンダーのひとつがLinux Advent Calendar 2016です。一昨年から、
今年も同様に、
昨年のエントリ
今回の実行例はUbuntu 16.
前回は、
Ubuntu 14.
cgmanagerから置き換わったLXCFSがUbuntu 15./proc
以下をコンテナ向けに仮想化する機能がありますのでなくなるわけではありませんが、
LXCFSのcgroup関連機能が不要になった理由は、
/proc/[PID]/nsディレクトリ
cgroup名前空間を紹介する前に、/proc/[PID]/ns
ディレクトリについて紹介しておきましょう
各プロセスに関連する情報を格納したファイルが存在する/proc/[PID]
ディレクトリ以下にはns
というディレクトリが存在します。
このディレクトリには、
ls -l
を実行すると、
$ ls -l /proc/self/ns total 0 lrwxrwxrwx 1 ubuntu ubuntu 0 Dec 6 19:41 cgroup -> cgroup:[4026531835] lrwxrwxrwx 1 ubuntu ubuntu 0 Dec 6 19:41 ipc -> ipc:[4026531839] lrwxrwxrwx 1 ubuntu ubuntu 0 Dec 6 19:41 mnt -> mnt:[4026531840] lrwxrwxrwx 1 ubuntu ubuntu 0 Dec 6 19:41 net -> net:[4026531957] lrwxrwxrwx 1 ubuntu ubuntu 0 Dec 6 19:41 pid -> pid:[4026531836] lrwxrwxrwx 1 ubuntu ubuntu 0 Dec 6 19:41 user -> user:[4026531837] lrwxrwxrwx 1 ubuntu ubuntu 0 Dec 6 19:41 uts -> uts:[4026531838]
リンク先に数字が表示されていますが、
この/proc/[PID]/ns
以下のファイルには、
あるプロセスが所属する名前空間に移動するためのシステムコールとしてsetns(2)というシステムコールがあります。このsetns(2)
はファイルディスクリプタと名前空間の種類を引数に取ります。ここで与えるファイルディスクリプタが、/proc/[PID]/ns
ディレクトリ以下に存在するファイルのファイルディスクリプタです。
LXCでsetns(2)
を使うコマンドは、lxc-attach
です。この/proc/[PID]/ns
以下の実装が完成したのは3.setns(2)
がすべての名前空間に対してきちんと動作し、lxc-attach
コマンドが動作するようになったのがこの時でした。
cgroup名前空間
それではいよいよcgroup名前空間について見ていきましょう。
cgroup名前空間は4.
cgroup名前空間は名前空間ごとにcgroupを仮想化します。具体的には、
cgroup名前空間が使えるかどうかは、/proc/[PID]/ns
以下にcgroup
というファイルが存在するかを見ればわかります。先の実行例で示したように、ns
ディレクトリ以下を見ると、
$ ls /proc/self/ns cgroup ipc mnt net pid user uts
以上のようにcgroup
というファイルが存在しており、/proc/[PID]/ns
を見ても、cgroup
というファイルは存在しません。
cgroup名前空間が提供する機能
LXCFSが提供するコンテナ内のcgroupfs
まずは比較のために前回紹介したLXCFSを使うとコンテナ内でcgroupfsがどのように見えたかをおさらいしておきましょう。
$ lxc-start -n xenial01
以上のように起動した非特権コンテナ"xenial01"用のcgroupは、
- 親環境上で見た場合
/sys/
に存在fs/ cgroup/ cpu/ user/ 1000. user/ 1. session/ lxc/ xenial01/ - コンテナ内で見た場合
/sys/
に存在fs/ cgroup/ cpu/ user/ 1000. user/ 1. session/ lxc/ xenial01/
以上のように親であるホスト上の環境でもコンテナ内でも同じパスに存在しました
ただし、
つまりLXCFSは、
cgroup名前空間が提供するコンテナ内のcgroupfs
それではUbuntu 16.
$ lxc-start -n xenial01 $ lxc-attach -n xenial01 -- ls -F /sys/fs/cgroup/cpu,cpuacct/ cgroup.clone_children cpuacct.usage_percpu cpu.stat cgroup.procs cpu.cfs_period_us notify_on_release cpuacct.stat cpu.cfs_quota_us tasks cpuacct.usage cpu.shares
cpuサブシステムがマウントされた直下のルートグループを見ると、
そして、
つまり非特権コンテナ"xenial01"用のcgroupは、
- 親環境上で見た場合
/sys/
に存在fs/ cgroup/ cpu,cpuacct/ user/ ubuntu/ 1/lxc/ xenial01 (ログインユーザの環境により異なります) - コンテナ内で見た場合
/
(ルート) に存在
実際にはコンテナ用のcgroupがコンテナ内でのルートになるのは、
cgroupに属するPIDが書かれたtasks
ファイルcgroup.
ファイル)
$ head -n1 /sys/fs/cgroup/cpu,cpuacct/user/ubuntu/1/lxc/xenial01/tasks 1374 (コンテナ内systemdの親の名前空間でのPID) $ lxc-attach -n xenial01 -- sudo head -n1 /sys/fs/cgroup/cpu,cpuacct/tasks 1 (コンテナ内systemdのコンテナ内でのPID)
このように、
cgroup名前空間の機能を簡単に紹介したところで、
/proc/[PID]/cgroupファイル
プロセスがどのcgroupに属しているかは、/proc/[PID]/cgroup
というファイルを見ればわかります。
まずはテスト用にcgroupを作成します。
# mkdir /sys/fs/cgroup/memory/test01 (test01グループを作成) # echo $$ 1220 # echo 1220 > /sys/fs/cgroup/memory/test01/tasks (プロセスをtest01に登録)
以上のようにmemoryサブシステムのルート直下に"test01"グループを作成し、
# cat /proc/1220/cgroup | grep memory
4:memory:/test01 (所属するcgroupは"/test01")
cgroupファイルのmemory
行を見ると、/test01
と書かれています。つまり/test01
グループに属しているということですね。
cgroup名前空間と/proc/[PID]/cgroupファイル
cgroupファイルの内容が理解できたところで、unshare
コマンドを使って新たな名前空間を作成してみましょう。
なぜかUbuntu 16.unshare
コマンドはcgroup名前空間を扱えません。そこで別途util-linux 2.unshare
コマンドを作成しました。あとで使うために同時にマウント名前空間を作成しています。
# ./unshare --cgroup --mount bash (cgroupとマウント名前空間を作成) # echo $$ 1237 # cat /sys/fs/cgroup/memory/test01/tasks | egrep "(1237|1220)" ("test01"グループのtasksファイルにPIDが存在することを確認) 1220 1237
名前空間を作成してbash
を実行すると、/test01
グループに所属していたので、/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名前空間の機能によって、
しかし、
# 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/
が存在しており、mountinfo
ファイルでは、/..
それではcgroupfsをマウントしなおしてみましょう。
まずは、init
がsystemd以外の場合は不要です)。
# mount --make-rslave / (名前空間ごとに独立したマウントとする)
そして、
# 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/
の内容が変わっており、
# find /sys/fs/cgroup/memory/ -type d
/sys/fs/cgroup/memory/ (ルート以下にディレクトリは存在しない)
実際のcgroupfsを見ても、/sys/
以下にはcgroupは存在せず、
このようにマウント名前空間と連携して、/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
グループがありますね。つまり名前空間内では、
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:/
test01
とtest02
というグループを同じ深さの階層test01
グループにプロセスを登録し、
それでは、test02
に移動させてみましょう。自身はtest01
にいますが、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
へ移動後に、/../
となっています。つまりルートはtest01
のままで、test01
からの相対パスがcgroup
ファイルに現れます。一度ルートが定まると、
実際にはこのような操作をする意味はないでしょうし、
まとめ
今回は新しい名前空間としてcgroup名前空間を紹介しました。
コンテナ内にコンテナ向けのcgroupfsツリーを見せるという少し地味な機能でした。
コンテナ内でも、
cgroup名前空間がカーネルに実装されため、/proc
関連の機能のみが使われるようになっていきます。