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

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

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

さて,今年もAdvent Calendarの季節になりました。例年通り今年もいろいろなカレンダーがあって盛り上がっていますね。筆者もいくつかエントリしたので,そのための記事を書くのが大変です。

そのエントリしたカレンダーのひとつがLinux Advent Calendar 2016です。一昨年から,この連載の記事をLinux Advent Calendar 20152014へのエントリと兼ねて書いてきました。

今年も同様に,この記事をLinux Advent Calendar 2016 13日目の記事として書きました。Linux Advent Calendar 2016には今年もマニアックで面白いエントリが並んでいますね。

昨年のエントリ第30回は,Linuxカーネルに新たに追加されたcgroupのpidsサブシステムを紹介しました。今年は,Linuxカーネルが持つコンテナ関連機能としてはcgroupと双璧をなす,名前空間(Namespace)の新しい機能を紹介しようと思います。

今回の実行例はUbuntu 16.04上で実行しています。


前回は,コンテナ内でcgroupfsを利用するための仕組みとして,cgmanagerLXCFSを紹介しました。

Ubuntu 14.04 LTSでは,cgroupを管理するためのデーモンとしてcgmanagerが導入され,LXC 1.0と共に動作するように設定されました。しかしその後,Ubuntu 15.04ではLXCFSに置き換わりました。LXC 1.0はまだサポート期間ですので,cgmanagerはまだしばらくは使われ続けサポートが続きますが,短命なソフトウェアでしたね。

cgmanagerから置き換わったLXCFSがUbuntu 15.04で導入されたものの,その後リリースされた長期サポート版であるUbuntu 16.04 LTSでは,コンテナ内にcgroupfsツリーを提供する機能については使われなくなりました。LXCFSには前回紹介した通り,/proc以下をコンテナ向けに仮想化する機能がありますのでなくなるわけではありませんが,cgroup関連機能については今後不要になっていきます。

LXCFSのcgroup関連機能が不要になった理由は,新たにLinuxカーネルにcgroupを仮想化する機能が追加されたためです。これがcgroup名前空間という機能です。

/proc/[PID]/nsディレクトリ

cgroup名前空間を紹介する前に,これまでの名前空間の記事では紹介していなかった/proc/[PID]/nsディレクトリについて紹介しておきましょう("[PID]"はプロセスのPIDが入ります)⁠

各プロセスに関連する情報を格納したファイルが存在する/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.8カーネルの時で,setns(2)がすべての名前空間に対してきちんと動作し,lxc-attachコマンドが動作するようになったのがこの時でした。

cgroup名前空間

それではいよいよcgroup名前空間について見ていきましょう。

cgroup名前空間は4.6カーネルで導入された機能です。Ubuntu 16.04 LTSでインストールされるカーネルのバージョンは4.4ですが,このカーネルにもバックポートされており,Ubuntu 16.04でもcgroup名前空間が使用できます。

cgroup名前空間は名前空間ごとにcgroupを仮想化します。具体的には,新たにcgroup名前空間を作成すると,LXCFSが実現していたように自身に関連するcgroupだけが見えるようになります。見え方はLXCFSとは少し違います。

cgroup名前空間が使えるかどうかは,先に紹介した/proc/[PID]/ns以下にcgroupというファイルが存在するかを見ればわかります。先の実行例で示したように,Ubuntu 16.04上でnsディレクトリ以下を見ると,

$ ls /proc/self/ns
cgroup  ipc  mnt  net  pid  user  uts

以上のようにcgroupというファイルが存在しており,cgroup名前空間が使えることがわかります。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/に存在

以上のように親であるホスト上の環境でもコンテナ内でも同じパスに存在しました(具体的なパスはユーザ環境により異なります)⁠

ただし,コンテナ内では"xenial01"以下のディレクトリ以外にはcgroup関連のファイルが存在せず,他のディレクトリは空でした。

つまりLXCFSは,親と同じツリー構造を見せつつ,自分が権限を持つグループだけcgroupの中身を見せていました。

cgroup名前空間が提供するコンテナ内のcgroupfs

それではUbuntu 16.04上で同様に非特権コンテナを起動してみましょう。

$ 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サブシステムがマウントされた直下のルートグループを見ると,ディレクトリはありません。つまりルートcgroupのみ存在しています。OSを起動してcgroupfsをマウントしたときと同じですね。

そして,このコンテナ内に存在するcgroupfsのルート以下は,親環境ではルートグループより深いパスに存在していたコンテナ用cgroupの中身が見えているのです。このように,cgroup名前空間を使うと,コンテナ内では自身のcgroupがルートになります。とても自然な動きですね。

つまり非特権コンテナ"xenial01"用のcgroupは,

親環境上で見た場合
/sys/fs/cgroup/cpu,cpuacct/user/ubuntu/1/lxc/xenial01に存在(ログインユーザの環境により異なります)
コンテナ内で見た場合
/(ルート)に存在

実際にはコンテナ用のcgroupがコンテナ内でのルートになるのは,マウント名前空間をあわせて使っているからです。この連携はあとで説明します。

cgroupに属するPIDが書かれたtasksファイル(とcgroup.procsファイル)には,PID名前空間内でのPIDが書かれます。

$ 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)

このように,コンテナ内でもホストOS上と同様のcgroupfsの見せ方を提供する機能がcgroup名前空間です。

cgroup名前空間の機能を簡単に紹介したところで,もう少し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"グループを作成し,シェルのPIDを登録しました。

# cat /proc/1220/cgroup | grep memory
4:memory:/test01 (所属するcgroupは"/test01")

cgroupファイルのmemory行を見ると,/test01と書かれています。つまり/test01グループに属しているということですね。

著者プロフィール

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

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

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

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

コメント

コメントの記入