先に要点
- Docker イメージ は読み取り専用のレイヤを積み重ねたテンプレートで、
docker imagesで一覧と SIZE を確認できます。 - コンテナ はイメージの上に薄い書き込み可能レイヤを足して起動した実体で、
docker ps -aで起動中も停止中も確認できます。 - コンテナ内で作ったファイルは書き込み可能レイヤに乗るため、
docker rmでコンテナごと消えます。残すには ボリューム を使います。 docker pullはイメージ取得、docker runで初めてコンテナが動き、Dockerfile はそのイメージの「作り方」です。
Docker を触り始めると、イメージ コンテナ Dockerfile が一気に出てきて、ここで頭がこんがらがりやすいです。
特に「pull したのにまだ動いていないの?」あたりは、初心者がかなりつまずきやすいところです。
この記事は概念の言い換えで終わらせず、手元で docker images docker ps -a docker inspect を実際に叩いて、イメージとコンテナの状態・サイズ・レイヤをどこで確認するかを示します。あわせて「コンテナ内の変更がなぜ消えるのか」を docker run → ファイル作成 → exit → 再 run の手順で再現し、commit や ボリューム との関係まで踏み込みます。
Docker 全体の話から見たいなら、Dockerとは?コンテナで何がうれしい?初心者向けに仕組み・メリット・使いどころを解説 から入ると流れがつかみやすいです。
Dockerイメージとは何か(まず images で見てみる)
Docker イメージ は、コンテナを作る元になる読み取り専用のテンプレートです。Docker Docs では、コンテナ実行に必要なファイル・ライブラリ・設定をまとめた標準化パッケージと説明されています。重要なのは、イメージが「複数の読み取り専用レイヤを積み重ねたもの」だという点です。
抽象的な説明よりも、まず手元で確認した方が早いです。何かイメージを取得して一覧を見てみます。
docker images(docker image ls と同じ)の典型的な出力はこうなります。
REPOSITORY TAG IMAGE ID CREATED SIZE
node 22-alpine 3f8a1c9e0b21 2 weeks ago 156MB
node 22 7b2d4e6f9a10 2 weeks ago 1.12GB
nginx stable a1b2c3d4e5f6 3 weeks ago 192MB
ここで見るべきは右端の SIZE です。同じ Node.js でも 22-alpine は 150MB 台、標準の 22 は 1GB を超えます。ベースに何を選ぶかで、後述の pull 時間や CI の重さが大きく変わります。この時点ではまだ何も「動いて」いません。テンプレートをローカルに置いただけです。
レイヤ構造を確認したいときは docker history が使えます。
$ docker history node:22-alpine
IMAGE CREATED CREATED BY SIZE
3f8a1c9e0b21 2 weeks ago CMD ["node"] 0B
<missing> 2 weeks ago RUN apk add --no-cache ... 12.3MB
<missing> 2 weeks ago ENV NODE_VERSION=22.x 0B
...
各行が1レイヤで、RUN や COPY のような変更を伴う命令ごとに積み上がっているのが分かります。CMD や ENV のようなメタデータだけの命令は 0B です。
コンテナとの違い(ps -a で実体を数える)
イメージとコンテナの違いは、一言でいえば次の通りです。
| 観点 | イメージ | コンテナ |
|---|---|---|
| 正体 | 読み取り専用レイヤの積み重ね(テンプレート) | イメージ + 書き込み可能レイヤを起動した実体 |
| 確認コマンド | docker images | docker ps -a |
| 状態 | あるか無いか(取得済みか) | created / running / exited / removed |
| 個数の関係 | 1つ | 同じイメージから何個でも起動できる |
| 変更の永続性 | immutable(変わらない) | 書き込み可能レイヤは rm で消える |
Docker Docs でも、コンテナは a runnable instance of an image と説明されています。イメージが1つでも、そこから起動したコンテナは複数になり得ます。実際に同じイメージから2つ起動して数えてみます。
$ docker run -d --name web1 nginx:stable
$ docker run -d --name web2 nginx:stable
$ docker ps
CONTAINER ID IMAGE COMMAND STATUS NAMES
9f1a2b3c4d5e nginx:stable "/docker-entrypoint.…" Up 3 seconds web2
1a2b3c4d5e6f nginx:stable "/docker-entrypoint.…" Up 8 seconds web1
docker ps は「起動中」だけを出します。停止したコンテナも含めて全部見たいときは -a を付けます。ここを知らないと「コンテナが消えた」と勘違いしがちです。
$ docker ps -a
CONTAINER ID IMAGE COMMAND STATUS NAMES
9f1a2b3c4d5e nginx:stable "/docker-entrypoint.…" Up 1 minute web2
1a2b3c4d5e6f nginx:stable "/docker-entrypoint.…" Exited (0) 5 seconds ago web1
STATUS 列の Up と Exited が、まさに「動いているか止まっているか」です。イメージ側にはこういう状態はありません。
inspect でレイヤと書き込み層をのぞく
「コンテナはイメージ + 書き込み可能レイヤ」という説明は、docker inspect で実際に確認できます。コンテナのストレージ構成を見てみます。
$ docker inspect web1 --format '{{json .GraphDriver}}'
{"Data":{
"LowerDir":"/var/lib/docker/overlay2/abc.../diff:/var/lib/docker/overlay2/def.../diff",
"MergedDir":"/var/lib/docker/overlay2/xyz.../merged",
"UpperDir":"/var/lib/docker/overlay2/xyz.../diff",
"WorkDir":"/var/lib/docker/overlay2/xyz.../work"
},"Name":"overlay2"}
ここがイメージとコンテナの違いの核心です。
LowerDir(イメージ層)
コロン区切りで複数並ぶのが、イメージの読み取り専用レイヤです。同じイメージから起動したコンテナはこれを共有します。だから2個目以降のコンテナはほとんどディスクを食いません。
UpperDir(書き込み可能層)
このコンテナ専用の書き込み層です。コンテナ内でファイルを作る・書き換えると、すべてここに記録されます。コンテナを docker rm するとこのディレクトリごと消えます。
ストレージドライバは overlay2 がほぼ標準で、コピーオンライト方式です。元のイメージファイルを書き換えるときだけ UpperDir にコピーしてから変更するため、イメージ自体(LowerDir)は immutable のまま保たれます。これが「コンテナで何をいじってもイメージは変わらない」の仕組みです。
docker inspect はイメージにも使えます。たとえばエントリポイントやレイヤの digest を確認できます。
$ docker inspect node:22-alpine --format '{{.Config.Cmd}} {{len .RootFS.Layers}} layers'
[node] 4 layers
コンテナ内の変更が消えるのを再現する
ここが今回いちばん手を動かしてほしいところです。「コンテナを止めるとデータが消える」とよく言われますが、正確には「docker rm されると書き込み可能レイヤごと消える」「新しいコンテナには引き継がれない」です。次の手順で自分で再現できます。
実際の流れと出力はこうなります。
$ docker run -it --name tmp1 ubuntu:24.04 bash
root@1a2b3c:/# echo hello > /data.txt
root@1a2b3c:/# cat /data.txt
hello
root@1a2b3c:/# exit
# 同じイメージから「別の新しいコンテナ」を起動
$ docker run --rm ubuntu:24.04 cat /data.txt
cat: /data.txt: No such file or directory
# 一方、さっきの tmp1 を再起動すると残っている
$ docker start -ai tmp1
root@1a2b3c:/# cat /data.txt
hello
ここで分かることが2つあります。第一に、/data.txt はイメージ(ubuntu:24.04)ではなく tmp1 の書き込み可能レイヤに書かれたので、別コンテナには存在しません。第二に、コンテナは exit しただけでは消えず、書き込み層も残るので、同じコンテナを docker start すればデータは復活します。「止めた瞬間に消える」のではなく「rm で消える」のです。
最後に tmp1 を docker rm tmp1 すると、UpperDir ごと削除されて hello は永久に失われます。
現象: 本番コンテナの中で設定ファイルを直接編集して動かしていたら、デプロイ(コンテナ作り直し)で設定が全部消えた。原因: 編集内容が書き込み可能レイヤにしか無く、イメージにも永続ストレージにも残っていなかった。確認手順: docker diff コンテナ名 で「A /etc/app/config.yml」のように書き込み層の差分が出る。回避: 再現したい変更は Dockerfile に戻す、消えてはいけないデータは ボリューム に逃がす。
commit とボリューム:変更を残す2つの道
書き込み層は消えると分かったうえで、「変更を残したい」ときの選択肢が docker commit とボリュームです。性質がまったく違うので使い分けます。
docker commit でイメージに固める
docker commit は、コンテナの書き込み可能レイヤを新しいレイヤとして固め、新しいイメージにします。先ほどの tmp1 をコミットしてみます。
$ docker commit tmp1 my-ubuntu:with-data
sha256:b9c8d7...
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
my-ubuntu with-data b9c8d7e6f5a4 3 seconds ago 78.2MB
ubuntu 24.04 a1b2c3d4e5f6 2 weeks ago 78.1MB
$ docker run --rm my-ubuntu:with-data cat /data.txt
hello
新イメージ my-ubuntu:with-data から起動すると /data.txt が含まれています。ただし commit は「手作業の結果をそのまま焼き込む」ので、何をどう変えたかが記録に残りません。再現性が無く、チームで管理しにくいため、本番向けの変更は commit ではなく Dockerfile に書くのが実務の原則です。commit はトラブル調査用のスナップショット程度に留めるのが無難です。
ボリュームでデータを外に逃がす
データベースやアップロードファイルのように「コンテナとは寿命を分けたいデータ」は、ボリュームに置きます。ボリュームは Docker デーモンが管理する永続ストレージで、コンテナを rm しても残ります。
$ docker volume create appdata
$ docker run -it --rm -v appdata:/data ubuntu:24.04 bash
root@x:/# echo persist > /data/note.txt
root@x:/# exit # --rm なのでコンテナは即削除される
# 別の新コンテナで同じボリュームをマウント
$ docker run --rm -v appdata:/data ubuntu:24.04 cat /data/note.txt
persist
--rm で1つ目のコンテナは消えたのに、ボリュームにあった note.txt は2つ目のコンテナから読めます。書き込み可能レイヤとの決定的な違いです。3つの関係を整理すると次の通りです。
| 保存先 | 寿命 | 再現性 | 向いている用途 |
|---|---|---|---|
| 書き込み可能レイヤ | コンテナと同じ(rm で消える) | なし | 一時ファイル、キャッシュ |
| docker commit(新イメージ) | イメージとして永続 | 低い(手作業の記録なし) | 調査用スナップショット |
| ボリューム | コンテナと独立、明示削除まで残る | データそのもの | DB、アップロード、ログ |
| Dockerfile | ビルドし直せば常に再現 | 高い | アプリ・依存・設定 |
pull と run、Dockerfile の位置づけ
ここまでで実体が掴めたら、用語の関係はすっきり整理できます。
docker pull: レジストリからイメージのレイヤをローカルへ取得する。まだ起動しない。docker run: イメージから書き込み可能レイヤを足してコンテナを作り、起動する。ローカルにイメージが無ければ先に pull もする。- Dockerfile: そのイメージの「作り方」を書くテキスト。
docker buildでイメージになる。
つまり Dockerfile(作り方)→ イメージ(成果物)→ コンテナ(起動した実体) という一方向の流れです。「Dockerfile を書いた=もう動いている」ではないこと、「pull した=もう動いている」ではないことの2点を押さえると、最初の混乱はほぼ消えます。Dockerfile の中身については Dockerfileとは?何を書くもの?最初に読むべき命令を初心者向けに整理 で扱っています。
イメージで見るべきポイント(数値の目安つき)
ベースとサイズ
同じ言語でも alpine 系は数百MB以下、フル版は1GB超になりがちです。SIZE が大きいほど pull・CI・デプロイが重くなるので、まず docker images の SIZE を見る癖をつけます。
作り直す前提で考える
イメージは immutable です。動いたコンテナを手で直して終わりにせず、再現したい変更は Dockerfile 側へ戻します。docker history でレイヤが無駄に増えていないかも確認します。
イメージを小さくする定番は、マルチステージビルドと .dockerignore です。ビルドツールを最終イメージに残さない、node_modules や .git をコンテキストから除外する、といった対応で SIZE を数分の1にできることも珍しくありません。
Dockerイメージとコンテナのよくある質問
Q. イメージ1つから複数のコンテナを起動できますか?
A. はい。docker run を繰り返せば何個でも作れます。読み取り専用のイメージ層(inspect の LowerDir)は共有され、各コンテナは自分の書き込み層だけを持つので、2個目以降はディスクをほとんど消費しません。
Q. コンテナを止めるとデータは消えますか?
A. exit や stop では消えません。書き込み可能レイヤは残り、docker start で復活します。消えるのは docker rm したときです。本記事の再現手順(run→作成→exit→再run)で確認できます。残したいデータは ボリューム に置きます。
Q. コンテナの中で何を変更したか後から確認できますか?
A. docker diff コンテナ名 を使います。A は追加、C は変更、D は削除を表し、書き込み可能レイヤに対する差分が一覧できます。設定を直接いじってしまった事故の切り分けに便利です。
Q. docker commit と docker build はどう使い分けますか?
A. commit はコンテナの現状を焼き込むだけで、何をしたかが記録に残らず再現できません。build は Dockerfile から作るので手順が残り、チームで再現できます。本番向けは原則 build、commit は調査用スナップショットに留めます。
Q. イメージのサイズが大きくなる原因は何ですか?
A. ベースが重い、ビルドツールを含めたまま、キャッシュやログを消していない、複数の RUN でレイヤが蓄積、などが典型です。docker history で重いレイヤを特定し、マルチステージビルドと .dockerignore で削減します。
Q. イメージはどこに保存されていますか?
A. Linux では /var/lib/docker/ 配下(overlay2 ドライバなら overlay2/)に、Docker Desktop では内部のディスクイメージ内に置かれます。docker inspect の GraphDriver でコンテナごとの実パスも確認できます。リモート共有は Docker Hub などのレジストリへ push します。
Q. イメージのタグ(latest、1.0 など)はどう運用すべきですか?
A. latest は中身が動くので本番では避けるのが安全です。ビルドごとに v1.2.3 や Git コミットハッシュを付けると、docker ps の IMAGE 列だけで「今動いている版が何か」を追跡できます。
Q. イメージは ARM(Apple Silicon)と x86 で互換性がありますか?
A. アーキテクチャごとに別バイナリが必要です。docker buildx でマルチアーキテクチャイメージをビルドすれば、linux/amd64 と linux/arm64 を1つのタグで提供できます。docker inspect の Architecture でイメージの対象を確認できます。
まとめ
Docker イメージ は読み取り専用レイヤの積み重ねたテンプレート、コンテナ はそこに書き込み可能レイヤを足して起動した実体です。違いは概念で覚えるより、docker images でサイズを、docker ps -a で状態を、docker inspect で LowerDir / UpperDir を実際に見るのが近道です。
コンテナ内の変更が消えるのは「止めたから」ではなく「書き込み層が rm で消えるから」です。残したいものは commit(再現性は低い)かボリューム(データ向け)か Dockerfile(アプリ向け)で逃がす、と整理できれば、Docker まわりの用語はほぼつながります。
続けて読むなら、Dockerfileとは?何を書くもの?最初に読むべき命令を初心者向けに整理 や Docker Composeとは?複数コンテナをまとめて動かす基本を初心者向けに解説 が相性のよい次の一歩です。
参考リンク
- Docker Docs: What is an image?
- Docker Docs: What is a container?
- Docker Docs: docker image ls
- Docker Docs: Storage overview(writable layer / volumes)
- Docker Docs: docker container inspect