Dockerの仕組み~「仮想化」ではない

2019年7月2日

Dockerまとめがあるので参照されたい

Dockerはどういう仕組みで動いているのかを知りたいと思い、少々検索して記事を読んでみるのだが、さっぱりわからない。曰く、これまでの仮想化はマシン仮想化で重かったのだが、Dockerはコンテナ仮想化で軽い云々という解説とイメージ図、いかにDockerにメリットがあるかという説明である。

これでは素人にもわからず、玄人にもわからない、何の原理さえも説明されていないからだ。以下では、さらに検索してみて自分なりにわかった部分を解説していく。

※以下の記述には多分に想像も含まれている。間違いがあれば指摘していただきたい。

従来の仮想化

ハイパーバイザー型とかホストOSとかの言葉があるが、やっていることに変わりはない。何らかの仮想的なマシンという「幻想」を(いくつでも)作り出し、その上にOSを載せるというものだ。

インストールされるOSの側からは物理マシンとして見え、そしてOSの種類は(そのマシンに適合するものであれば)何でも良い。このため、ホストOS方式の場合は、Linux上でWindowsを動作させたり、その逆も可能であったりする。ハイパーバイザ型の場合は、WindowsとLinuxが共存して動作したりできる。

この方式の欠点としては、やたらと「かさばる」ことだ。

  • OSまるごとの実行環境というボリュームがある
  • 少なくとも仮想メモリをOSごとに割り当てなければならない。

Dockerの「仮想化???」

Dockerも「仮想化」するというのだが、この言葉はむしろ使わない方がよい。仮想マシンにおける「仮想化」とは全く意味が異なるからだ。

まず基本としてDockerでは、Linux上でのLinuxのプロセスの実行しかしない。OS仮想化のようにWindowsやら何や自由にOSを選択することはできない。

※例外があるのだが、これは後述する。基本はLinux上でのLinuxプロセスの実行だ。

そして、ホストとなるLinuxというOSは一つしか動作させない。その上で単にLinuxプロセスが複数動作するだけだ。OSが複数動作するわけではない。一つのLinux上でLinuxプロセスを扱うだけなのである。

では、何が「仮想化」なのだろうか?

Dockerコンテナの実行環境

Dockerの一つのコンテナには、あたかも自分だけがLinuxカーネル上で動作するかようなイメージが格納されている(らしい)。

つまり、それぞれ独自の/etcディレクトリがあるし、/binや/usr/binがあり、その中にコマンドがあり、/homeがあるという具合なのだが、こういったものをそれぞれ詰め込んであるコンテナを、複数同時に一つのLinux上で動作させる(実行させたい)のである。

そこで最初に出てくるのがchrootだ。この機能を使用すると、あるプロセスに対し、任意のディレクトリをrootディレクトリと認識させることができ、それ以外のディレクトリにはアクセスできない。

例えば、あるプロセスに対して/var/docker/process0というディレクトリを与え、そこにchrootすると、そのプロセスは、このディレクトリが/であると思いこみ、それ以外の部分にはアクセスできなくなる。もちろん、このプロセスが作り出した別のプロセスでも同じだ。

このようにして、少なくとも各コンテナの扱うディレクトリを分離することができる。それぞれのコンテナは「/から始まる全ファイルシステム」を自由にしているものと思いこんでいるわけだ。

chrootだけではなく、他にも様々な仕組みがあるようだ。cgroupsやnamespacesといったLinuxカーネルで提供されている機能により、「自分達以外のプロセスは見えない」などの分離が行われている。

仮想化ではなく隔離化

このような仕組みにより、それぞれのコンテナの実行環境が切り離されているだけの話だ。「仮想マシン」のノリで「仮想」という言葉を使うのは不適当であることは明白だろう。

ともあれ、Dockerが「軽い」という理由は、何のことは無い、基本的には普通にLinuxのプロセスを動作させているだけだからだ。

これは仮想化ではなく、むしろ隔離化と呼ぶ方が適切である。

LinuxカーネルとLinuxコンテナ

したがって、Dockerとは、基本的にLinuxカーネル上で、Linuxプロセスを動作させるものなのである。

Linuxとしての実行に必要なカーネル以外の部分は、すべてそれぞれのコンテナの中に含まれているため、Linuxであればどのようなコンテナでも良い。

つまり、CentOS上のDockerでは、Fedoraのコンテナでも、Ubuntuのコンテナでも動作する。しかし、Windowsのコンテナを動作させるわけにはいかない。仕組みが異なるからだ。ここが仮想マシンとは全く違う部分だ。

そして、あるマシン上で構成したLinuxコンテナを別のマシンに持っていくこともほぼ問題無いというわけだ。

自由に移動できるわけではない

ここで良く考えてみると、「あるマシン上で構成したLinuxコンテナを別のマシンに持っていくこともほぼ問題無い」は厳密には成立しないことがわかる。まず第一に、ホストとコンテナのCPUアーキテクチャが一致してないと動かない

当たり前だ、Intelの64bitCPU用のバイナリの詰まったコンテナを、32bitのCPUや、あるいは異なるアーキテクチャのCPUで動作させることはできない。

また、その機能がカーネルバージョンに依存するとか、ハードウェアに依存するとかのコンテナは問題が出るだろう。

Dockerは非常に軽い代わりに、仮想マシンより制限が大きくなるのである。

Docker for Windows

したがって、従来的には、Dockerというのは「Linux上でLinuxコンテナを扱うもの」だったらしいのだが、ここにMicrosoftも協力しだしたらしく、Docker for Windowsというものが出てきた。これはいかなるものかと言えば、

  • Windows上でLinuxカーネルを動作させ、その上でLinuxコンテナを動作させる。
  • Linuxコンテナではなく「Windowsコンテナ」なるものも動作させることができる。

Microsoftとしては当然だろう。世の中がLinuxコンテナだけになってしまっては困るため、協力する代わりに「Windowsコンテナ」もデビューさせたというわけだ。

もちろん、こちらとしては「Windowsコンテナ」などはどうでも良い。重要なことは、Windows上でもLinuxコンテナを扱うことができるという点である。これはありがたい。

Dockerイメージ

かようなわけで、Dockerコンテナとは「カーネルのみが含まれないLinux環境」と考えればわかりやすくなる。このコンテナの元となるものをイメージと呼ぶらしいのだが、これはVMWareやVirtualBox用の仮想マシンイメージと同じようなものと考えれば良いだろう。

しかし、仮想マシンイメージではCPUの種類位の制限しか無い(それさえもエミュレーションで乗り越えることもできるが)のだが、Dockerコンテナではずっと制限が大きくなることに注意しなければならない。