Dockerは「映画スタジオのセット」である ── コンテナの本質とユーザランドの話
「Dockerは仮想化技術だ」と説明されることがありますが、厳密には CPUなどのハードウェアを丸ごとエミュレートする仮想化技術ではありません 。
Dockerは、共通のカーネル上で、プロセスから見える環境を隔離し、リソースへのアクセスを制御し、アプリケーションとその依存関係をまとめて実行するための技術です。
とはいえ、「なんて?」となりますよね。
こういうのって、説明に説明がどんどん必要になるので、初学者に非常に良くない。
深く考えすぎると迷宮入りしがちなDockerの仕組みを、今回は 「映画スタジオのセット」 に例えて解説していきます。
Linux前提の注意
前提として、この記事はLinuxにおけるDockerの仕組みを前提にしています。
MacやWindowsでDockerが動くのは、裏で軽量なLinux仮想マシンが1台動いていて、その中にスタジオを設営しているからです。ちょっと特殊なケースなので注意してください。
1. コンテナとは「貸しスタジオ」である
さて、コンテナといえば、貨物船に積まれた大きな鉄の箱を思い浮かべるかもしれません。中身が何であろうと、同じ仕組みで運び、積み下ろしできる箱、アレです。
Dockerにも似た考え方でいろんな名前がつけられています。例えば「コンテナ」といった具合です。ただ、今回注目したいのは箱を運ぶことではなく、その箱の中に作られる実行環境の方です。
……とはいえ、貨物船や客船の仕組みに馴染みがないと、コンテナ自体、ピンとこないでしょう。もっと身近なもので言えば、「貸し事務所」や「映画の撮影スタジオ」 が感覚として近いはずです。僕の感覚には一番近かったので、せっかくなのでこの例で説明していきます。
映画の撮影スタジオを想像してみてください。
スタジオの中には、電話機や机、筆記用具などを好きな配置で並べて「オフィスのセット」を作ります。これがDockerでいう 「箱の中の実行環境」 です。
ここで重要なのは以下の3点です。
- インフラ(カーネル)はスタジオ本体のものを共有している
カーネルとはOSの中核部分であり、スタジオでいう電気や水道、フロアスペースといったインフラそのものです。セット(コンテナ)は、スタジオの大元から必要な分だけ設備を引き込んで使っています。 - 他のコンテナからは見えないが、ホストからは丸見え
隣のスタジオ(別コンテナ)からは、基本的にはこちらのセットの中身は見えません。しかし、スタジオのオーナー(=ホストOS)は、どのセットで何が使われているかを把握していますし、外から見ることだってできます。 - 配管が甘ければ漏れる(セキュリティ)
壁で仕切られて「見えない」だけで、実はすぐ外にホストの資源があります。もしセットの作り方や配管(権限設定や名前空間の分離)が悪ければーーつまり、申請が雑に行われていて、チェックが甘かったり、設計が不十分だったりすると、結構まずいことが起こります。スタジオの管理台帳をコンテナに見せてしまったりすると最悪です。
こういう経路を通ってよからぬものが外へ漏れ出しますし、外に手が届けばスタジオ全体をどうにでもできてしまいます(コンテナエスケープ)。
つまりコンテナとは、独立した別世界ではなく、「共通のカーネルが提供する機能を、隔離された環境から利用している仮設空間」 に過ぎないのです。
豆知識
これはオタクトークですが、この「隣のセットが見えない壁」を作るのがLinuxの namespaces、「電気や水道をどれだけ使っていいか」を制御するのが cgroups です。
Dockerは、そうした仕組みをうまく組み合わせて、セットを簡単に作ったり壊したりできるようにしているわけです。
2. 「じゃあ、どうしてOSのイメージを入れるの?」
「カーネルを共有しているなら、なぜわざわざ ubuntu や alpine といったOSのイメージ(ベースイメージ)を入れるのか?」
おっしゃる通りで、本来はUbuntuなどのディストリビューションのベースイメージすら不要です。
実際、Dockerには全くの無から始める scratch という空っぽのイメージが存在します。たとえばGo言語で書かれたシングルバイナリのプログラムなら、scratch の上でも「ある程度簡単に」動かすことができます。
ただ、本当に何もない空間だと困ることも起きます。
例えば「現在時刻を現地時間で表示したい」と思ってもタイムゾーン情報(tzdata)がなかったり、「HTTPSで外部と通信したい」と思ってもCA証明書(ca-certificates)がなかったりして、機能が動かないのです。これらをいちいち自前で用意してやるのは骨が折れます。
つまり、私たちが普段使っている ubuntu などのベースイメージは、UbuntuというOSそのものが必要なのではなく、「都合よく使いたい小道具(ユーザランドーー便利な道具箱やディレクトリ構造)が一通り揃っているセット」 として定義されたものを借りているだけなのです。
もちろん、セットと言ってもハリボテの鳴らない電話なんかではなく、本物の調理器具やレジ(本物のライブラリやコマンド群)が揃っています。『一式揃った本物の道具箱』を表すのに『あれのセット』という呼び名が便利なので、『Ubuntuのイメージ』と呼んでいるわけです。
つまり、ベースイメージの指定 というのは「このスタジオの内装、とりあえず『マクドナルド』に合わせておいて!」という発注みたいなものなんです。
これはあくまで 「稼働するスタジオセット」 です。裏側のインフラはスタジオ本体のものを使っていますが、セットの中で働くスタッフ(プロセス)にとっては、その「現実」で十分なのです。
精巧に作られたマクドナルドの撮影セットには、パティを焼くグリルだって、フライヤーだってある。本物の電気とガスを引き込めば、マクドナルドのクルーはそのセットの中で本物のポテトだって揚げられます。それと同じことです。
3. Dockerfileは「美術スタッフへの指示書」、Composeは「街の設計図」
さて、じゃあこの「イメージ」。これが肝だということはわかりました。とはいえ、やっぱり深く考えすぎるとわからなくなります。そんな時は スタジオセット を作る Dockerfile を一行ずつ眺めてみるとシンプルに理解できます。段取りが完了し封を切る寸前のスタジオセット、つまり、まだ稼働していない状態のスタジオを イメージ として捉えるとわかりやすいです。
Dockerfile = スタジオ設営の指示書
Dockerfile に書かれているのは、スタジオの美術スタッフに向けた設営指示書です。
FROM: 「マクドナルドの基本セットをベースにして」COPY: 「この店アイスやってるから、そう、ここの棚に置いて」RUN: 「置いたら、ビニール剥がしてアイスクリームマシン組み立てておいて」CMD/ENTRYPOINT: 「本番が始まったら、スタッフはこの役割を受け持って!」
ここまでやっておくと、RUN まででスタジオセットの基本的な内装や設備が整いますよね。
そして、実際に docker run を行うと、スタジオセットの封が切られ、 CMD / ENTRYPOINT に従って、スタッフが本番の役割を受け持ち、本番が始まる、というわけです。
Docker Compose = 複数セットの関係性定義
そして docker-compose.yml は、複数のスタジオセットを組み合わせて一つの「街」や「業務フロー」を作るための定義書です。
- 「マクドナルド店舗のセット」
- 「倉庫のセット」
- 「セントラルキッチンのセット」
これらをいくつ用意し(scale / replicas)、どのセットとどのセットの間に道を繋ぎ(networks)、どのセットよりどのセットが先に仕上がってないといけないか(depends_on)といった関係性をまとめて定義しているに過ぎません。
4. 「セット」だとわかれば、アンチパターンが見える
コンテナが「精巧に作られた撮影セット」だと理解すると、Dockerにおける「やってはいけないこと(アンチパターン)」の理由が自然と腑に落ちます。
- ❌ 稼働中のコンテナに入って直接設定を変える
やるべきではないとわかります。セットだからです。
現場のセットの壁紙を勝手に塗り替えたら、設計図(Dockerfile)と食い違い、次の撮影のために隣のスタジオで設営する(再作成)時に元に戻ってしまいます。変えるなら指示書の方を変えるべきです。 - ❌ コンテナの中に大事なデータを保管する
やるべきではないとわかります。セットだからです。
撮影が終われば、セットは使われなくなります。そして、スペースの要請に応じて解体・撤去されます。大事な売上帳簿や撮影したフィルム(永続データ)は、セットの机の引き出しではなく、スタジオ外の金庫(Volumeや外部DB)に保管しなければなりません。
セットだからこそ、「明日は隣のスタジオで同じ撮影をしたい」 と言われたら、指示書ひとつで全く同じ空間を即座に設営できるべきです。
セットだからこそ、「基本は同じで、やっぱフルーリーも売ってる店舗にちょっと変えたやつ」 も簡単に設営できるべきなのです。
ちなみに、これは「アイスをやってるマクドナルド」をベースイメージにする事で、非常に簡単にできます。イメージはベースイメージになれるんです。FROM アイスをやってるマクドナルド、COPY ./flurry_machine /セット内の棚でフルーリーマシンを入れて、RUN ./セット内の棚/flurry_machine/setup.sh でマシンの組み立てをするだけです。アイスをやってるマクドナルドのセットをベースにしているので、アイスの基本的な設備はすでに揃っています。
これが
FROM アイスをやってるマクドナルド:latest
COPY ./flurry_machine /セット内の棚
RUN ./セット内の棚/flurry_machine/setup.shの意味です。 CMD がないのも、アイスをやってるマクドナルドのセットをベースにしているからで、そっちですでにスタッフに役割が割り当てられているからですね。
おわりに
コンテナの中で動くプログラムたちは、自分が作られたセットの中にいるとは露知らず、与えられた「マクドナルドの内装、調理器具」と、壁の裏から供給される「ホストのカーネル」を使って、今日も元気にポテトを揚げています。
つまり、Dockerとはおおむねこういうことです。
"Good morning! And in case I don’t see ya, good afternoon, good evening, and good night!"
(おはよう! 念のため、こんにちは、こんばんは、おやすみ!)
あなたはクリストフとして、トゥルーマン・ショーを撮ってるんですよ。実は。
昔書いたエッセイなのですが、せっかくなので供養します。