Dockerのimage, container, Dockerfile
はじめに
Dockerのイメージ(image)、コンテナ(container)、Dockerfileの違いがよく分からなかったので調べてみた。
巷ではよく、コンテナはイメージをレイヤーごとに重ねたもので、Dockerfileは設計書と言われる。 イメージには
- Linuxファイルシステムや
- アプリケーションといったファイルやディレクトリ
が含まれていて、
build/runすることでコンテナという形になり、アプリが実行可能になる。
このような説明を聞いて、私はすっと内容が頭に入ってこなかった。 分からない点は
- ファイルシステムとは?
- レイヤーとは何を表しているのか?
- イメージの集合がコンテナなのか? という点である。
LLMと問答していると、数学の言葉で説明したほうが個人的にわかりやすいと感じたので、この記事にメモした。 先行研究が見つからなかったので、必ずしも正しい話ではないことに注意しながら読んでほしい。
結論をいえば、ファイルシステムとはパスからノードへの部分関数であり、レイヤーとはそのファイルシステム状態への変更操作(差分)である。イメージは差分の列と実行設定の組であり、コンテナはイメージを評価したファイルシステム状態の上に書き込み可能な差分を重ねた実行中の状態である。「イメージの集合がコンテナ」ではなく、「イメージから生成された一時的な実行環境」がコンテナである。
ファイルシステム
パス
パス全体の集合をとすると、その元は例えば
のような名前付きの位置を表す。
対象
ファイルシステム上の対象(ノード)全体の集合をとする。各ノードには、
という組みであると考える。ここで、
- はファイル、ディレクトリ、symlinkなどの種別
- は内容
- は権限、所有者、時刻 を表す。
ファイルシステム
ファイルシステム状態とは、パスからノードへの部分関数
である。パスがあるからと言って、ファイルが必ずしも存在するとは限らないので、部分関数という取り扱いをしている。すなわち、あるパスに対して、が定義されていないことがある。例えば、次のような例である:
これは二つのみ対象が存在するファイルシステム状態である。
/bin/sh
/etc/os-release
という構成のほうが見やすいかもしれない。ここでは「パスにノードが対応している」という意味である。
ファイルシステム状態
ファイルシステム状態全体の集合をと書く。したがって、上記のファイルシステム状態はであった。
差分
差分とは、ファイルシステム状態を別の状態に移す写像である:
である。差分全体の集合をとし、はである。差分には「追加」「変更」「削除」の三つがある。
差分:追加
差分「に内容helloを持つファイルを追加する差分」をとし、状態に対して適用したものをとしてみよう。これは、状態に対して、が新たに定義された状態を返す。
たとえば、
なら、
である。
差分:削除
差分「に内容helloを持つファイルを削除する差分」をとする。つまり、
差分:変更
差分「に内容helloを持つファイルを変更する差分」をとする。つまり、
レイヤーとイメージ
Dockerにおけるレイヤーは、この意味での差分として理解できる。すなわち、レイヤーとはファイルシステム状態全体に対する変更操作である。
差分列とイメージ
差分の有限列全体の集合をとする。その元はの形をしている。
イメージに埋め込まれる実行設定の集合をとしよう。その元には、たとえば次のようなコマンドが含まれる:
CMDENTRYPOINTENVWORKDIRUSER
イメージ全体の集合を
と定める。すなわち、あるイメージはという組みである。ここで、
- はファイルシステムの差分の列
- は実行設定 を表す。
イメージは「一つの完成済みのディレクトリ」ではなく
- ルートファイルシステムを作る差分列
- その上で対象がどう起動するかの設定 の組である。
An OCI Image is an ordered collection of root filesystem changes and the corresponding execution parameters for use within a container runtime. This specification outlines the JSON format describing images for use with a container runtime and execution tool and its relationship to filesystem changesets, described in Layers. OCI Image Configurationより引用
Dockerfile
実際にDockerを使うとき、すでに存在するイメージを利用することがある。そのようなイメージをベースイメージとする。たとえば、alpine、ubuntu、python:3.13などは、この意味でのベースイメージである。
DockerfileにあるコマンドFROM alpineは既存のイメージを初期として採用する操作である。
COPYやADDによって参照される入力ファイル集合をとする。これをビルドコンテキストと呼ぶこととする。
Dockerfile のbuildは、概念的には
である。すなわち、Dockerfileによるビルドはベースイメージとビルドコンテキストを入力として、新しいイメージを返す操作である。したがって、Dockerfileはコンテナの設計書ではなく、厳密にはイメージを生成する規則である。
Dockerfileの各命令をもう少し詳しくみるために、現在のイメージをとしておこう。
FROM
FROM bは、既存イメージを初期値として採用する操作である。
COPY
COPY a:tは、ビルドコンテキストのファイルをターゲットパスに配置する差分
を生成し、
へ写す。ここでは列の連結を表す。
RUN
RUN cmdは、ビルド時にコマンドを実行し、その結果生じる差分
を追加する操作である。すなわち
へ写す。
CMD, ENTRYPOINT, ENV, WORKDIR, USER
CMD、ENTRYPOINT、ENV、WORKDIR、USERはファイルシステムの差分を増やすのではなく、設定を更新する操作である。たとえば、CMD cは
という形の更新であり、新しいファイルシステムのレイヤーを作らない。
コンテナ
イメージの評価
イメージの差分列を空のファイルシステム状態に順次適用することで得られるファイルシステム状態を
と書く。これはイメージを「評価」した結果であり、読み取り専用として固定される。
書き込み可能レイヤー
コンテナが起動すると、の上に新たな書き込み可能な差分が追加される。コンテナ上でのファイル操作はすべてこのに反映される。コンテナ上でのファイルシステムの実効状態は
である。
コンテナ
コンテナ全体の集合をとする。コンテナは
という組みである。ここで、
- はイメージから得られる読み取り専用のファイルシステム状態
- はコンテナ起動後の書き込み可能な差分(初期値は恒等写像)
- は実行設定
- はプロセスの実行状態(, , など)
を表す。
docker run
docker runはイメージからコンテナを生成する操作であり、
と書ける。具体的には、に対して
である。は恒等写像(何も変更しない差分)を表す。
一つのイメージから複数のコンテナを起動できる。各コンテナは独立したを持つため、互いのファイルシステムへの書き込みは干渉しない。
コンテナの停止と削除
docker stopでコンテナを停止しても、は保持される。docker rmでコンテナを削除すると、は破棄される。イメージは不変であるため、コンテナを削除してもは影響を受けない。
まとめ
本記事で導入した概念を整理する。
| 概念 | 形式的な定義 | 役割 |
|---|---|---|
| ファイルシステム状態 | パスからノードへの部分関数 | |
| レイヤー(差分) | ファイルシステム状態への変更操作 | |
| イメージ | 差分列と実行設定の組 | |
| Dockerfile | イメージを生成する規則 | |
| コンテナ | イメージを評価し実行中の状態 |
これらの関係は次のように図示できる:
- Dockerfileはベースイメージとビルドコンテキストから新しいイメージを生成する規則である。「コンテナの設計書」ではなく、正確には「イメージを生成する規則」である。
- イメージは差分列と実行設定の組であり、不変(immutable)である。
- コンテナはイメージを評価したファイルシステム状態の上に書き込み可能な差分を重ねた、実行中の状態である。コンテナを削除するとは失われるが、イメージは影響を受けない。