Docker

Dockerfile の読み方

10

Dockerfile の読み方 — Docker Tips

Dockerfile はイメージの組み立て手順を書いたテキストです。実務で頻出するのは FROM(ベース)、RUN(コマンド)、CMD(既定の起動)です。リポジトリの case01/Dockerfile を行ごとに読みます。

参考: Dockerfile reference · Building best practices · Dockerfile overview


目次

  1. Dockerfile とは
  2. FROM — ベースイメージ
  3. RUN — ビルド時に実行するコマンド
  4. CMD と ENTRYPOINT の違い
  5. レイヤーという考え方
  6. case01 の Dockerfile を読む
  7. ベストプラクティスと apt のクリーンアップ
  8. うまくいかないとき

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.04

FROM は「どの土台の上にイメージを作るか」を決める命令で、Dockerfile の実質的な出発点です。名前:タグ の形式で指定し、タグを省略すると latest になります。

学習用途では公式の軽量な Linux ディストリビューション(ubuntudebianalpine など)をベースにすることが多いです。alpine は非常に小さい反面、標準の C ライブラリが musl である等の違いから、Ubuntu 前提の手順と挙動が食い違うことがあります。最初は ubuntudebian のような一般的な構成で慣れるほうが、余計なトラブルを避けやすいです。

タグを固定しないと、再ビルドしたタイミングで意図せず新しいバージョンのイメージに切り替わることがあります。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 updateapt-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 CMDENTRYPOINT への 既定の引数 になり、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 つの重要な性質があります。

  1. キャッシュされる — 同じ内容の命令であれば、再ビルド時にレイヤーを再利用できる(命令の文字列や COPY 対象が変わるとそこから再実行される)
  2. 差分だけ保存される — レイヤーは変更点だけを持つため、同じベースイメージを使う複数のイメージ間でディスク容量を共有できる

レイヤー数を無闇に増やすとイメージサイズが大きくなりやすいため、「関連するコマンドは 1 つの RUN にまとめる」「不要なキャッシュファイルは同じ RUN 内で削除する」という書き方が推奨されます。case01RUN 命令が 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 で入り、curlvim を使って中身を触る、という学習用の使い方が想定された構成です。case02 はこの Dockerfile を docker-compose.yml からビルドするだけの違いで、内容自体は同一です。


ベストプラクティスと apt のクリーンアップ

Ubuntu / Debian 系のイメージで apt-get を使う場合、いくつかの定番の書き方があります。

--no-install-recommends を付ける

RUN apt-get install -y --no-install-recommends curl

apt-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 内で updateinstall を実行 キャッシュのずれを防ぐ
同一 RUN 内で rm -rf /var/lib/apt/lists/* 不要なキャッシュファイルを残さずイメージを軽量化

これらは Debian / Ubuntu 系イメージの Dockerfile で共通して使われる定石で、case01 の書き方もこの慣習に沿っています。


うまくいかないとき

症状 原因 対処
ビルドが Unable to locate package で失敗 apt-get update を実行していない、または別レイヤーで実行 updateinstall を同じ RUN にまとめる
ビルド中に対話的な入力待ちで止まる apt-get install-y が無い -y を付けて自動承認する
イメージサイズが想定より大きい 推奨パッケージやキャッシュが残っている --no-install-recommendsrm -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 命令だけで動かし、必要に応じて COPYWORKDIR を足していくのが学習の進め方として無理がありません。

シェア