😎

Dockerfileのリファレンスまとめ(自分用)

2023/04/27に公開約9,900字

概要

Dockerを使用していたが、そもそもDockerfileについてしっかりと理解していなかったので、ここで一旦 Dockerfile についてまとめてみます。Dockerfile の命令コマンドについて簡単にまとめてある程度理解をしようと思います。

まとめる内容は以下のリファレンスを参考にしているものです。
https://docs.docker.com/engine/reference/builder/

簡易的なまとめ

命令 説明
FROM ベースイメージを作成する
RUN シェルで実行するコマンドをここで命令する(イメージ)
CMD RUNと同様で、制約(最後のCMD命令のみ実行される)がある
LABEL イメージにメタデータを付与する
EXOPOSE 解放したいポートを指定する(実際は解放されない)
EMV 環境変数
ADD 追加したいファイル、ディレクトリ、リモートファイルの URLを指定すると、イメージのファイルシステム上に追加します
COPY 追加したいファイル、ディレクトリからイメージのファイルシステム上に追加します
ENTRYPOINT CMDと同様で、一つのENTRYPOINTを実行する
WORKDIR コンテナが作成された時に作業するディレクトリを指定する
ARG 変数として値を代入してDockerfile内で使用することが可能です

命令コマンド

FROM

FROM命令は、新しいビルドステージを初期化し、後続の命令のためのベースイメージを設定します。そのため、有効なDockerfileはFROM命令で始まる必要がある。(例外として、ARG命令は、FROM命令より前に記載することが可能)

# 説明
1 ARGはDockerfileでFROMに先行することができる唯一の命令です
2 FROM命令は、複数のイメージを作成したり、あるビルドステージを別のビルドステージの依存関係として使用するために、1つのDockerfile内で複数回現れることある。新しいFROM命令(2つ目以降)の前に、コミットされ、出力された最後のイメージIDをメモしておくだけでよい。各FROM命令は、前の命令で作成された状態をすべてクリアする。
3 オプションとして、FROM命令にAS名を追加することで、新しいビルドステージに名前を付けることができる。これは、後続のFROM命令やCOPY -from=<名前>命令で、このステージで構築されたイメージを参照するために使用することができる。
4 タグやダイジェストの値はオプションです。どちらかを省略した場合、ビルダーはデフォルトでlatestをしていする。タグの値が見つからない場合、ビルダーはエラーとなる。。

ARGとFROMの相互作用を理解する

FROM命令は、最初のFROMの前に出現するARG命令で宣言された変数をサポートします。

ARG  CODE_VERSION=latest
FROM base:${CODE_VERSION}
CMD  /code/run-app

FROM extras:${CODE_VERSION}
CMD  /code/run-extras

FROMの前に宣言されたARGは、ビルドステージ外と認識される。FROMの後の命令では使用することができない。最初のFROMの前に宣言されたARGのデフォルト値を使用するには、ビルドステージの内部で値のないARG命令を使用します。

ARG VERSION=latest
FROM busybox:$VERSION
ARG VERSION
RUN echo $VERSION > image_version

https://docs.docker.com/engine/reference/builder/#from

RUN

# shell 形式(コマンドはシェル内で実行される。デフォルトは Linux が /bin/sh -c で、 Windows は cmd /S /C )
RUN <コマンド>
# exec 形式
RUN ["実行ファイル", "パラメータ1", "パラメータ2"]

RUN命令は、現在のイメージの上に新しいレイヤーで任意のコマンドを実行し、その結果をコミットします。コミットされたイメージは、Dockerfileの次のステップで使用されます。

  • RUN命令を重ねてコミットを生成することは、コミットが安価で、ソース管理のようにイメージの履歴のどの時点からでもコンテナを作成できるDockerの中核概念に適合しています。
  • exec形式は、シェルの文字列の乱れを回避し、指定したシェル実行ファイルを含まないベースイメージを使用してコマンド実行を可能にしている。
  • シェル形式では、1つのRUN命令を次の行に続けるために、\(バックスラッシュ)を使用することができます。
RUN /bin/bash -c 'source $HOME/.bashrc ;\
echo $HOME'
# ↑と↓は同等
RUN /bin/bash -c 'source $HOME/.bashrc ; echo $HOME'
# exec形式だと
RUN ["/bin/bash", "-c", "echo hello"]

:::note
exec形式JSON配列として構文解析されます。そのため、文字を囲むにはシングル・クォート(')ではなく、ダブル・クォート(")を使う必要がある。
:::

exec形式は、shell形式と異なり、コマンドシェルを呼び出すものではありません。つまり、通常のシェル処理は行われない。例えば、RUN [ "echo", "HOME" ]は、HOMEの変数置換を行いません。

  • シェル処理を行いたい場合は、シェル形式を使用するか、シェルを直接実行します:RUN [ "sh", "-c", "echo $HOME" ]
  • exec形式を使用してシェルを直接実行する場合、シェル形式の場合と同様に、環境変数の拡張を行うのはシェルであり、Dockerではありません。

RUN命令のキャッシュは、ビルド時に自動的に無効化されるわけではありません。RUN apt-get dist-upgrade -yのような命令のキャッシュは、次のビルド時に再利用されます。RUN命令のキャッシュは、docker build --no-cacheのように--no-cacheフラグを使用することで無効にすることができます。

https://docs.docker.com/engine/reference/builder/#run

CMD

CMD ["実行ファイル","パラメータ1","パラメータ2”](exec 形式:推奨)
CMD ["パラメータ1", "パラメータ2”](ENTRYPOINT 命令に対するデフォルトのパラメータとして扱う)
CMD コマンド パラメータ1 パラメータ2(シェル形式)

DockerfileのCMD命令は1つだけで、複数のCMDを記載した場合は、最後のCMDのみが有効になる。

CMDの主な目的 実行中のコンテナにデフォルトを提供することです。
これらのデフォルトは実行可能ファイルを含むことも、実行可能ファイルを省略することもでき、その場合はENTRYPOINT命令も指定する必要がある。

ENTRYPOINT命令のデフォルト引数としてCMDを使用する場合は、CMD命令とENTRYPOINT命令の両方をJSON配列形式で指定する必要がある。

  • exec 形式は、RUN 命令と同等。

    コンテナを起動するたびに、同じコマンドを毎回実行するのであれば、 ENTRYPOINT 命令と CMD 命令の組み合わせを検討。

https://docs.docker.com/engine/reference/builder/#cmd

LABEL

LABEL <キー>=<値> <キー>=<値> <キー>=<値> ...

LABEL命令は、イメージにメタデータを付加する命令です。LABELは、キーと値のペアです。LABELの値にスペースを含めるには、コマンドラインのパースと同様に、引用符とバックスラッシュを使用します。

LABEL "com.example.vendor"="ACME Incorporated"
LABEL com.example.label-with-value="foo"

イメージには複数のLABELを持つことが可能、また一行に複数のラベルを指定可能。

LABEL multi.label1="value1" multi.label2="value2" other="value3"

LABEL multi.label1="value1" \
      multi.label2="value2" \
      other="value3"

https://docs.docker.com/engine/reference/builder/#label

EXOPOSE

EXPOSE <ポート> [<ポート>/<プロトコル>...]

EXPOSE命令は、コンテナが実行時に指定されたネットワークポートをリッスンすることをDockerに通知します。ポートはTCPまたはUDPのどちらかを指定でき、プロトコルを指定しない場合のデフォルトはTCPです。

  • EXPOSE命令は、実際にポートを公開するわけではありません。これは、イメージを構築するユーザーとコンテナを実行するユーザーの間で、どのポートを公開することを意図しているかについて、一種の文書として機能している。
  • 実際にポートを公開するには、docker run-pフラグを使用して1つまたは複数のポートを公開してマッピングするか、-Pフラグを使用してすべての公開ポートを公開して高位ポートにマッピングする。
  • デフォルトでは、EXPOSEはTCPを想定しています。UDPを指定することも可能です。
EXPOSE 80/udp

https://docs.docker.com/engine/reference/builder/#expose

EMV

ENV <キー>=<値> ...

ENV命令は、環境変数keyに値valueを設定します。この値は、ビルドステージにおける後続のすべての命令の環境変数となり、同様に多くのインラインで置き換えることができます。この値は他の環境変数に対しても解釈されるため、引用文字がエスケープされていない場合は削除されます。コマンドラインの解析と同様に、引用符とバックスラッシュを使用して、値の中にスペースを含めることができます。

ENV命令は、環境変数keyに値valueを設定します。
この値は、ビルドステージにおける後続のすべての命令の環境変数となり、同様に多くのインラインで置き換えることができます。この値は他の環境変数に対しても解釈されるため、引用文字がエスケープされていない場合は削除されます。コマンドラインの解析と同様に、引用符とバックスラッシュを使用して、値の中にスペースを含めることができます。

ENV MY_NAME="John Doe"
ENV MY_DOG=Rex\ The\ Dog
ENV MY_CAT=fluffy

一行に複数の値を設定することが可能です。

ENV MY_NAME="John Doe" MY_DOG=Rex\ The\ Dog \
    MY_CAT=fluffy

https://docs.docker.com/engine/reference/builder/#env

ADD

ADD [--chown=<user>:<group>] [--chmod=<perms>] [--checksum=<checksum>] <src>... <dest>
ADD [--chown=<user>:<group>] [--chmod=<perms>] ["<src>",... "<dest>"]

後者の形式は、空白を含むパスの場合に必要です。

  • ADD命令は、新しいファイル、ディレクトリ、またはリモートファイルURLをsrcからコピーし、パスdestにあるイメージのファイルシステムに追加する命令です。
  • 複数のsrcリソースを指定することができますが、それらがファイルまたはディレクトリである場合、それらのパスはビルドのコンテキストのソースからの相対パスとして解釈されます。

hom "で始まるファイルをすべて追加する場合

ADD hom* /mydir/

相対パスを使用し、WORKDIR/relativeDir/test.txtを追加しています:

ADD test.txt relativeDir/

ADDは以下のルール

# 説明
1 srcパスはビルドのコンテキスト内になければなりません。Dockerビルドの最初のステップはコンテキストディレクトリ(およびサブディレクトリ)をdockerデーモンに送信するため、../something /somethingを追加することができません。
2 srcがURLで、destが末尾のスラッシュで終わっていない場合、URLからファイルをダウンロードし、destにコピーします。
3 srcがURLで、destが末尾のスラッシュで終わっている場合、URLからファイル名が推測され、<dest>/<filename>にファイルがダウンロードされます。例えば、ADD http://example.com/foobar /は、ファイル/foobarを作成します。この場合、適切なファイル名を発見できるように、URLは自明でないパスでなければなりません(http://example.com は機能しません)。
4 srcがディレクトリの場合、ファイルシステムのメタデータを含むディレクトリの全内容がコピーされる。
5 srcが認識された圧縮形式(identity、gzip、bzip2、xz)のローカルtarアーカイブの場合、ディレクトリとして解凍されます。リモートURLからのリソースは解凍されません。ディレクトリがコピーまたは解凍されると、tar -xと同じ動作になり、結果はその組合わせになる: ①デスティネーションパスに存在するもので ②ソースツリーの内容で、ファイル単位で "2. "に有利なようにコンフリクトを解決したものです。
6 src が他の種類のファイルである場合、メタデータとともに個別にコピーされる。この場合、destの末尾がスラッシュ/であれば、それはディレクトリとみなされ、srcの内容はdest/base(src)に書き込まれます。
7 複数のsrcリソースが直接またはワイルドカードの使用により指定された場合、destはディレクトリでなければならず、スラッシュ/で終わらなければならない。
8 destが末尾にスラッシュを持たない場合、通常のファイルとみなされ、srcの内容がdestに書き込まれます。
9 destが存在しない場合は、そのパスにあるすべての欠落したディレクトリと一緒に作成されます。

https://docs.docker.com/engine/reference/builder/#add

COPY

COPY [--chown=<user>:<group>] [--chmod=<perms>] <src>... <dest>
COPY [--chown=<user>:<group>] [--chmod=<perms>] ["<src>",... "<dest>"]

この後者の形式は、空白を含むパスの場合に必要です。

COPY命令は、新しいファイルやディレクトリを<src>からコピーし、パス<dest>にあるコンテナのファイルシステムに追加する命令です。

複数の<src>リソースを指定することができますが、ファイルやディレクトリのパスは、ビルドのコンテキストのソースからの相対パスとして解釈されます。

COPYのルール

# 説明
1 srcパスはビルドのコンテキスト内になければなりません。dockerビルドの最初のステップはコンテキストディレクトリ(およびサブディレクトリ)をdockerデーモンに送信するため、COPY ../something /somethingはできません。
2 srcがディレクトリの場合、ファイルシステムのメタデータを含むディレクトリの全内容がコピーされる。
3 src が他の種類のファイルである場合、メタデータとともに個別にコピーされる。この場合、destの末尾がスラッシュ/であれば、それはディレクトリとみなされ、srcの内容はdest/base(src)に書き込まれます。
4 複数のsrcリソースが直接またはワイルドカードの使用により指定された場合、destはディレクトリでなければならず、スラッシュ/で終わらなければならない。
5 destが末尾にスラッシュを持たない場合、通常のファイルとみなされ、srcの内容がdestに書き込まれます。
6 destが存在しない場合は、そのパスにあるすべての欠落したディレクトリと一緒に作成されます。

https://docs.docker.com/engine/reference/builder/#copy

ENTRYPOINT

# exec形式: 推奨
ENTRYPOINT ["executable", "param1", "param2"]
# shell形式
ENTRYPOINT command param1 param2

ENTRYPOINTでは、実行ファイルとして実行されるコンテナを構成することができる。

例えば、nginxがデフォルトの内容で起動し、ポート80でリスニングします。

sh
docker run -i -t --rm -p 80:80 nginx

docker run <image> のコマンドライン引数は、exec形式の ENTRYPOINTのすべての要素の後に付加され、CMDで指定したすべての要素を優先します。これにより、エントリーポイントに引数を渡すことができ、例えば、docker run <image> -dはエントリーポイントに-d引数を渡します。docker run --entrypointフラグを使用すると、ENTRYPOINT命令をオーバーライドすることができる。

シェル形式はCMDrunコマンドライン引数の使用を防ぎますが、ENTRYPOINT/bin/sh -cのサブコマンドとして起動されるという欠点があり、これはシグナルを受け渡しません。つまり、実行ファイルはコンテナのPID 1ではなく、Unixのシグナルを受け取らないので、実行ファイルはdocker stop <container>からSIGTERMを受け取らない。

Dockerfileの最後のENTRYPOINT命令だけが効果を発揮する。

https://docs.docker.com/engine/reference/builder/#entrypoint

WORKDIR

WORKDIR /path/to/workdir

WORKDIR命令は、Dockerfile内でそれに続くRUNCMDENTRYPOINTCOPYADD命令のための作業ディレクトリを設定します。WORKDIRが存在しない場合、後続のDockerfile命令で使用されなくても作成されます。
WORKDIR命令は、Dockerfile内で複数回使用することができる。相対パスが指定された場合、前のWORKDIR命令のパスからの相対パスとなる。

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

このDockerfileの最後のpwdコマンドの出力は、/a/b/cとなる。
WORKDIR命令は、ENVを使用して以前に設定した環境変数を解決することができます。Dockerfileで明示的に設定された環境変数のみを使用することができる。

ENV DIRPATH=/path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd
  • Dockerfileの最後のpwdコマンドの出力は、/path/$DIRNAMEになります。
  • 指定しない場合、デフォルトの作業ディレクトリは/です。実際には、Dockerfileをゼロから構築していない場合、WORKDIRは使用しているベースイメージによって設定されている可能性が高いでしょう。
  • 未知のディレクトリでの意図しない操作を避けるために、WORKDIRを明示的に設定するのがベストプラクティスです。

https://docs.docker.com/engine/reference/builder/#workdir

ARG

ARG <name>[=<default value>]

ARG命令は、ユーザがビルド時にdocker buildコマンドで-build-arg <varname>=<value>フラグを使ってビルダーに渡すことができる変数を定義しています。

Dockerfileは、1つ以上のARG命令を含むことができる。

FROM busybox
ARG user1
ARG buildno

まとめ

今回は以下の内容をまとめました。現在僕がよく使うものをまずはまとめました。これから少しずつまとめていこうかと思います。

(自分的なまとめ)

命令 説明
FROM ベースイメージを作成する
RUN シェルで実行するコマンドをここで命令する(イメージ)
CMD RUNと同様で、制約(最後のCMD命令のみ実行される)がある
LABEL イメージにメタデータを付与する
EXOPOSE 解放したいポートを指定する(実際は解放されない)
EMV 環境変数
ADD 追加したいファイル、ディレクトリ、リモートファイルの URLを指定すると、イメージのファイルシステム上に追加します
COPY 追加したいファイル、ディレクトリからイメージのファイルシステム上に追加します
ENTRYPOINT CMDと同様で、一つのENTRYPOINTを実行する
WORKDIR コンテナが作成された時に作業するディレクトリを指定する
ARG 変数として値を代入してDockerfile内で使用することが可能です

Discussion

ログインするとコメントできます