イメージとは何で、コンテナとは何か
一言でいうと
イメージは読み取り専用のレイヤーを積み重ねたファイルツリーで、コンテナはその上に書き込みレイヤーを1枚載せて実行しているごく普通のLinuxプロセスです。
なぜ必要なのか
「コンテナは軽量なVM」という説明が長く出回っていますが、この文は実務のほとんどの問題で誤った予測を導きます。ホストでpsを実行すると、コンテナ内のnginxがそのまま見えます。
48213 root nginx
48261 systemd+ nginx
同じプロセスをコンテナ内で見ると、1 nginx、29 nginxと表示されます。プロセスが2つあるのではなく、同じプロセスを別のネームスペースから見ているだけです。カーネルのバージョンを確認すると、さらにはっきりします。ホストも、alpineコンテナも、ubuntuコンテナも、すべて同じカーネルを報告します。イメージが提供するのはカーネルではなく、ユーザー空間のファイルツリーだけです。
どう動くのか
レイヤーはユニオンファイルシステム(overlayfs)で重ね合わされます。
merged <- 컨테이너가 보는 뷰
↑ upperdir (쓰기 가능, 컨테이너 레이어)
↑ lowerdir (읽기 전용, 이미지 레이어들이 겹겹이)
- 読み取り: 上から順に探していき、いちばん上にあるファイルが優先されます。
- 書き込み: 下のレイヤーのファイルを変更するときは、上へコピーしてから変更します(copy-up)。
- 削除: 実際には消さず、上のレイヤーにホワイトアウトの印を残します。
すべてのレイヤーには、内容のSHA-256ハッシュで名前が付きます。内容が同じなら名前も同じなので、重複して保存されることはなく、ハッシュを比べるだけで完全性を検証できます。イメージIDがsha256:...なのも同じ理由です。
レイヤーが積み重なる瞬間に何が決まるのか
Dockerfileの命令1つがレイヤー1つです。そして、レイヤーは削除されません。この2つの文から、実務の落とし穴のほとんどが生まれます。
COPY secret.pem /tmp/secret.pem ← 레이어 3에 파일이 들어간다
RUN ./setup.sh && rm /tmp/secret.pem ← 레이어 4는 "지웠다" 는 기록일 뿐
最終イメージでは/tmp/secret.pemが見えませんが、レイヤー3にはそのまま残っています。docker saveで展開すれば、誰でも取り出せます。機密情報をイメージに入れてから削除しても、削除したことにはなりません。ビルドシークレット(RUN --mount=type=secret)やマルチステージで解決します。
同じ理由で、サイズも小さくなりません。
RUN apt-get update && apt-get install -y build-essential # +400MB
RUN apt-get purge -y build-essential # 여전히 +400MB
1つのRUNの中でダウンロードと削除を済ませると、そのレイヤーの最終状態だけが残ります。
コンテナを削除しても残るもの
コンテナを削除すると、書き込みレイヤーが消えます。そのため、コンテナ内で作ったファイルはdocker rmと同時になくなります。これがボリュームが必要な理由です。
| 保存場所 | コンテナ削除後 | 書き込み性能 | 使う場面 |
|---|---|---|---|
| 書き込みレイヤー | 消える | 遅い(copy-up) | 一時ファイル |
| 匿名ボリューム | 残る(名前がないため見つけにくい) | 速い | 意図せずできるもの |
| 名前付きボリューム | 残る | 速い | DBのデータ |
| バインドマウント | ホストのファイルのまま | 速い | 開発中のソース |
「copy-up」が重要です。overlayfsでは、下のレイヤーのファイルを変更すると、全体が上のレイヤーへコピーされます。1GBのファイルの1バイトを書き換えると、1GBがコピーされます。データベースをボリュームなしでコンテナレイヤー上で動かしてはいけない理由が、これです。
イメージ名とダイジェスト
タグは動きます。nginx:1.25は、昨日と今日で別のイメージかもしれません。latestはなおさらです。「最新」という意味ではなく、タグを省略したときの既定の名前にすぎません。
nginx:1.25 ← 움직인다
nginx@sha256:a1b2c3... ← 절대 안 움직인다
本番環境へのデプロイは、ダイジェストで固定します。そうすれば、「昨日は動いたのに今日は動かない」ときに、まず「昨日と今日で同じイメージか」を確認できます。
現場での姿
同じベースを使うコンテナ100個がlowerdirをまるごと共有するため、コンテナの作成が速く、ディスクも節約できます。反対に、copy-upのコストがあるため、データベースファイルのような大きな書き込みは、必ずボリュームへ逃がす必要があります。
もう1つあります。docker saveとdocker exportを取り違えると、丸1日を無駄にします。saveはレイヤー・タグ・履歴を含むイメージを書き出し、exportはコンテナのファイルシステム1枚だけを書き出します。exportしたものをimportすると、ENTRYPOINTもENVもすべて失われた状態で復活します。
次のラボですること
イメージ一覧を自分で確認し、コンテナを1つ起動してログを確認し、タグが新しいイメージを作らないことをイメージIDで証明します。