AI can fly !!

AI がやりたい Web エンジニアのアウトプット (AI の知識は無い)

【Docker】初心者のための Dockerfile まとめ

docker-logo

はじめに

普段 Docker を使用して開発を行っていますが、ちょっとした開発程度であれば Docker Hub に公開されている Docker イメージをそのまま利用するだけで十分事足りています。

しかし、少し手の込んだ環境や無駄の無い環境を構築したい場合は、 Dockerfile を使用してオリジナルな Docker イメージを構築する必要があります。

以前 Docker Compose に関する記事 を書きましたが、今回はもう少し Docker に踏み込んで Dockerfile についてまとめてみました。

ai-can-fly.hateblo.jp

まとめた内容については 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 公式ドキュメントの該当ページをご覧ください。

docs.docker.com

ここではよく使用する命令を中心に、 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 使おう (꒪ཫ꒪; )