Dockerfile の読み方 — Docker Tips
Dockerfile はイメージの組み立て手順を書いたテキストです。実務で頻出するのは FROM(ベース)、RUN(コマンド)、CMD(既定の起動)です。リポジトリの case01/Dockerfile を行ごとに読みます。
参考: Dockerfile reference · Building best practices · Dockerfile overview
目次
- Dockerfile とは
- FROM — ベースイメージ
- RUN — ビルド時に実行するコマンド
- CMD と ENTRYPOINT の違い
- レイヤーという考え方
- case01 の Dockerfile を読む
- ベストプラクティスと apt のクリーンアップ
- うまくいかないとき
Dockerfile とは
Dockerfile は、docker build に読み込ませることで、決まった手順どおりにイメージを組み立てるための 設計図 です。ファイル名は既定で Dockerfile(拡張子なし)で、これを使うと docker build 実行時に追加のオプション指定が不要になります。
主な命令は次の表の通りです。学習用途で頻繁に使うのは FROM / RUN / CMD / COPY / WORKDIR あたりです。
| 命令 | 役割 |
|---|---|
FROM |
ベースになるイメージを指定(Dockerfile の先頭が基本) |
RUN |
ビルド時にコマンドを実行し、結果をイメージに反映 |
COPY / ADD |
ホストのファイルをイメージにコピー |
WORKDIR |
以降の命令の作業ディレクトリを設定 |
ENV |
環境変数を設定 |
EXPOSE |
コンテナが使う想定のポートを記述(実際の公開は docker run -p 側) |
CMD |
コンテナ起動時の既定コマンド |
ENTRYPOINT |
常に実行される起動コマンド(CMD は引数扱いになる) |
USER |
実行ユーザーを指定 |
Dockerfile は 上から順番に 実行されます。FROM より前に置けるのは ARG とコメント・パーサーディレクティブだけです。
FROM — ベースイメージ
FROM ubuntu:24.04FROM は「どの土台の上にイメージを作るか」を決める命令で、Dockerfile の実質的な出発点です。名前:タグ の形式で指定し、タグを省略すると latest になります。
学習用途では公式の軽量な Linux ディストリビューション(ubuntu、debian、alpine など)をベースにすることが多いです。alpine は非常に小さい反面、標準の C ライブラリが musl である等の違いから、Ubuntu 前提の手順と挙動が食い違うことがあります。最初は ubuntu や debian のような一般的な構成で慣れるほうが、余計なトラブルを避けやすいです。
タグを固定しないと、再ビルドしたタイミングで意図せず新しいバージョンのイメージに切り替わることがあります。ubuntu:24.04 のように バージョンを明示 しておくと、再現性が高まります。
RUN — ビルド時に実行するコマンド
RUN apt-get update && \ apt-get install -y --no-install-recommends \ ca-certificates \ curl \ vim \ && rm -rf /var/lib/apt/lists/*RUN は ビルド時に一度だけ 実行され、その結果(インストールされたファイルなど)がイメージの新しい層として保存されます。コンテナを起動するたびに実行されるわけではない点が、後述の CMD との大きな違いです。
複数のコマンドを && でつないでいるのは、1 つの RUN にまとめてレイヤー数を抑える ための一般的な書き方です。RUN を命令ごとに分けると、途中の中間状態(パッケージ一覧の更新結果など)がそれぞれ別レイヤーとして残ってしまい、イメージが不必要に大きくなりやすくなります。
apt-get update と apt-get install は同じ RUN にまとめるのが定石です。別の RUN に分けると、update の結果だけがキャッシュされて install の対象パッケージ一覧が古いままになる、といった不整合が起きることがあります。
CMD と ENTRYPOINT の違い
CMD ["sleep", "infinity"]CMD は コンテナ起動時に実行される既定のコマンド です。docker run <イメージ> <コマンド> のように起動時にコマンドを指定すると、Dockerfile の CMD は上書きされます。
ENTRYPOINT は「常に実行される起動コマンド」で、docker run 側で渡した引数は ENTRYPOINT への 追加の引数 として扱われます(CMD が未指定の ENTRYPOINT と組み合わさる形)。
| 命令 | 上書きのされ方 |
|---|---|
CMD のみ |
docker run <イメージ> <別コマンド> で完全に置き換わる |
ENTRYPOINT のみ |
docker run の引数は ENTRYPOINT への引数として追加される |
ENTRYPOINT + CMD |
CMD が ENTRYPOINT への 既定の引数 になり、docker run 側の引数指定で上書きできる |
学習用のちょっとしたコンテナでは CMD だけで十分なことが多いです。case01 の Dockerfile も CMD ["sleep", "infinity"] のみで、ENTRYPOINT は使っていません。「常にこの実行ファイルを固定で動かし、引数だけ変えたい」というツール的なイメージを作るときに ENTRYPOINT が向いています。
記法の違いにも注意します。
CMD ["sleep", "infinity"]これは exec 形式(JSON 配列)で、シェルを経由せず直接プロセスを起動します。シグナルの伝達(docker stop の SIGTERM など)が素直に効くため、基本的にはこちらを推奨します。
CMD sleep infinityこれは シェル形式 で、内部的に /bin/sh -c "sleep infinity" として実行されます。シェル機能(環境変数展開やパイプ)が必要なときのみ使うのが基本です。
レイヤーという考え方
Dockerfile の命令(主に FROM / RUN / COPY / ADD)は、実行されるたびに 1 枚のレイヤー(層) としてイメージに積み重なります。イメージは、この読み取り専用のレイヤーをスタックしたものです。
レイヤー3: CMD の設定(メタデータのみ、サイズはほぼ0)レイヤー2: RUN apt-get install ... の結果(curl, vim などのファイル)レイヤー1: FROM ubuntu:24.04(ベースの全ファイル)レイヤーには 2 つの重要な性質があります。
- キャッシュされる — 同じ内容の命令であれば、再ビルド時にレイヤーを再利用できる(命令の文字列や
COPY対象が変わるとそこから再実行される) - 差分だけ保存される — レイヤーは変更点だけを持つため、同じベースイメージを使う複数のイメージ間でディスク容量を共有できる
レイヤー数を無闇に増やすとイメージサイズが大きくなりやすいため、「関連するコマンドは 1 つの RUN にまとめる」「不要なキャッシュファイルは同じ RUN 内で削除する」という書き方が推奨されます。case01 の RUN 命令が apt-get update && apt-get install ... && rm -rf /var/lib/apt/lists/* を 1 行にまとめているのは、まさにこの理由によるものです。
case01 の Dockerfile を読む
リポジトリの case01/Dockerfile は次の内容です。
FROM ubuntu:24.04RUN apt-get update && \ apt-get install -y --no-install-recommends \ ca-certificates \ curl \ vim \ && rm -rf /var/lib/apt/lists/*CMD ["sleep", "infinity"]行ごとの意味は次のとおりです。
| 行 | 意味 |
|---|---|
FROM ubuntu:24.04 |
Ubuntu 24.04 をベースにする |
apt-get update |
パッケージ一覧を最新化 |
apt-get install -y --no-install-recommends ... |
ca-certificates / curl / vim を確認なし(-y)でインストール。推奨パッケージは入れない |
rm -rf /var/lib/apt/lists/* |
パッケージ一覧のキャッシュを削除してイメージを軽量化 |
CMD ["sleep", "infinity"] |
起動後、何もせず動き続ける(学習用に常駐させる) |
ca-certificates は HTTPS 通信(curl で外部サイトにアクセスするなど)で証明書検証に必要なパッケージです。curl 自体を入れても証明書ストアが無いと、HTTPS 先へのアクセスで検証エラーになることがあるため、セットで入れておくのが安全です。
このイメージをビルドして起動すると、sleep infinity によってコンテナはずっと Up の状態を保ちます。そこへ docker exec -it <コンテナ名> bash で入り、curl や vim を使って中身を触る、という学習用の使い方が想定された構成です。case02 はこの Dockerfile を docker-compose.yml からビルドするだけの違いで、内容自体は同一です。
ベストプラクティスと apt のクリーンアップ
Ubuntu / Debian 系のイメージで apt-get を使う場合、いくつかの定番の書き方があります。
--no-install-recommends を付ける
RUN apt-get install -y --no-install-recommends curlapt-get install は既定で「推奨パッケージ」も一緒にインストールします。コンテナは GUI や補助ツールが不要な場合が多く、--no-install-recommends を付けることで、本当に必要なパッケージだけに絞れます。イメージサイズの削減とビルド時間の短縮に直結します。
パッケージ一覧のキャッシュを削除する
RUN apt-get update && apt-get install -y --no-install-recommends curl \ && rm -rf /var/lib/apt/lists/*apt-get update はパッケージ一覧のキャッシュを /var/lib/apt/lists/ 以下にダウンロードします。インストール後はこのキャッシュが不要になるため、同じ RUN 内で 削除します。別の RUN に分けて削除しても、レイヤーとしては前のレイヤーにキャッシュが残ってしまうため、必ず同一の RUN にまとめる必要があります。
-y で確認プロンプトを止める
対話的な確認(Do you want to continue? [Y/n])はビルドを止めてしまうため、-y を付けて自動承認します。
まとめての比較表
| 書き方 | 効果 |
|---|---|
apt-get install -y |
確認プロンプトを止める |
--no-install-recommends |
不要な推奨パッケージを入れない |
同一 RUN 内で update と install を実行 |
キャッシュのずれを防ぐ |
同一 RUN 内で rm -rf /var/lib/apt/lists/* |
不要なキャッシュファイルを残さずイメージを軽量化 |
これらは Debian / Ubuntu 系イメージの Dockerfile で共通して使われる定石で、case01 の書き方もこの慣習に沿っています。
うまくいかないとき
| 症状 | 原因 | 対処 |
|---|---|---|
ビルドが Unable to locate package で失敗 |
apt-get update を実行していない、または別レイヤーで実行 |
update と install を同じ RUN にまとめる |
| ビルド中に対話的な入力待ちで止まる | apt-get install に -y が無い |
-y を付けて自動承認する |
| イメージサイズが想定より大きい | 推奨パッケージやキャッシュが残っている | --no-install-recommends と rm -rf /var/lib/apt/lists/* を追加 |
docker run してもすぐコンテナが終了する |
CMD が常駐しないコマンドになっている |
sleep infinity などの常駐用コマンドを検討 |
docker run <イメージ> <コマンド> を渡しても無視される |
ENTRYPOINT が設定され、CMD が既定引数として吸収されている |
ENTRYPOINT の仕様を確認し、意図した使い方かチェック |
curl で HTTPS 先に繋がらない |
ca-certificates が入っていない |
Dockerfile に ca-certificates を追加してビルドし直す |
| 変更した Dockerfile の内容が反映されない | 古いイメージ・コンテナを使い続けている | docker build を再実行し、コンテナも作り直す |
Dockerfile は短い記述量でも、レイヤーの積み方ひとつでイメージの大きさや再現性が変わります。まずは FROM / RUN / CMD の 3 命令だけで動かし、必要に応じて COPY や WORKDIR を足していくのが学習の進め方として無理がありません。