前回は、名前空間ごとに仮想化したcgroupツリーが見える、Linuxカーネルのcgroup名前空間という機能を紹介しました。
今回はそのcgroup名前空間の応用例を紹介することを兼ねて、コンテナ内でコンテナを動作させてみます。つまりコンテナをネストさせて遊んでみようという企画です。
今回はroot権限で動作するコンテナを使ってネストを試してみます。試している環境はUbuntu 16.04です。コマンドの実行例は、実行している環境が、ホストなのかどのコンテナなのかがわかりやすいようにプロンプトまで引用しています。
親コンテナの作成と起動
まずはホスト上にコンテナを作成します。
ここではネストさせるコンテナの「親」という意味でparent
という名前のコンテナを作成しました。ここまでは通常のコンテナを作成する場合と同じです。
コンテナのネストを行うためには、ネスト用の設定を行う必要があります。LXC では標準でそのために設定ファイルnesting.conf
が付属していますので、lxc-create
で作成したコンテナの設定ファイルにincludeの設定を行うだけです。
これでコンテナの中でコンテナを起動する準備ができました。このコンテナを起動し、コンテナ内にlxcパッケージをインストールしましょう。
子コンテナの作成と起動
ここまででparent
コンテナ内でコンテナを作成する準備が整いました。「親」の中に「子」コンテナを作成して、先の例と同様にネスト用の設定ファイルをincludeする設定を足しておきましょう。
コンテナ名はchild
としました。
特に問題なく起動しています。
ネストしたコンテナの確認
ところで、lxc-ls
コマンドには--nesting
というオプションがあり、ネストしたコンテナの状態が表示できます。
一旦、ホスト上のシェルに戻ってlxc-ls
コマンドを実行してみましょう。
以上のように、ホスト上で直接parent
が実行されており、その中でchild
が実行されている様子が確認できます。また、parent
内でLXCをインストールしたため、LXCがブリッジを作成しています。コンテナ起動時に作成されるvethインターフェースに割りあたっているアドレスだけでなく、そのブリッジに割りあたっているIPアドレス(10.0.4.1
)も表示されています。
孫コンテナの作成と起動
以上で、コンテナ内でコンテナを起動できることが確認できました。LXCでは、ネストしたコンテナ内でさらにコンテナを起動する機能をサポートしていますので試してみましょう。
さきほど作成した「子」コンテナchild
内でさらにコンテナを作成し、起動してみます。「親」コンテナから見ると孫ですね。
最初の親コンテナから見ると孫になりますのでgrandchild
と名づけました。
先の実行例と同様に、ホストからlxc-ls
でネストされている状態を確認してみましょう。
cgroupの確認
孫コンテナまで3段のネストをさせたところで、これまでに作成したコンテナそれぞれでcgroupfsツリーがどのようになっているかを確認してみましょう。
孫コンテナのcgroup
まずは、最後に作成したネストの一番の末端である孫コンテナgrandchild
で、cgroupfsがどのようになっているかを確認してみましょう。
/sys/fs/cgroup
以下は、通常のホストOS上と同じように、サブシステムごとのディレクトリが準備されています。このうちfreezer
ディレクトリを見てみましょう。
以上のようにcgroupとfreezerサブシステム用のファイルが存在します。freezerサブシステムをオプションに指定して、cgroupfsをマウントした直後と同じ状態です。
その他のサブシステムやsystemd用のcgroupについても、ホストOS上で直接cgroupfsをマウントしたときと同じように見えます。
子コンテナのcgroup
孫コンテナのcgroupを確認したところで、ネストのひとつ上のコンテナである子コンテナchild
で、cgroupがどうなっているかを確認してみましょう。先の例と同じようにfreezerディレクトリ以下を確認します。
今度はサブディレクトリが存在します。LXCコンテナは通常、cgroupのルートにlxc
というcgroupを作成し、その下にコンテナ名のcgroupを作成します。child
内でgrandchild
という名前のコンテナを起動しましたので、そのコンテナ用のディレクトリlxc/grandchild
が見えています。
つまり、孫コンテナ内で/sys/fs/cgroup/freezer
として見えていたcgroupは、ひとつ上の子コンテナから見た/sys/fs/cgroup/freezer/lxc/grandchild
に相当します。
cgroup名前空間の機能で、「孫」コンテナ(grandchild
)用に作られたcgroupが、コンテナ内ではルートとして見えていることが確認できました。
親コンテナのcgroup
親コンテナであるparent
内でも同様に確認してみましょう。
コンテナごとにlxc/(コンテナ名)
というcgroupが作成されており、ネストしている様子がわかります。
ホストのcgroup
同様にホストから確認しましょう。
先の例と同様にcgroupがネストしていますね。
LXCコンテナ内でのDocker
ここまでで、多段にネストしたコンテナを作成し、その場合にcgroupがどのように作られ、コンテナ内ではcgroup namespaceによって自分用のcgroupツリーのみが見えることを確認しました。
ここまでで作成したコンテナはすべてLXCコンテナでしたが、最後にLXCコンテナの中でDockerコンテナを動かしてみましょう。
まずはDockerデーモンを動かすためのLXCコンテナを作成します。LXCコンテナを表すlxcct
という名前にしてみました。
Ubuntu 16.04のコンテナを作成しています。このコンテナ内でDockerを動かすための設定を追加します。追加した行は次の2行です。
AppArmorを無効にする設定と、無効にするケーパビリティがない状態にしています。そして、LXCコンテナ内でDockerを利用する際に必要な、overlayfs用のモジュールをホスト上でロードしておきます。
これで準備は完了です。コンテナを起動し、コンテナ内でDockerパッケージをインストールします。
インストールはUbuntuのパッケージを使ってインストールします。動作を確認してみましょう。
動作していることが確認できました。ストレージとしてoverlayfsが使われていることがわかります。
それではDockerコンテナを起動してみましょう。
Ubuntuイメージを使ってDockerコンテナが起動しました。/.dockerenv
ファイルがあるので確かにDockerコンテナ内にいるようですね。
Dockerコンテナ内からcgroupを確認してみます。
freezerサブシステムがマウントされており、特に子cgroupは存在しない状態です。このままDockerコンテナが起動している状態で、ホスト上のシェルからfreezerサブシステムのツリーの様子を確認してみます。
lxc/lxcct
がLXCが作成したcgroup、docker/(コンテナ名)
がDockerが作成したcgroupです。
LXCコンテナ上からみると、次のようになります。
Dockerコンテナを起動した場合も、LXCコンテナと同様にcgroup namespaceの機能により、コンテナ自身が所属するcgroup以下のツリーのみが見えていることがわかります。
まとめ
今回は、cgroup namespaceの実際の動きを確認することを兼ねて、コンテナをネストさせて遊んでみました。LXCでは多段のネストもサポートしています。そこで、実際に何段もコンテナをネストさせることはあまりないかもしれませんが、多段のネストも試してみました。
Linuxカーネルの機能的に見ると、cgroupは階層構造が作れますし、名前空間内に名前空間を作れますので、コンテナをネストさせることで問題になることはありません。
ただ、今回紹介したようにコンテナをネストさせることができるかどうかは、コンテナランタイムの実装次第でしょう。
LXCは、カーネルで実装されているコンテナ関連の機能をフルに活かすように実装されていますので、今回紹介したようなネストができるというわけです。
LXCと同じく、linuxcontainers.orgで開発されているLXDでも、ライブラリとしてLXCを使っていますので、コンテナをネストできます。LXDコンテナ上でDockerコンテナを起動させる記事がありますので、興味がある方は是非そちらもご覧になってみてください。
さて、次回も今回に続いてコンテナのネストの話題です。一般ユーザ権限で起動する非特権コンテナでコンテナをネストさせてみる予定です。お楽しみに。
第11回 コンテナ型仮想化の情報交換会@大阪
6/17(土)に大阪で、この連載でも何度か紹介しているコンテナの勉強会を開催しました。
当日は、自作コンテナ実装のお話から、LXCやLXDの活用事例、Dockerの監視の話まで色々なコンテナに関わるお話を聴くことができ、大変勉強になりました。
当日のセッションそれぞれの動画の公開や発表資料へのリンクがありますので、勉強会ページをぜひご覧ください。