【Docker】初心者のための Dockerfile まとめ
はじめに
普段 Docker を使用して開発を行っていますが、ちょっとした開発程度であれば Docker Hub に公開されている Docker イメージをそのまま利用するだけで十分事足りています。
しかし、少し手の込んだ環境や無駄の無い環境を構築したい場合は、 Dockerfile を使用してオリジナルな Docker イメージを構築する必要があります。
以前 Docker Compose に関する記事 を書きましたが、今回はもう少し Docker に踏み込んで Dockerfile についてまとめてみました。
まとめた内容については Docker Compose 同様、初心者向けとなっています。
何故なら、僕自身が Docker 初心者だからです!!
動作環境
OS | Version |
---|---|
Ubuntu | 20.04 LTS |
※ Windows 10 Pro の Windows Subsystem for Linux (WSL 2) を使用
Docker Products | Version |
---|---|
Docker Desktop for Windows | 4.1.1 |
Docker Engine | 20.10.8 |
Dockerfile まとめ
元も子もないことを書きますが、 Dockerfile に関する詳細は Docker 公式ドキュメントの該当ページをご覧ください。
ここではよく使用する命令を中心に、 Dockerfile の書き方をまとめました。
Dockerfile
# Comments FROM <base_image_name>[:<tag_name>] EXPOSE <port>[/<protocol>] ENV <key> <value> VOLUME <volume_path> WORKDIR <workdir_path> COPY <src> <dest> RUN <command> CMD ["<command>", "<param>"]
一般的に Dockerfile のファイル名は Dockerfile
(※ 拡張子無し) が使用されます。
Dockerfile に記述された各命令は、基本的に上から順に実行されていきます。
また、行頭に #
を記述すると、その一行はコメントとして扱われます。
FROM (ベースイメージ)
FROM <base_image_name>[:<tag_name>] # e.g. FROM python:3.9
イメージ構築時のベースとなる Docker イメージとタグを指定します。
多くの場合、ここで指定したイメージ名とタグ名で Docker Hub に公開されている Docker イメージを取得し、ベースイメージとして使用します。
EXPOSE (公開用ポート番号)
EXPOSE <port>[/<protocol>] # e.g. EXPOSE 5000/tcp
コンテナから公開するポート番号を指定します。
プロトコルの指定は省略可能で、省略した場合は tcp
がデフォルトとして設定されます。
ここでポート番号を指定しただけでは実際に公開はされないため、 docker container run
実行時に -P
や -p
オプションを指定する必要があります。
ENV (環境変数)
ENV <key> <value> ENV <key1>=<value1> [<key2>=<value2> ...] # e.g. ENV FLASK_APP flask_app
コンテナ内で有効な環境変数を指定します。
ここで指定した環境変数は、コンテナ内で有効なだけでなく、 Dockerfile 内の以降の命令においても有効です。
VOLUME (ボリューム)
VOLUME <volume_path> # e.g. VOLUME /flask
ボリュームをマウントするコンテナ内のディレクトリを指定します。
ここで指定したボリュームは、 docker container run
の -v /flask
または --volume /flask
と同様の動きをします。
-v
フラグではホスト側のディレクトリを指定してコンテナ側へマウントすること (Bind Mounts) や、任意の名前を付けたボリュームをマウントすること (Named Volumes) ができますが、 Dockerfile の VOLUME
では Docker が自動的に決定したボリューム名でマウントされます。
また、 Dockerfile の VOLUME
で作成されたボリュームは、 docker container run
コマンドに -rm
フラグを付けてコンテナを起動した場合、コンテナの停止時にコンテナと共に削除されることに注意が必要です。
WORKDIR (作業ディレクトリ)
WORKDIR <workdir_path> # e.g. WORKDIR /flask
作業ディレクトリを指定します。
ここで指定するディレクトリが存在しない場合、新たにディレクトリが作成されます。
WORKDIR
を指定した以降の Dockerfile 内の命令は、ここで指定した作業ディレクトリをカレントディレクトリとして実行されます。
COPY (コピー)
COPY <src> <dest> # e.g. COPY ./source/* /flask/
ホストのファイルやディレクトリを、コンテナ内の指定したパスへコピーします。
相対パスを指定した場合、ホスト側はビルドコンテキスト、コンテナ側は WORKDIR
をカレントディレクトリとしてコピーが実行されます。
コピー元 (ホスト側) のディレクトリやファイルは複数指定、またはワイルドカード (*
など) を使用した場合、コピー先 (コンテナ側) はディレクトリを指定する必要があります。
RUN (コマンド実行 (イメージ構築時))
RUN <command> # e.g. RUN pip install -U pip \ && pip install -r requirements.txt
イメージ構築時に実行するコマンドを指定します。
\
(バックスラッシュ) を使用して、一つの RUN
命令を複数行にわたって記述することが可能です。
コマンドの結果はキャッシュされ、次回以降のイメージ構築時にコマンドに変更が無かった場合、コマンドは実行されずにキャッシュされた結果が使用されます。
そのため、例えば RUN apt update
と一行で記述した場合、そのキャッシュが初回以降のイメージ構築時に使用されるため、古いパッケージ一覧を使用してしまう可能性があります。
キャッシュを使用せずにイメージ構築を行うには、 docker image build
コマンドに --no-cache
フラグを指定する必要があります。
CMD (コマンド実行 (コンテナ開始時))
CMD ["<command>", "<param>"] # e.g. CMD ["/bin/bash"]
コンテナ開始時に実行するコマンドを指定します。
exec 形式 (CMD ["<command>", "<param>"]
) で記述した場合はシェルが実行されないため、コマンドはフルパスで記述する必要があります。
Docker CLI
作成した Dockerfile を基に Docker イメージを構築するには、 docker image build
コマンドを使用します。
image
docker image <command>
Docker イメージを管理するコマンドを実行します。
build (Dockerfile から Docker イメージを構築)
docker image build [options] <build_context>
Options | Description | Example |
---|---|---|
--file , -f |
Dockerfile へのパスを指定 | -f ./path/to/Dockerfile |
--tag , -t |
構築するイメージ名とタグを指定 | -t repository/image_name:tag_name |
--no-cache |
キャッシュを使用せずにイメージを構築 | --no-cache |
指定した Dockerfile とビルドコンテキストから、イメージを構築します。
Dockerfile へのパスを指定しなかった場合、ビルドコンテキストのルートディレクトリに存在する Dockerfile
が利用されます。
container
docker container <command>
Docker コンテナを管理するコマンドを実行します。
run (Docker イメージから Docker コンテナを作成・開始)
docker container run [option] <image_name>[:<tag_name>] [command]
Options | Description | Example |
---|---|---|
--detach , -d |
コンテナをバックグラウンドで実行し、コンテナ ID を出力 | -d |
--interactive , -i |
アタッチされていなくても、 STDIN を受け付ける | -i |
--mount |
コンテナにマウントするボリュームやファイルシステムを指定 | --mount type=bind, source=/var/log/flask, destination=/flask/logs |
--name |
作成するコンテナ名を指定 | --name container_name |
--publish , -p |
ホストに対して公開するコンテナのポート番号と対応させるホストのポート番号を指定 | -p 5000:5000 |
--publish-all , -P |
Dockerfile の EXPOSE 命令で指定されたポート番号を公開し、ホストのランダムポートへ対応させる |
-P |
--rm |
コンテナ停止時にコンテナを削除 | --rm |
--tty , -t |
コンテナに疑似 TTY を割り当て | -t |
--volume , -v |
コンテナにマウントするボリュームやファイルシステムを指定 | -v /var/log/flask:/flask/logs |
指定した Docker イメージから、 Docker コンテナを作成し開始します。
[command]
を指定した場合、 Docker イメージの CMD
命令が上書きされ、開始された Docker コンテナ内で指定したコマンドが実行されます。
-d
フラグを指定しなかった場合はフォアグラウンドで実行され、 Ctrl + C
でコンテナを停止できます。
おわりに
以前書いた Docker Compose の記事で Dockerfile は Docker Compose ほど難しくない的なことを書きましたが、むしろ Dockerfile の方が Docker イメージを無駄なく効率良く作成するために色々と考える必要があり、非常に奥が深いものであると再認識しました…
とはいえ、開発で使用する限りにおいては多少ざっくりと書いても問題になることは少ないと思われるので、本当にシビアに書く必要があるのは本番環境で運用する場合などに限られるのではないかと思います。
仕事関係、特に弊社内ではまだ Docker に関する知見が少なく Docker 人口自体が少ない上、こういうのはインフラエンジニアが担当することが多いこともあって、なかなか実践的な知識を身につける機会がありません…
開発で使用する Dockerfile オレオレベストプラクティスはまだ見付けられていないので、引続き個人で色々と試しながら Docker の知識を深めていきたいと思います。
みんな、もっと Docker 使おう (꒪ཫ꒪; )