Dockerは触ったことあるけど、Dockerfileを見ると「急によくわからない」…となる人向けの記事です。
今日は Dockerfileを最小構成で書いて、自分のイメージを docker build で作り、docker run で動かすところまでを手でやります。ついでに、2回目のビルドで キャッシュ(CACHED) も体感します。
TL;DR
- Dockerfileの最低限(
FROM/COPY)が読める -
docker build→docker runの流れで「自作イメージ」を起動できる - “Dockerfileは上からレイヤーが積まれる” と “CACHEDの意味” が腹落ちする
これから何をするのか(全体像)
まずは、これを頭に置いてからコマンドに入ります。
(ローカルPC)
├─ Dockerfile … イメージの設計図(上から順に命令)
└─ index.html … Nginxで配るHTML(これを差し替える)
│ docker build
▼
[Docker Image] … “テンプレ”みたいなもの(レイヤーの集合)
│ docker run
▼
[Container] … 実際に動く実体(プロセス)
│ -p 8080:80
▼
http://localhost:8080 で表示確認
この後やることはシンプルで、
-
index.htmlを用意 -
Dockerfileで「Nginxの中の既定ページを差し替える」 -
docker buildでイメージを作る -
docker runで起動してブラウザで確認 - HTMLを1文字変えて もう一回 build してキャッシュを観察
という流れです。
前提(環境)
- Dockerが動くこと(Docker Desktopなど)
- 端末で
docker --versionが通ること
なぜDockerfileが必要?(docker run だけじゃダメ?)
結論から言うと、Dockerfileは「ちゃんと動いた」を 再現できる形で残すために必要です。
docker run は “起動” には強いんですが、次みたいな瞬間に困りがちです。
-
コンテナの中で手作業した変更が、次回起動で消える
例:docker execでファイルを編集した/パッケージを入れた → コンテナを作り直したら元通り -
同じ環境をもう一回作ろうとすると、記憶と勘に頼る
「あれ、どのコマンド打ったっけ?」が起きる -
チームや未来の自分に渡せない
“動いた”の共有が口頭やスクショになる
Dockerfileがあると、これが一気に楽になります。
- 再現性:誰がいつ作っても同じイメージを作れる(「動く状態」を保存できる)
- 共有:コードとしてレビューできる/チームに配れる/CIで自動ビルドできる
- 変更に強い:差分がDockerfileに残るから、原因調査もしやすい
今回のハンズオンは「Dockerfileがあると何が嬉しいか」を最短で体感するために、Nginxのページ差し替えを題材にします。
ハンズオン:最小Dockerfileで “動いた!” を作る
1) 作業フォルダを作る
mkdir dockerfile-hands-on
cd dockerfile-hands-on
完成形はこういうファイル構成になります。
dockerfile-hands-on/
├─ Dockerfile
└─ index.html
2) 配信するHTMLを用意する(index.html)
Nginxで配るページを自分のHTMLにします。
cat > index.html << 'EOF'
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<title>Dockerfile Hands-on</title>
</head>
<body>
<h1>Hello from my Docker image!</h1>
<p>index.html を COPY で差し替えました。</p>
</body>
</html>
EOF
3) Dockerfileを書く(今回は2命令だけ)
ここが今日の主役です。やることは「Nginxを土台にして、既定ページを差し替える」だけ。
cat > Dockerfile << 'EOF'
FROM nginx:1.27-alpine
COPY index.html /usr/share/nginx/html/index.html
EOF
ここでいったん意味を確認
-
FROM ...:ベースとなるイメージを選ぶ(ここではNginx) -
COPY ...:ローカルのファイルをイメージ内にコピーする- Nginxのデフォルト配信先は
/usr/share/nginx/html/なので、そこへ上書きしています
- Nginxのデフォルト配信先は
※ nginx:1.27-alpine は例です。ポイントは タグを固定すること(後で“罠”として触れます)。
4) docker build でイメージを作る
ここで “Dockerfileがイメージに変わる” 体験をします。
Dockerfile(設計図) + index.html(素材)
│
└─ docker build
│
▼
Docker Image(完成品)
実行:
docker build -t my-nginx:1 .
-t my-nginx:1 は「このイメージに名前(タグ)を付ける」オプションです。
最後の . は ビルド対象(Dockerfileやindex.htmlが置いてある場所) を指します。
ビルドログは環境で多少違いますが、雰囲気はこんな感じです。
-
FROM ...のステップ -
COPY ...のステップ - 最後に
Successfully built .../Successfully tagged ...
5) docker run で起動してブラウザで見る
次は “イメージを実体(コンテナ)として動かす” パートです。
Docker Image(テンプレ)
│ docker run
▼
Container(動いてる実体)───(ポート転送)──▶ localhost:8080
実行:
docker run --name my-nginx -p 8080:80 my-nginx:1
-
--name my-nginx:コンテナに名前を付ける(後で触りやすい) -
-p 8080:80:PCの8080番 → コンテナの80番(Nginx)へつなぐ
ブラウザで開きます:
http://localhost:8080
さっきの Hello from my Docker image! が表示されていれば成功です。
(止めるときは、このターミナルで Ctrl + C)
寄り道:2回目の build で「キャッシュ」を体感する
ここからがA+Bの“混ぜどころ”です。
やることは index.htmlを1文字変えて、もう一回 docker build するだけ。
6) index.html をちょっとだけ変更する
例えば見出しを変えます。
sed -i '' 's/Hello from my Docker image!/Hello again (cache test)!/' index.html 2>/dev/null || \
sed -i 's/Hello from my Docker image!/Hello again (cache test)!/' index.html
(macの sed とLinuxの sed の差を吸収するために少しだけ工夫してます。気にしなくてOKです)
7) もう一度 docker build する(ログの “CACHED” を探す)
docker build -t my-nginx:1 .
ここで注目してほしいのが、ビルドログです。
多くの環境では、どこかに CACHED が出ます。
なぜこうなる?(超ざっくり)
Dockerfileは 上から順に実行されて、各行ごとに“レイヤー”として積まれます。
Layer 1: FROM nginx:...
Layer 2: COPY index.html ...
今回、変えたのは index.html なので、
-
FROMは変わらない → そのレイヤーは再利用(キャッシュ) -
COPYはファイルの中身が変わった → そのレイヤーは作り直し
という挙動になります。
「Dockerfileは上から読むだけで、何が変わるとどこが作り直されるか想像できる」
ここが分かると、Dockerfileが一気に怖くなくなります。
なぜキャッシュがあると嬉しい?(“待ち時間” を減らすため)
Dockerを触っていると、意外と多いのがこのループです。
少し変える → build → run → 確認 → また少し変える
このループで毎回フルでビルドが走ると、待ち時間が地味に効いてきます。
特に、開発や学習では “小さな変更を何回も試す” ので、ビルドが遅いとテンポが落ちて理解も進みにくい。
Dockerのキャッシュが効くと、変わっていない手順(レイヤー)は再利用されて、変わった部分だけ作り直します。
- 嬉しいこと1:反復が速くなる(小さく変えてすぐ試せる)
- 嬉しいこと2:CIでも速くなる(毎回ゼロからやり直しになりにくい)
-
嬉しいこと3:Dockerfileを読むコツになる
「どこが変わると、どこから作り直しになるか」を想像できるようになる
今回の例だと、index.html だけ変えるので、FROM は再利用されて、COPY だけが作り直される…という挙動になります。
レイヤーを“覗く”:docker image history で確認する
見える化するとさらに安心できます。
docker image history my-nginx:1
出力は環境依存ですが、だいたい
-
nginxを土台にしたレイヤー -
COPY index.html ...に対応するレイヤー
のように並びます。
ここでやりたいことは「細部の理解」ではなく、感覚として
- Dockerfileの1行が、何かしらの“差分(レイヤー)”を生む
- その差分が積み重なってイメージになる
と掴むことです。
初心者が踏みがちな罠(最小だけ)
罠1:latest に頼ると、ある日突然ズレる
FROM nginx:latest は便利ですが、いつの間にか中身が変わる可能性があります。
学習中ほど「昨日動いたのに今日は動かない」を避けたいので、まずは タグ固定が安全です。
罠2:docker build の最後の . を雑にすると事故る
docker build -t ... . の . は ビルドコンテキスト(Dockerに渡す範囲)です。
巨大なフォルダでやると不要ファイルまで含まれて遅くなったり、意図せず秘密情報を含める事故が起きます。
今回みたいに、専用フォルダを切るのが正解です。
罠3:ポートが開かない(-p を忘れる)
Nginxはコンテナ内で80番で待っています。
PCから見るには -p 8080:80 のようなポート転送が必要です。
お片付け(任意)
コンテナを消したいとき:
docker rm -f my-nginx
イメージを消したいとき:
docker rmi my-nginx:1
まとめ(before → after)
- Dockerfileが「呪文」から「上から読める設計図」になった
-
docker buildでイメージを作り、docker runで動かせた - 2回目のビルドで CACHED を見て、「どの行が作り直されるか」をレイヤー視点で説明できるようになった
Dockerfileは、覚える命令を増やすより先に “レイヤーとして読む” だけで理解が進みます。今日の2行(FROM / COPY)が読めたなら、もう怖さはだいぶ減ってるはずです。