はじめに
Dockerを使い始めたばかりのときは、公開されているイメージを取得し、そのままコンテナとして起動することが多いでしょう。しかし、実際の開発では、自分で作ったファイルをイメージに入れたり、必要なソフトウェアを追加したりしたくなります。
その手順をファイルに記述できるのがDockerfileです。この記事ではNginxを例に、Dockerfileからイメージをビルドし、自分で作ったHTMLページを表示します。さらに、よく使う命令と、CMD・ENTRYPOINT、COPY・ADDの違いを確認します。
Dockerの基本操作やdocker runに慣れていない場合は、先にDocker入門記事を読んでおくと進めやすくなります。
Dockerfileとは
Dockerfileは、Dockerイメージの作成手順を記述するテキストファイルです。例えば、土台となるイメージの指定、ファイルのコピー、ビルド時に実行するコマンドなどを書けます。
Dockerfileを保存しておけば、手作業で加えた変更を思い出す必要がありません。同じファイルを使ってイメージを作り直したり、チーム内で作成手順を共有したりできます。
Dockerfileからイメージを作る操作をビルドと呼びます。作成したイメージは、docker runでコンテナとして実行します。
事前準備
Docker Desktopを使う場合は、アプリを起動しておいてください。ターミナルで次のコマンドを実行し、Docker本体へ接続できることを確認します。
docker info
Dockerの情報が表示されれば準備完了です。接続エラーが出る場合は、Docker Desktopの起動状態を確認してください。
最初に、このハンズオン用のフォルダを作ります。
mkdir dockerfile-practice
cd dockerfile-practice
mkdir nginx-page
cd nginx-page
Nginxを土台にイメージを作る
nginx-pageフォルダに、拡張子を付けずにDockerfileという名前のファイルを作り、次の1行を保存します。
FROM nginx:1.29.5-alpine-slim
FROMは、イメージの土台を指定する命令です。ここではNginxが利用できるイメージを指定しています。
イメージ名の後ろに付くタグ
nginx:1.29.5-alpine-slimのように、イメージ名の後ろに:で付ける識別名をタグと呼びます。この例のタグは1.29.5-alpine-slimで、Nginxのバージョンやイメージの種類を指定しています。
nginxのようにタグを省略すると、nginx:latestとして扱われます。latestはタグ名であり、常に最も新しいバージョンを示すとは限りません。使うバージョンを明確にしたい場合は、対応するタグを指定しましょう。
ただし、バージョン付きのタグでも、公開元が同じタグの参照先を更新することがあります。タグの指定だけで、イメージの内容が永久に固定されるわけではありません。Docker Docs
Dockerfileのあるフォルダで、次のコマンドを実行してください。
docker build -t my-nginx-base .
docker buildはイメージをビルドするコマンドです。-t my-nginx-baseで作成するイメージに名前を付け、最後の.で現在のフォルダをビルドコンテキストに指定します。元になるイメージが手元になければ、ビルド時に取得されます。
my-nginx-baseのようにタグを省略すると、作成するイメージにはlatestタグが付きます。
作成したイメージを起動しましょう。
docker run --rm --name my-nginx-base -p 8080:80 -d my-nginx-base
ブラウザでhttp://localhost:8080/を開き、Nginxの初期ページが表示されれば成功です。-p 8080:80は、PCの8080番ポートとコンテナの80番ポートを接続します。-dはバックグラウンドで実行する指定です。
ここではFROMの1行だけでイメージを作ったため、表示されるページは元のNginxイメージと同じです。次の実験の前にコンテナを停止します。
docker stop my-nginx-base
--rmを付けて起動したので、停止するとこのコンテナは自動的に削除されます。ビルドしたイメージは残ります。
COPYで自分のWebページを追加する
今度は、自分で作ったHTMLをイメージに入れてみましょう。先ほどのnginx-pageフォルダにindex.htmlを作成し、以下の内容を保存します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Dockerfileの練習</title>
</head>
<body>
<h1>Dockerfileで作ったページです</h1>
<p>COPYでHTMLファイルを追加しました。</p>
</body>
</html>
次に、同じフォルダにあるDockerfileを、以下の内容に書き換えます。
FROM nginx:1.29.5-alpine-slim
COPY index.html /usr/share/nginx/html/index.html
この時点でのファイル構成は次のとおりです。
dockerfile-practice/
└── nginx-page/
├── Dockerfile
└── index.html
COPYは、ビルドコンテキストにあるファイルをイメージ内へコピーする命令です。ここではindex.htmlをNginxの公開ディレクトリへコピーし、初期ページを置き換えています。
変更後のイメージを、新しい名前でビルドします。nginx-pageフォルダで実行してください。
docker build -t my-nginx-page .
docker run --rm --name my-nginx-page -p 8080:80 -d my-nginx-page
再びブラウザでhttp://localhost:8080/を開きます。「Dockerfileで作ったページです」と表示されれば、自分のHTMLを含むイメージを作成できています。
ファイルをコピーしたのはビルド時です。後からPC上のindex.htmlを書き換えても、すでに作ったイメージの中身は変わりません。変更を反映するには、コンテナを停止してイメージを再ビルドし、コンテナを起動し直します。
表示を確認したら停止しましょう。
docker stop my-nginx-page
Dockerfileでよく使う命令
今回使ったFROMとCOPY以外にも、Dockerfileには次のような命令があります。
| 命令 | 主な役割 |
|---|---|
FROM | 土台となるイメージを指定する |
WORKDIR | 以降の命令で使う作業ディレクトリを指定する |
COPY | ファイルやディレクトリをイメージにコピーする |
RUN | イメージのビルド中にコマンドを実行する |
CMD | コンテナ起動時のデフォルトのコマンドや引数を指定する |
ENTRYPOINT | コンテナ起動時の実行プログラムを指定する |
Nginxの例ではCMDを記述していません。元のNginxイメージに起動設定が含まれているため、その設定を使ってNginxが起動します。各命令を毎回すべて書く必要はありません。
CMDで起動時の処理を指定する
次は、起動時に短いメッセージを表示するイメージを作ります。現在のnginx-pageフォルダから一つ上に戻り、別の作業フォルダを作成してください。
cd ..
mkdir hello-sample
cd hello-sample
hello-sampleフォルダに、次の内容のDockerfileを作成します。
FROM alpine:latest
CMD ["echo", "Hello, World!"]
CMDは、このイメージからコンテナを起動するときのデフォルトのコマンドを指定します。ビルドして実行してみましょう。
docker build -t my-hello-world .
docker run --rm my-hello-world
次の文字が表示されます。
Hello, World!
ここで作ったものは、Docker公式のhello-worldイメージそのものではありません。Alpineを土台にして、同じようにメッセージを表示する自作のイメージです。イメージ名も区別してmy-hello-worldにしています。
CMDとENTRYPOINTの違い
先ほどのhello-sampleフォルダにあるDockerfileを、次の内容に書き換えます。
FROM alpine:latest
ENTRYPOINT ["echo"]
CMD ["Hello, World!"]
この例では、ENTRYPOINTで実行するプログラムをechoに指定し、CMDでデフォルトの引数を設定しています。先ほどとは別のイメージ名でビルドします。
docker build -t my-entrypoint-sample .
docker run --rm my-entrypoint-sample
実行結果は次のとおりです。
Hello, World!
続いて、docker runのイメージ名の後ろに別の文字列を指定します。
docker run --rm my-entrypoint-sample "Hello, Docker!"
今度は次のように表示されます。
Hello, Docker!
CMDのデフォルト引数がHello, Docker!に置き換わり、ENTRYPOINTで指定したechoに渡されたためです。
ENTRYPOINT自体を変更することもできます。例えば、次のコマンドではechoの代わりに/bin/shを指定します。
docker run --rm --entrypoint /bin/sh my-entrypoint-sample -c "echo ENTRYPOINTを変更しました"
したがって、ENTRYPOINTは「上書きできない」わけではありません。通常のdocker runでイメージ名の後ろに渡した値は引数になりますが、--entrypointを指定すれば実行するプログラムを変更できます。
ADDとCOPYの違い
COPYとADDは、どちらもファイルをイメージへ追加する命令です。先ほどのHTMLファイルのように、通常のファイルをコピーするならCOPYを使うと目的が分かりやすくなります。
COPY index.html /usr/share/nginx/html/index.html
一方、ADDには、ローカルのtarアーカイブを自動展開したり、URLやGitリポジトリから内容を取得したりする機能があります。例えば、ビルドコンテキスト内にあるtarアーカイブを追加する場合は次のように書けます。
ADD app.tar.gz /opt/app/
ただし、すべての圧縮ファイルが自動展開されるわけではありません。自動展開の対象は、ローカルの認識可能なtarアーカイブです。ZIPファイルやURLから取得したtarアーカイブは、この例と同じようには自動展開されません。
通常のコピーはCOPY、ADD固有の機能が必要な場合はADDと使い分けましょう。
Dockerfileを使う利点
Dockerfileを使うと、イメージを作る手順をファイルとして残せます。変更履歴を確認したり、チームで同じ手順を共有したりしやすくなります。
今回もFROMの1行から始め、COPYを追加して自分のHTMLを表示できました。何を追加した結果、イメージがどう変わったかをファイルで確認できます。
設定をコードとして管理する考え方は、Infrastructure as Code(IaC)とも関係しています。ただし、Dockerfileだけでサーバーやネットワークを含むすべてのインフラ構成を管理できるわけではありません。Dockerfileが記述するのは、主にイメージの作成手順です。
まとめ
この記事では、Dockerfileの基本的な書き方から、自分で作ったHTMLをNginxのイメージに含めて表示するところまで試しました。
FROMで土台となるイメージを指定するCOPYで自分のファイルをイメージに追加するdocker buildでイメージを作り、docker runで起動するCMDでデフォルトのコマンドや引数を指定できるENTRYPOINTは--entrypointで変更できる- 普通のファイルのコピーには
COPYを使い、ADDは追加機能が必要なときに使う
Dockerfileで一つのイメージを作れるようになったら、Docker Compose入門で複数のコンテナをまとめて起動する方法も試してみてください。
“Dockerfile入門|基本の書き方から自分のWebページを表示するまで” への3件のフィードバック
[…] Dockerの基本的な使い方を先に確認したい場合は、「Docker入門」をご覧ください。また、独自イメージを作成する方法については「Dockerfile入門」で解説しています。 […]
[…] 次はDockerfileの書き方とビルド方法を学ぶと、自分でイメージを作れるようになります。複数のコンテナをまとめて起動したい場合は、Docker Compose入門へ進んでください。 […]
[…] 自分のファイルを含むイメージの作成方法は、Dockerfile入門で解説しています。 […]