腑に落ちなかったこと
コンテナを学び始めたとき、ほとんどの解説が「仮想マシンとの比較」から入る。「仮想マシンはOSごと仮想化するが、コンテナはカーネルを共有するので軽い」。
だが腑に落ちなかった。「カーネルを共有する」とはどういうことか。仮想マシンの「軽量版」なら、何を軽くしているのか。そもそもコンテナは何を「仮想化」しているのか。
調べていくうちにわかった。コンテナは仮想マシンとは根本的に異なる技術である。 仮想マシンの延長で理解しようとすること自体が、理解を遠ざけていたのである。
一言で言えば、コンテナ ≒ 強化されたOSのユーザ管理 + ネットワーク仮想化である。この記事では、なぜそう言えるのかを順を追って説明する。
仮想マシンは「ないものを、あるように見せる」技術
まず仮想マシンの本質を整理する。
仮想化とは、物理リソースを抽象化し、実際の状態とは異なる状態に見せかけることである。仮想マシンの場合、1台の物理マシン上に、あたかも独立した物理マシンが複数台あるかのように見せかける。
各仮想マシンは、自分専用のCPU・メモリ・ディスク・ネットワークインターフェースを持っていると「思い込んでいる」。実際にはハイパーバイザがそう見せかけているだけで、仮想マシン上のOSは、自分が仮想環境にいることすら知らない。
キーワードは「見せかける」である。存在しないハードウェアを、存在するかのように振る舞わせる。これが仮想化の本質である。
コンテナは「見せかけ」ていない
コンテナは根本的に違う。何かを「見せかける」のではなく、OSのカーネル機能を使って、実際に環境を分離する。
具体的には、Linuxカーネルが持つ2つの機能が核になる。
Namespace — 「見える範囲」を制限する
Namespaceは、プロセスから見えるOSリソースの範囲を制限する機能である。
通常、OSの上で動くプロセスは、すべてのプロセス一覧・すべてのファイルシステム・すべてのネットワークインターフェースを見ることができる。Namespaceはこの「見える範囲」を制限する。
| Namespace | 制限する対象 |
|---|---|
| PID namespace | 見えるプロセスの範囲 |
| Mount namespace | 見えるファイルシステムの範囲 |
| Network namespace | 見えるネットワークインターフェースの範囲 |
| UTS namespace | ホスト名 |
| User namespace | UID/GIDのマッピング |
| IPC namespace | プロセス間通信の範囲 |
例えば、あるプロセスをPID namespaceに入れると、そのプロセスからは同じnamespace内のプロセスしか見えなくなる。他のプロセスが消えたわけではなく、見えなくなっただけである。
つまりこれは「仮想的なOSを作っている」のではなく、1つのOSの中で、見える範囲を区切っているだけである。

cgroups — 「使える量」を制限する
cgroups(Control Groups)は、プロセスが使えるリソースの量を制限する機能である。
- このプロセスグループはCPUを最大50%まで
- このプロセスグループはメモリを最大512MBまで
これも仮想化ではない。仮想的なCPUやメモリを作り出しているのではなく、実際のCPUとメモリの使用量に上限をかけているだけである。
仮想マシンとの決定的な違い
ここで「仮想マシンも見える範囲を制限しているのでは?」と思うかもしれないが、それは違う。
仮想マシンは各VMに完全に独立した仮想ハードウェアを与え、その上で独立したOSを起動する。各OS同士は、相手の存在すら知らない。制限しているのではなく、そもそも別世界を作っているのである。
例えていえば、コンテナは「同じ家の中に壁を立てて部屋を分ける」。仮想マシンは「家を丸ごと別に建てる」。

| コンテナ | 仮想マシン | |
|---|---|---|
| OS | 1つ。カーネルを共有 | VMごとに独立したOSが動く |
| 隔離の方法 | 1つのOSの中で見える範囲を制限 | 別のOS同士なので最初から見えない |
| 隔離の強度 | 壁で仕切った部屋(カーネル共有ゆえに漏れうる) | 別の家(原理的に漏れない) |
この違いが、セキュリティ特性の差にも直結する。コンテナはカーネルを共有しているため、カーネルの脆弱性を突かれると隔離が破れる可能性がある。仮想マシンはOS自体が別なので、そのリスクは原理的に低い。「軽さ」と「隔離の強度」はトレードオフの関係にある。

OSのユーザ管理と同じ構造
ここまで読んで、既視感がないだろうか。
OSのユーザ管理も、まったく同じことをやっている。
| ユーザ管理 | コンテナ | |
|---|---|---|
| 見える範囲の制限 | ファイルパーミッション(他ユーザのファイルにアクセスできない) | Namespace(他コンテナのプロセスやファイルが見えない) |
| 使える量の制限 | ulimit(プロセス数やファイル数の上限) | cgroups(CPU・メモリの上限) |
| 仕組み | OSカーネルの機能 | OSカーネルの機能 |
どちらも「OSが持つ機能を使って、環境を分離する」という点で同じ構造である。コンテナはユーザ管理の延長線上にある技術であり、仮想マシンの延長線上にはない。
ユーザ管理では「AさんのファイルをBさんが読めないようにする」レベルだったものを、コンテナではプロセス・ファイルシステム・ネットワークまで丸ごと分離できるように強化した。コンテナとは、強化されたユーザ管理なのである。
なぜ「仮想化」と混同されるのか
1つだけ、コンテナが仮想化技術を使う部分がある。ネットワークである。
コンテナのNetwork namespaceには、仮想ネットワークインターフェース(veth)が割り当てられる。これは実在しないネットワークインターフェースを作り出しているので、確かに「仮想化」である。
しかしこれはコンテナの本質ではなく、ネットワークを分離するための手段の1つに過ぎない。プロセスの隔離もファイルシステムの隔離もリソースの制限も、すべて仮想化ではなくカーネル機能による実際の分離である。
ネットワーク部分だけを見て「コンテナは仮想化技術」と言うのは、「家にエアコンがあるから家は家電だ」と言うようなものである。
まとめ
| 仮想マシン | コンテナ | |
|---|---|---|
| 本質 | ないものを、あるように見せかける | あるものの、見える範囲と使える量を制限する |
| 使う技術 | ハイパーバイザ(ハードウェアの抽象化) | Namespace + cgroups(カーネル機能) |
| 近い概念 | 仮想化 | OSのユーザ管理 |
| OS | 仮想マシンごとに独立したOSが動く | 全コンテナが1つのOSカーネルを共有する |
コンテナを「軽量な仮想マシン」と理解すると、「なぜカーネルを共有するのか」「なぜ起動が速いのか」が永遠に腑に落ちない。仮想マシンの枠組みで考えている限り、答えは出ない。
コンテナは仮想マシンとは別の系譜の技術である。強化されたOSのユーザ管理に、ネットワーク仮想化を加えたもの。そう捉えれば、「カーネルを共有する」のは当然である。同じOSの機能を使っているのだから。「なぜ起動が速いのか」も自明になる。OSを起動していないからである。

コメント