TT Lab
はじめる
学ぶ 学習パス コース

Docker基礎

イメージとは何で、コンテナとは何か

TT Labで続きを見る

一言でいうと

イメージは読み取り専用のレイヤーを積み重ねたファイルツリーで、コンテナはその上に書き込みレイヤーを1枚載せて実行しているごく普通のLinuxプロセスです。

なぜ必要なのか

「コンテナは軽量なVM」という説明が長く出回っていますが、この文は実務のほとんどの問題で誤った予測を導きます。ホストでpsを実行すると、コンテナ内のnginxがそのまま見えます。

48213 root      nginx
48261 systemd+  nginx

同じプロセスをコンテナ内で見ると、1 nginx、29 nginxと表示されます。プロセスが2つあるのではなく、同じプロセスを別のネームスペースから見ているだけです。カーネルのバージョンを確認すると、さらにはっきりします。ホストも、alpineコンテナも、ubuntuコンテナも、すべて同じカーネルを報告します。イメージが提供するのはカーネルではなく、ユーザー空間のファイルツリーだけです。

どう動くのか

レイヤーはユニオンファイルシステム(overlayfs)で重ね合わされます。

overlayfsのレイヤー構造: 読み取り専用のイメージレイヤーの上に書き込みレイヤーが1枚載り、mergedという1つのファイルツリーとして見えます。読み取りは上から順に探し、書き込みは下のファイルをまるごと上へコピーするcopy-upで行われ、削除はホワイトアウトの印だけを残して下の元ファイルはそのまま残ります

merged   <- 컨테이너가 보는 뷰
  ↑ upperdir  (쓰기 가능, 컨테이너 레이어)
  ↑ lowerdir  (읽기 전용, 이미지 레이어들이 겹겹이)

すべてのレイヤーには、内容の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で証明します。