お盆休みはひたすらOSの仕組みとデータベーススペシャリスト試験の勉強をやっていました。特にOSの仕組みを学んでいて、一見魔法に見える動作が中身を知ってしまうと拍子抜けするということがよくありました。逆に言うと低レイヤーの中身を知っていれば簡単なことでも魔法じみた行為が可能になってしまうということです。低レイヤーってすげえなとしみじみ思い、なんか身近な魔法の仕組みを低レイヤーから暴けないかと思ったところちょうど良い題材としてDockerがあったので夏休みの自由研究として調べてみました。Dockerに触れたことがあり、イメージやコンテナの作り方・使い方がわかるという方向けに書いています。
結論
DockerはOS(Linux)の機能を使って、プロセスを隔離し、見せるリソースを限定することであたかもホストマシンとは別マシンにいるように感じさせています。具体的には、あるプロセス(たち)が
- アクセスできるファイル・ディレクトリを限定し、かつファイルシステムの構造について嘘をつく
- 所属するネットワークを偽ってかつ限定
- 見えるユーザーの存在を限定
- 見ることができるプロセスを互いだけに限定
- 感知できるホスト名を偽る
- 通信できるプロセスを限定
- 使える物理リソース(CPU時間、メモリ等)を限定
しています。なお、偽る、限定するなどの言葉はネガティブなニュアンスを持たせる意図はなく、わかりやすく表現するために使っています。
水槽の脳
いきなり例え話をします。普段Windowsを使っている方はタスクマネージャというソフトをご存知かと思います。タスクマネージャの画面ってこんな感じですよね。
このスクショではいっぱいプロセスが見えています。ここで、次の問を考えてみましょう。ここに見えているプロセスは本当にこれで全部でしょうか?何を言っているのか、と。ここに出ているプロセスで全部に決まっているじゃないか、と。しかしながら、もしWindowsが嘘をついていて、ここに表示されないプロセスをいっぱい走らせていても、タスクマネージャを見ることしか許されていない人にとってはそれを知る方法がないので、タスクマネージャに出てきているプロセスが全てと思うしかないんですね。仮にここには表示されないGoogle ChromeやiTunesが裏で立ち上がっていたとしてもWindowsがタスクマネージャに表示しなければわかりません1。
別の例えでいくと、WindowsユーザーはCドライブの直下にいろいろなファイルが有ることをご存知だと思います。
これより更に上のフォルダは本当に無いんでしょうか?もし更に上のフォルダが何個もあって、その中の1つにUsersだとかProgram Filesフォルダが配置されていて、それを見せられているにすぎないとしても、Windowsがエクスプローラー上でこういう風に表示してしまえば、それを見せられている側としては、ああここがCドライブの一番上で、その下にはProgram FilesやUsersがあって普通にWindows10がインストールされているなー、と思うしかありません。
当然上に出した例えはあくまで例えで、タスクマネージャは正直に全部のプロセスを表示しているし、エクスプローラーは正直にCドライブの直下を見せてくれています。しかし、仮にOSが嘘をついてしまえば実態とは異なる景色を見せてあたかも水槽の脳のように虚構の世界を見ることになります。
リソースの限定による別マシンで動いている錯覚
Linuxには先程の例えに出したような、プロセスに見せるものを限定することで、あたかも別マシンで動いているように錯覚させられる仕組みがあります。
まず、pivot_rootについて説明します。pivot_rootは別のファイルシステムを、ルートファイルシステムにしてしまうシステムコールです。例えば、/home/knksm/hogeというディレクトリにルートディレクトリよろしくbinやらusrやらhomeやらを作っておきます。
/home/knksm/hoge
├── bin
├── home
└── usr
このディレクトリに対してmount –bind /home/knksm/hoge /home/knksm/hogeとしてやると、このディレクトリを別のファイルシステムとしてマウントできます。マウントされたディレクトリに対してpivot_rootを使用すると、使用したプロセスに対してはOSがhogeディレクトリをルートディレクトリとして返すようになります。このbinやらhomeやらに何らかのディストリビューションのファイルをいっぱい入れておけば、pivot_rootしたプロセスにとっては、自分があたかもそういうディストリビューションのLinux上で動いているように感じるという寸法です。
次に、namespace・cgroupsに関して説明します。Linuxのnamespaceでは、リソース群をグループ化し、プロセスをそのグループに所属させることができます。グループに所属したプロセスは、同一グループ内のリソースにしかアクセスできなくなります。グループ化できるリソースにはPID、Network、Mountなどがあります。例えば、PID namespaceでプロセスを隔離した場合、同じnamespaceのプロセスしか見えなくなるので、先程のWindowsタスクマネージャの例で出したような、実は他にもいっぱいプロセスが動いているのに、自分からは見えない、という状況を作る事ができます。あたかも本当のマシンとは別のマシンで動いているかのようです。cgroupsではプロセスのグループが使える物理的なリソース、例えばCPU時間やメモリを制限することができる仕組みです。namespaceは論理的なリソースの分離、cgroupsは物理的なリソースの分離に使われています。
Dockerのpivot_rootでルートディレクトリに設定されるディレクトリはdocker imageの中身
といわれてもそもそもdocker imageってなんだという話になりますので、docker imageに関してです。docker imageの実態は複数のディレクトリとその中に置かれたファイル群です。それぞれのディレクトリはレイヤーを構成しています。overlay filesystemという技術によって、これらのディレクトリ群の和集合をとってファイルシステムを構成することができます。例を出すと、hogeというディレクトリにa.txtというファイルが有り、hoge2というディレクトリにb.txtというファイルがあるとします。これらを重ね合わせfooというディレクトリを作ると、a.txtとb.txtというファイルが両方置かれています。overlay filesystemはこういう動作をするものです。これらのディレクトリを直に確認するにはdocker inspectでGraphDriverキーのところに書かれている内容を見ればOKです。
Dockerではこれらimageを構成するディレクトリをoverlay filesystemでまとめ上げて、pivot_rootでまとめ上げたファイルシステムにルートファイルシステムを移しているという動作をしています。
いかがでしたか?
も何も無いかと思いますが、以上が大まかなDockerの仕組みでした。コンテナ仮想化というからには、環境差異を埋める何かしらの変換レイヤーを追加しているのかと思っていましたが、むしろプロセスに見せるリソースを制限することで別マシンで動いているように錯覚させているというところがとても面白く感じました。減らす方向に作られているので、コンテナ仮想化が他の仮想化と比べて軽量であるのも納得が行きます。なお、Dockerではコンテナを動かす際にruncというプログラムを使っており、今回説明した動作をしていますが、同じ結果を得るのに他にも色々なプログラムが存在しているようです。例えば、AWS Lambdaで動いているfirecracker、Googleが作っているgVisorなどがあるようです。これらはOCIランタイムと呼ばれており、興味がある方は調べてみてください。個人的には更に興味が湧いたのでruncのソースコードを読み始めています。仕組みがわかると面白いですね。
参考サイト
「Kernel/VM/探検隊online part1」にLT枠で参加しました – inductor’s blog
【連載】世界一わかりみが深いコンテナ & Docker入門 〜 その1:コンテナってなに? 〜
Introduction · Container Security Book
Overlay Filesystem と Docker について – CUBE SUGAR CONTAINER
今話題のいろいろなコンテナランタイムを比較してみた
[Docker Meetup Tokyo #26発表レポート] | by Kohei Tokunaga | nttlabs | Medium
コメント