はじめに
前回の記事では、nginx
のコンテナイメージを使用してdocker container run
などのコマンドの使用例を説明しました。ここで使用したnginx
のイメージは、公式に配布されているイメージでしたが、Dockerfileを用いると自分好みにカスタマイズしたコンテナイメージを作成できるようになります。コンテナを使っていくうえで、Dockerfileはほぼ必ず使うものになるので、この記事でDockerfileの使い方とイメージをつかんでいただければと思います。
Dockerfileとはどんなものか
Dockerfileは、dockerコンテナのイメージを作成するための設定ファイルです。アプリケーションに必要なパッケージをインストールしたり、必要なファイルをコピーしたりと、コンテナを作成して動作させるために必要な操作を記述します。
例えば、nginx
を使って自分で作ったhtmlを公開したい場合、下記のイメージのように、自分で作ったhtmlファイルをnginxのベースイメージに加える必要があります。
そのため、Dockerfileには以下のようなことを記述します。
例
nginx
のイメージをベースに、自分で作ったhtmlファイルを表示するようなイメージを作成する例をお見せしたいと思います。nginxでは、デフォルトの状態では以下のようなページを表示するウェブサーバが起動します。
起動した際に表示するページを、デフォルトのページから自分が作成したページに置き換えるためのDockerfileを作成してみます。
以下のようなページを表示することを目標にします。
用意する必要があるのは、
- カスタムイメージのためのDockerfile
- 表示するhtmlファイル
の2点です。下記の様なディレクトリ構成でこれらのファイルを配置します。
.
├── Dockerfile
└── html
└── index.html
Dockerfileは、概要で説明したような手順と同様で、以下のように記述します。
# ベースイメージとして公式のnginxイメージを使用
FROM nginx:latest
# カスタムのindex.htmlをnginxのデフォルトのドキュメントルートにコピー
COPY html/index.html /usr/share/nginx/html/index.html
# デフォルトのnginxコマンドを実行
CMD ["nginx", "-g", "daemon off;"]
htmlファイルは、サンプルとして以下のようなものを使用します(このファイルの内容は本記事の本筋とは関係ありません)。
<!DOCTYPE html>
<html>
<head>
<title>Custom Nginx Page</title>
<style>
body {
width: 50em;
margin: 0 auto;
font-family: Arial, sans-serif;
background-color: #f4f4f4;
}
header {
background-color: #333;
color: white;
padding: 10px 0;
text-align: center;
}
nav ul {
list-style: none;
padding: 0;
}
nav ul li {
display: inline;
margin: 0 10px;
}
nav ul li a {
color: white;
text-decoration: none;
}
.content {
margin: 20px 0;
}
footer {
text-align: center;
padding: 10px 0;
background-color: #333;
color: white;
}
</style>
</head>
<body>
<header>
<h1>Welcome to My Custom Nginx Page!</h1>
<nav>
<ul>
<li><a href="#">Home</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Contact</a></li>
</ul>
</nav>
</header>
<div class="content">
<p>If you see this page, the nginx web server is successfully installed and working with a custom index.html file.
</p>
</div>
<footer>
<p><em>Thank you for using nginx.</em></p>
</footer>
</body>
</html>
Dockerfileのあるディレクトリに移動し、以下のコマンドでコンテナイメージをビルドします。
docker build -t custom-nginx .
-t
オプションを指定すると、作成するコンテナイメージの名前を指定することができます。この場合はcustom-nginx
という名前を指定しています。
docker image ls
コマンドで作成したコンテナイメージを確認します。
$ docker image ls
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
REPOSITORY TAG IMAGE ID CREATED SIZE
localhost/custom-nginx latest 1c4d9598f126 37 seconds ago 192 MB
docker.io/library/nginx latest 4f67c83422ec 11 days ago 192 MB
localhost/custom-nginx
が、いま作成したカスタムのnginxイメージで、docker.io/library/nginx
が、カスタムのnginxイメージを作成するうえでベースとして使用したコンテナイメージになります。
ではこのコンテナイメージから、docker container run
コマンドでコンテナを起動します。
$ docker container run -d -p 10000:80 localhost/custom-nginx:latest
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
4586930a4c616e5afcc4f978b243bcf6aa06f8dd7bb1d0b14d1542d08e1e765d
docker container ls
コマンドで、コンテナの起動を確認します。
$ docker container ls
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4586930a4c61 localhost/custom-nginx:latest nginx -g daemon o... 41 seconds ago Up 41 seconds 0.0.0.0:10000->80/tcp nostalgic_kepler
無事に起動しているので、ブラウザでhttp://localhost:10000
にアクセスすると、カスタムのページが表示されます。
Dockerfileでよく使うコマンド
よく使うコマンドは以下のようなものになります。
コマンド | 説明 | 使用例 |
---|---|---|
FROM |
ベースとなるイメージを指定します。 | FROM ubuntu:20.04 |
RUN |
イメージのビルド時に実行するコマンドを指定します。 | RUN apt-get update && apt-get install -y nginx |
CMD |
コンテナ起動時に実行するデフォルトのコマンドを指定します。 | CMD ["nginx", "-g", "daemon off;"] |
ENTRYPOINT |
コンテナ起動時に実行するメインのコマンドを指定します。 | ENTRYPOINT ["docker-entrypoint.sh"] |
COPY |
ホストシステムからコンテナにファイルまたはディレクトリをコピーします。 | COPY myapp/ /usr/src/myapp/ |
ADD |
COPY と似ていますが、URLからのダウンロードやアーカイブの自動解凍もサポートします。 |
ADD myapp.tar.gz /usr/src/myapp/ |
ENV |
環境変数を設定します。ビルド時と実行時に使用されます。 | ENV APP_ENV=production |
EXPOSE |
コンテナがリッスンするネットワークポートを指定します。メタデータの一種で、実際に何か操作を行うわけではなく、コンテナがリッスンするポートを分かりやすくしています。 | EXPOSE 80 |
WORKDIR |
作業ディレクトリを設定します。この命令以降の、RUN コマンド等のコマンドが実行されるディレクトリを指定します。 |
WORKDIR /usr/src/myapp |
USER |
コンテナ内でコマンドを実行するユーザーを指定します。 | USER appuser |
VOLUME |
ホストとコンテナ間でマウントされるボリュームを指定します。 | VOLUME /var/lib/mysql |
ARG |
ビルド時に渡される引数を指定します。 | ARG build_env=production |
LABEL |
メタデータをイメージに追加します。バージョン情報やメンテナンス情報を含めるのに便利です。 | LABEL version="1.0" description="A simple example" |
ONBUILD |
このイメージが他のイメージのベースとして使用されたときに実行するコマンドを指定します。 | ONBUILD ADD . /app/src |
Dockerfileの使用例
上記で紹介したコマンドを使って、もう少し発展的なDockerfileを作成してみます。次に作成するイメージは、ubuntu:20.04
をベースに、nginx
をインストールし、カスタムhtmlファイルを表示します。また、環境変数の設定、コンテナイメージにラベルを追加、コンテナが使用するポートの明示などを行います。
FROM ubuntu:20.04
# メタデータの追加
LABEL maintainer="yourname@example.com" version="1.0"
# 環境変数の設定
ENV APP_ENV=production
# 作業ディレクトリの設定
WORKDIR /usr/src/app
# パッケージのインストール
RUN apt-get update && apt-get install -y nginx
# アプリケーションコードのコピー
COPY html/ /usr/share/nginx/html/
# ポートの公開
EXPOSE 80
# デフォルトのコマンド
CMD ["nginx", "-g", "daemon off;"]
docker image build -t custom-nginx-from-ubuntu .
コマンドを実行して、上記のDockerfileからコンテナイメージをビルドします。
LABELでイメージをフィルタリング
LABEL
コマンドでラベル付しているので、フィルタリングすることができます。ここでは、docker image ls
コマンド実行時にフィルタリングする方法を紹介します。
まず、いまあるイメージをdocker image ls
コマンドで確認します。
$ docker image ls
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
REPOSITORY TAG IMAGE ID CREATED SIZE
localhost/custom-nginx-from-ubuntu latest 49badc0cc0e3 2 minutes ago 188 MB
docker.io/library/ubuntu 20.04 5f5250218d28 6 days ago 75.2 MB
localhost/custom-nginx-from-ubuntu
と、docker.io/library/ubuntu
の二つのイメージがあることが分かります。ラベルでフィルターして、localhost/custom-nginx-from-ubuntu
のみを表示してみます。
フィルタリングする場合、--filter
オプションを使います。今回作成したカスタムイメージには、maintainer
というラベルをキーとして付与したので、このキーを持つイメージを絞り込みます。docker image ls --filter "label=maintainer"
コマンドを使います。
$ docker image ls --filter "label=maintainer"
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
REPOSITORY TAG IMAGE ID CREATED SIZE
localhost/custom-nginx-from-ubuntu latest 49badc0cc0e3 2 minutes ago 188 MB
このように、該当のラベルを持つイメージだけが表示されます。
また、ラベルはKey-Valueの形で指定しているので、Key-Valueの形でフィルタリングすることもできます。LABEL
を同じKey、違うValueで作成したDockerfileから、custom-nginx-from-ubuntu2
というイメージをビルドしました。ラベルがmaintainer
のキーを持つものでイメージを絞り込むと、以下のように二つのイメージが絞り込まれます。
$ docker image ls --filter "label=maintainer"
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
REPOSITORY TAG IMAGE ID CREATED SIZE
localhost/custom-nginx-from-ubuntu2 latest 3fd56fc54470 2 minutes ago 188 MB
localhost/custom-nginx-from-ubuntu latest 49badc0cc0e3 9 minutes ago 188 MB
ここから、Valueの値をもとに絞り込んでみます。maintainer=yourname@example.com
のものを絞り込むと、
$ docker image ls --filter "label=maintainer=yourname@example.com"
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
REPOSITORY TAG IMAGE ID CREATED SIZE
localhost/custom-nginx-from-ubuntu latest 49badc0cc0e3 11 minutes ago 188 MB
このように最初に作成したほうのカスタムイメージが表示されます。また、maintainer=yourname2@example.com
で絞り込むと、
$ docker image ls --filter "label=maintainer=yourname2@example.com"
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
REPOSITORY TAG IMAGE ID CREATED SIZE
localhost/custom-nginx-from-ubuntu2 latest 3fd56fc54470 4 minutes ago 188 MB
このように後に作成したイメージが表示されます。
環境変数を確認
稼働しているコンテナに接続して、環境変数が正しく設定されているか確認してみます。
まずはコンテナをdocker run
コマンドで実行します。
$ docker run -d -p 10000:80 custon-nginx-from-ubuntu
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
a90bcbc31d3567e658a20ac2f84060cfb4fe0dcdefbe8d4d5eb72ff576a44a1e
コンテナに入る(コンテナで新しいシェルセッションを開始する)には、docker exec
コマンドを-it
オプションを合わせて実行します。今回起動したコンテナのIDは、docker run
コマンドの時の出力からa90~
であることが分かったので、a
をコンテナIDとして指定しています。また、シェルを実行したいため、実行するコマンドには/bin/bash
を指定しています。
$ docker exec -it a /bin/bash
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
root@a90bcbc31d35:/usr/src/app#
それでは、Dockerfile内で指定した環境変数を確認してみます。
root@a90bcbc31d35:/usr/src/app# echo $APP_ENV
production
このように、正しく設定されていることが分かります。
ENTRYPOINTとCMDの違い
ぱっと見よく似たENTRYPOINT
とCMD
ですが、当然異なる使い道で使われます。よくあるのは、
-
ENTRYPOINT
でデフォルトで実行するコマンドを指定する -
CMD
でデフォルト引数を指定する
という使われ方があります。例えば、
FROM ubuntu:20.04
ENTRYPOINT ["echo"]
CMD ["hello"]
のようなDockerfileをビルドすると、デフォルトでは以下の様に実行されます。
$ docker build -t entry-cmd .
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
STEP 1/3: FROM ubuntu:20.04
STEP 2/3: ENTRYPOINT ["echo"]
--> 52812f7b33e5
STEP 3/3: CMD ["hello"]
COMMIT entry-cmd
--> 021f720c1602
Successfully tagged localhost/entry-cmd:latest
021f720c1602a360bcedb797e22350746198086982311c678e4065e021ffd6fd
$ docker run entry-cmd
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
hello # CMDで指定したデフォルト引数のhelloが出力される
docker run
のときの引数として異なる値を渡すと、以下のようになります。
$ docker run entry-cmd world
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
world
ENTRYPOINT
で指定されたコマンドは、docker run
時に--entrypoint
オプションをつけて上書きしない限り必ず実行されます。CMD
で指定されたものは、docker run
時に引数として与えることでそのまま上書きされます。そのため、このような使いわけを行います。
ちなみに、下記ようにENTRYPOINT
とCMD
を入れ替えて実行することはできません(ENTRYPOINT
で指定されたものがコマンドとして認識されるため)。
FROM ubuntu:20.04
CMD ["echo"]
ENTRYPOINT ["hello"]
$ docker run entry-cmd
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.
Error: runc: runc create failed: unable to start container process: exec: "hello": executable file not found in $PATH: OCI runtime attempted to invoke a command that was not found
まとめ
以上、Dockerfileの基本的な使い方でした。次回はシンプルなウェブアプリを実装し、Dockerで実行する場合とそうでない場合を比較したいと思います。
次の記事