Dockerfile リファレンス¶
Docker は Dockerfile
から命令を読み込み、自動的にイメージを構築できます。 Dockerfile
はテキスト形式のドキュメントであり、コマンドライン上でイメージを作り上げる命令を全て記述します。ユーザは docker build
を使い、複数のコマンド行の命令を順次実行し、イメージを自動構築します。
このページでは Dockerfile
内で利用可能な命令を説明します。ページを読み終えたら、より便利に使うための Dockerfile
の ベスト・プラクティス もご覧ください。
使い方¶
docker build
コマンドは Dockerfile
と コンテクスト(context;イメージに含まれる「内容」の意味) に従ってイメージを構築します。構築用コンテクストとは、ファイルを示す PATH
や URL
の場所です。 PATH
はローカルのファイルシステム上のディレクトリです。 URL
は Git リポジトリの場所です。
コンテクストの処理は再帰的です。そのため、 PATH
にはサブディレクトリを含みます。また URL
であればリポジトリと、そのサブモジュールも含みます。単に build コマンドを実行したら、現在のディレクトリをコンテクストとして使います。
$ docker build .
Sending build context to Docker daemon 6.51 MB
...
構築を処理するのは Docker デーモンであり、 CLI ではありません。まずはじめの構築プロセスは、対象のコンテクスト(再帰的)をデーモンに送信することです。多くの場合、空のディレクトリをコンテクストとして使いますので、Dockerfile をそのディレクトリ設置できます。Dockerfile の構築に必要なファイルのみを(ディレクトリに)追加します。
警告
PATH
として自分のルート・ディレクトリ /
を使わないでください。これは、自分のハードディスクに含まれる内容を、Docker デーモンに転送しようとするためです。
Dockerfile
に記述した COPY
命令などで使うファイル指定を参照し、コンテクスト(内容物の意味)を構築します。構築パフォーマンスを向上するためには、 .dockerignore
ファイルにファイルやディレクトリを追加し、コンテクスト・ディレクトリから除外できます。より詳しい情報は、 .dockerignore ファイルの作成 をご覧ください。
伝統的に Dockerfile
は、Dockerfile
とコンテクストがあるルートの場所を示します。 docker build
時に -f
フラグを使えば、システム上のどこに Dockerfile があるか指定できます。
$ docker build -f /path/to/a/Dockerfile .
新しいイメージの構築に成功する時は、新しいイメージにリポジトリとタグを指定できます。
$ docker build -t shykes/myapp .
Docker デーモンは Dockerfile
の命令を1行ずつ実行し、必要があれば命令ごとにイメージをコミットし、最終的に新しいイメージ ID を出力します。Docker デーモンは送信したコンテクストを自動的に削除します。
各命令は独立して実行されるのでご注意ください。新しいイメージの作成時、 RUN cd /tmp
を実行したとしても、次の命令には何ら影響を与えません。
Docker は可能であればいつでも中間イメージ(キャッシュ)を再利用します。これは docker build
処理を速くするためです。コンソール出力に Using cache
(キャッシュを利用中)の文字列が表示されます。より詳しい情報は Dockerfile
ベスト・プラクティス・ガイドの 構築キャッシュ をご覧ください。
$ docker build -t svendowideit/ambassador .
Sending build context to Docker daemon 15.36 kB
Step 0 : FROM alpine:3.2
---> 31f630c65071
Step 1 : MAINTAINER SvenDowideit@home.org.au
---> Using cache
---> 2a1c91448f5f
Step 2 : RUN apk update && apk add socat && rm -r /var/cache/
---> Using cache
---> 21ed6e7fbb73
Step 3 : CMD env | grep _TCP= | sed 's/.*_PORT_\([0-9]*\)_TCP=tcp:\/\/\(.*\):\(.*\)/socat -t 100000000 TCP4-LISTEN:\1,fork,reuseaddr TCP4:\2:\3 \&/' && echo wait) | sh
---> Using cache
---> 7ea8aef582cc
Successfully built 7ea8aef582cc
構築が終わったら、レジストリにリポジトリを送信 する準備が整います。
書式¶
ここでは Dockerfile
の書式を説明します。
# コメント
命令 引数
命令(instruction)は大文字と小文字を区別しません。しかし引数(arguments)を簡単に見分けられるよう、大文字にするのが便利です。
Docker は Dockerfile
の命令を順番に実行します。イメージ構築にあたり ベース・イメージ を指定するため、 1行めの命令は「FROM」であるべき です。
Docker は有効な パーサ・ディレクティブ でなければ、 #
で 始まる 行をコメントとみなします。 #
マークは行における以降の文字をコメントとみなします。コメントは次のような書き方ができます。
# コメント
RUN echo '良い感じもの何かを # 実行しています'
行中で命令の文字列が継続している場合は、コメント扱いしません。
パーサ・ディレクティブ(parser directive)¶
パーサ・ディレクティブはオプションです。 Dockerfile
中では、次に続く行にも影響を与えます。パーサ・ディレクティブは構築時にレイヤを追加しませんので、構築ステップでは表示されません。パーサ・ディレクティブは # ディレクティブ=値
という特別な種類のコメントとして記述します。1つのディレクティブは1度しか使われません。
コメントがあれば、空行または構築命令があったとしても、 Docker はパーサ・ディレクティブを探しません。そのかわり、あらゆる書式をパーサ・ディレクティブではなくコメントとみなすため、有効なパーサ・ディレクティブとはみなしません。そのため、全てのパーサ・ディレクティブは Dockerfile
の限りなく上に書くべきです。
パーサ・ディレクティブは大文字と小文字を区別しません。しかしながら、小文字での記述が便利です。パーサ・ディレクティブの後に空白行を入れるのも便利です。行を継続する文字列は、パーサ・ディレクティブではサポートされません。
これらのルールがあるため、以下の例は全て無効です。
行の継続は無効:
# direc \
tive=value
2つ並ぶ場合は無効:
# directive=value1
# directive=value2
FROM ImageName
構築命令の後にあれば、コメントとして扱う:
FROM ImageName
# directive=value
パーサ・ディレクティブでないコメントがあれば、以降のものはコメントとして扱う:
# About my dockerfile
FROM ImageName
# directive=value
不明なディレクティブは認識できないため、コメントとして扱う。さらに、パーサ・ディレクティブではないコメントの後にディレクティブがあったとしても、コメントとして扱う:
# unknowndirective=value # knowndirective=value
行を壊さない空白行はパーサ・ディレクティブとして使えます。従って、以下の行はすべて同一として扱う:
#directive=value
# directive =value
# directive= value
# directive = value
# dIrEcTiVe=value
以下のパーサ・ディレクティブをサポートします:
escape
escape¶
# escape=\ (バックスラッシュ)
または
# escape=` (バッククォート)
escape
ディレクティブは Dockerfile
で使うエスケープ文字をセットします。指定しなければ、デフォルトのエスケープ文字は \
です。
エスケープ文字を使うのは、行におけるエスケープ文字と、新しい行にエスケープする(つなげる)ための両方です。これにより、 Dockerfile
の命令を複数行に記述できます。 Dockerfile
で escape
パーサ・ディレクティブを指定しなければ RUN
命令でもエスケープ処理はされませんが、行の最後は除くのでご注意ください。
エスケープ文字に 「`」 を指定するのは Windows
上で役立ちます。 \
はディレクトリのパスのパーサ(区切り)だからです。 「`」 は Windows PowerShell で次の行をつなぎます。
以下では Windows では明確にエラーが出る例を考えます。2行目末尾の2つめの \
は、1つめの \
のエスケープとして処理されるのではなく、新しい行のためのエスケープとして扱われます。同様に3行目末尾の \
は次の行に命令が継続するものとして扱われます。この dockerfile を使った結果、2行目と3行目は1つの命令として見なされます。
FROM windowsservercore
COPY testfile.txt c:\\
RUN dir c:\
実行結果:
PS C:\John> docker build -t cmd .
Sending build context to Docker daemon 3.072 kB
Step 1 : FROM windowsservercore
---> dbfee88ee9fd
Step 2 : COPY testfile.txt c:RUN dir c:
GetFileAttributesEx c:RUN: The system cannot find the file specified.
PS C:\John>
解決方法の1つは、 COPY
命令とディレクトリで /
を使う方法です。しかしながら、構文上ベストなのは Windows
上のパスを普通に扱うことであり、最悪なのは Windows
上でパスの区切りとして /
を指定するとエラーを起こしがちです。
escape
パーサ・ディレクティブを追加することで、次の Dockerfile
は Windows
上のファイル・パスを期待通りに処理します。
# escape=`
FROM windowsservercore
COPY testfile.txt c:\
RUN dir c:\
実行結果:
PS C:\John> docker build -t succeeds --no-cache=true .
Sending build context to Docker daemon 3.072 kB
Step 1 : FROM windowsservercore
---> dbfee88ee9fd
Step 2 : COPY testfile.txt c:\
---> 99ceb62e90df
Removing intermediate container 62afbe726221
Step 3 : RUN dir c:\
---> Running in a5ff53ad6323
Volume in drive C has no label.
Volume Serial Number is 1440-27FA
Directory of c:\
03/25/2016 05:28 AM <DIR> inetpub
03/25/2016 04:22 AM <DIR> PerfLogs
04/22/2016 10:59 PM <DIR> Program Files
03/25/2016 04:22 AM <DIR> Program Files (x86)
04/18/2016 09:26 AM 4 testfile.txt
04/22/2016 10:59 PM <DIR> Users
04/22/2016 10:59 PM <DIR> Windows
1 File(s) 4 bytes
6 Dir(s) 21,252,689,920 bytes free
---> 2569aa19abef
Removing intermediate container a5ff53ad6323
Successfully built 2569aa19abef
PS C:\John>
環境変数の置き換え¶
Dockerfile
は環境変数( env 命令 で宣言)も解釈できます。命令文字(ステートメント・リテラル)中では、変数の様な構文でエスケープ・シーケンスも扱えます。
Dockerfile
の中では、環境変数を $variable_name
または ${variable_name}
の形式で記述します。これらは同等に扱われます。固定用の構文として典型的に使われるのは、空白スペースを変数名に入れず ${foo}_bar
のような変数名で割り当てることです。
${変数の_名前}
構文は、次のような bash
の変更をサポートしています。
${変数:-文字}
は、変数
を設定したら、その値を使うことを意味します。もし変数
がセットされ無ければ、文字
が設定されます。${変数:+文字}
は、変数
を設定したら、文字
を使います。変数
がセットされなければ、空白のままにします。
いずれの場合でも、 文字
とは何らかの文字列であり、追加の環境変数を含みます。
エスケープするには \$foo
や \${foo}
のように、変数名の前に \
を付けます。例えば、 $foo
と ${foo}
リテラルは別々のものです。
例(変数展開したものは、 #
のあとに表示):
FROM busybox
ENV foo /bar
WORKDIR ${foo} # WORKDIR /bar
ADD . $foo # ADD . /bar
COPY \$foo /quux # COPY $foo /quux
以下の命令で Dockerfile
における環境変数の利用がサポートされています。
ADD
COPY
ENV
EXPOSE
LABEL
USER
WORKDIR
VOLUME
STOPSIGNAL
同様に、
ONBLIUD
(上記の命令と組み合わせて使う場合にサポートされます)
注釈
1.4 より前のバージョンでは、環境変数における ONBUILD
命令と上記の命令の組み合わせはサポート していません 。
環境変数を使う代わりに、各変数をコマンド上で利用できます。次の例を見ましょう。
ENV abc=hello
ENV abc=bye def=$abc
ENV ghi=$abc
この結果は、 def
の値が hello
であり、 bye
ではありません。しかしながら ghi
の値は bye
になります。これは abc
を bye
に設定するのと同じコマンド行ではないためです。
.dockerignore ファイル¶
docker CLI がコンテクストを docker デーモンに送る前に、コンテクストのルートディレクトリ内の .dockerignore
ファイルを探します。もしファイルが存在していれば、CLI はコンテクストからパターンに一致するファイルとディレクトリを除外します。これは不必要に大きくならないようにします。また、取り扱いに注意が必要なファイルやディレクトリをデーモンに送らないようにします。ですが、 ADD
や COPY
でイメージに追加されるかもしれません。
CLI は .dockerignore
ファイルを行ごとに隔てて解釈します。行の一致パターンは Unix シェル上のものに似ています。パターンがコンテクストの root に一致すると考えられる場合は、root ディレクトリとして動作します。例えば、パターン /foo/bar
と foo/bar
がある場合、いずれも PATH
における foo
サブディレクトリの bar
ファイルを削除します。あるいは URL
の場所にある git のルートでもです。どちらでも除外されます。
.dockerignore
ファイルの1行目が #
で始まる場合は、この合はコメントであると見なされ、CLI の処理からは無視されます。
これは .dockerignore
ファイルの例です:
# コメント
*/temp*
*/*/temp*
temp?
このファイルは構築時に以下の動作をします。
# コメント
… 無視します。*/temp*
… ルート以下のあらゆるサブディレクトリを含め、temp
で始まる名称のファイルとディレクトリを除外します。例えば、テキストファイル/somedir/temporary.txt
は除外しますし、ディレクトリ/somedir/temp
も除外します。*/*/temp*
… ルートから2レベル以下のtemp
で 始まる名称のファイルとディレクトリを除外します。例えば/somedir/subdir/temporary.txt
を除外します。temp?
… ルートディレクトリにあるファイル名がtemp
と1文字一致するファイルとディレクトリを除外します。例えば、/tempa
と/tempb
を除外します。
一致には Go 言語の filepath.Match ルールを使います。処理前のステップでは、空白スペースと .
と ..
要素を Go 言語の filepath.Clean を用いて除外します。
行を !
(エクスクラメーション・マーク)で始めると、除外ルールとして使えます。以下の例は .dockerignore
ファイルでこの仕組みを使ったものです。
*.md
!README.md
README.md を除く全てのマークダウンファイルが、コンテントから除外されます。
!
除外ルールが影響を与えるのは、 .dockerignore
ファイルに書いた場所以降に一致するパターンが現れた時、含めるか除外するかを決めます。次の例で考えて見ましょう。
*.md
!README*.md
README-secret.md
README を含むファイル以外は、README-secret.md
も含め、残り全てのマークダウンファイルが除外対象です。
その次の例を考えましょう。
*.md
README-secret.md
!README*.md
README を含む全てのファイル除外します。真ん中の行 README-secret.md
は最終行の !README*.md
に一致するため、何の影響もありません。
.dockerignore
ファイルは Dockerfile
と .dockerignore
ファイルの除外にも使えます。それでも、これらのファイルはジョブを処理するためデーモンに送信されます。しかし ADD
と COPY
コマンドは、これらをイメージ内にコピーしません。
最後に、特定のファイルのみコンテクストに含め、他を除外したい場合があるでしょう。実行するには、始めに *
パターンに指定し、以下1つまたは複数の !
例外パターンを記述します。
注釈
歴史的な理由により、 .
パターンは無視されます。
FROM¶
FROM <イメージ>
または
FROM <イメージ>:<タグ>
または
FROM <イメージ>@<digest>
FROM
命令は、以降の命令で使う ベース・イメージ を指定します。あるいは、有効な Dockerfile
は、1行めを FROM
命令で指定する必要があります。イメージとは、あらゆる有効なものが利用できます。 パブリック・リポジトリ から イメージを取得する 方法が一番簡単です。
Dockerfile
では、コメント以外ではFROM
を一番始めに書く必要があります。
- 単一の
Dockerfile
から複数のイメージを作成するため、複数のFROM
を指定できます。各FROM
命令ごとに自動的にコミットし、最新のイメージ ID が出力されるのを覚えておいてください。
タグ
やdigest
値はオプションです。省略した場合、ビルダーはデフォルトのlatest
とみなします。ビルダーは一致するtag
値が無ければエラーを返します。
RUN¶
RUN には2つの形式があります。
RUN <コマンド>
(シェル形式、コマンドを実行する。Linux 上のデフォルトは/bin/sh -c
であり、Windows 上cmd /S /C
)RUN ["実行バイナリ", "パラメータ1", "パラメータ2"]
( exec 形式)
RUN
命令は既存イメージ上の新しいレイヤで、あらゆるコマンドを実行し、その結果をコミットする命令です。コミットの結果得られたイメージは、 Dockerfile
の次のステップで使われます。
RUN
命令の積み重ねとコミットによるイメージ生成は、 Docker の中心となるコンセプト(概念)に従ったものです。コミットは簡単であり、ソース・コントロールのように、イメージの履歴上のあらゆる場所からコンテナを作成可能です。
exec 形式はシェルの文字列を変更できないようにします。また、 指定されたシェル実行環境がベース・イメージに含まれなくても RUN
コマンドを使えます。
デフォルトの shell のシェルを変更するには SHELL
コマンドで変更できます。
シェル 形式では、RUN 命令を \
(バックスラッシュ)を使い、次の行と連結します。例えば、次の2行があるとします。
RUN /bin/bash -c 'source $HOME/.bashrc ;\
echo $HOME'
これは、次のように1行にできます。
RUN /bin/bash -c 'source $HOME/.bashrc ; echo $HOME'
注釈
「/bin/sh/」以外のシェルを使いたい場合は、exec 形式で任意のシェルを指定します。例: RUN ["/bin/bash", "-c", "echo hello"]
。
注釈
exec 形式は JSON 配列でパースされます。つまり、文字を囲むのはシングル・クォート(‘) ではなくダブル・クォート(”)を使う必要があります。
注釈
シェル 形式と異なり、 exec 形式はコマンド・シェルを呼び出しません。つまり、通常のシェルによる処理が行われません。例えば RUN [ "echo", "$HOME" ]
は $HOME
の変数展開を行いません。シェルによる処理を行いたい場合は、 シェル 形式を使うか、あるいはシェルを直接指定します。例: RUN [ "sh", "-c", "echo", "$HOME" ]
。
注釈
JSON 形式では、バック・スラッシュはエスケープが必要です。特に関係があるのは Windows でパス区切りにバック・スラッシュを使う場合です。次の行は JSON 形式ではなくシェル形式と見なされエラーになります: RUN ["c:\windows\system32\tasklist.exe"]
。適切な構文は RUN ["c:\\windows\\system32\\tasklist.exe"]
です。
次の構築時、RUN
命令によるキャッシュは自動的に無効化できません。 RUN apt-get dist-upgrade -y
のような命令のキャッシュがあれば、次の構築時に再利用されます。 RUN
命令でキャッシュを使いたくない場合は、 --no-cache
フラグを使います。例: docker build --no-cache
.
より詳しい情報は Dockerfile
ベスト・プラクティス・ガイド をご覧ください。
RUN
命令のキャッシュは、 ADD
命令によって無効化されます。詳細は 以下 をご覧ください。
既知の問題(RUN)¶
- Issue 783 は、AUFS ファイルシステム使用時、ファイルのパーミッションに関する問題が起こり得ます。例えば、ファイルを
rm
しようとする場合は注意が必要です。
最近の aufs バージョンを使っているシステムでは(例: dirperm1
マウント・オプションが利用可能 )、docker は dirperm1
オプションのレイヤをマウント時、自動的に問題を修正しようとします。 dirperm1
オプションに関する詳細は、 aufs
man ページ をご覧ください。
システムが dirperm1
をサポートしていない場合は、issue に回避方法があります。
CMD¶
CMD
には3つの形式があります。
CMD ["実行バイナリ", "パラメータ1", "パラメータ2"]
( exec 形式、推奨する形式)CMD ["パラメータ1", "パラメータ2"]
( ENTRYPOINT のデフォルト・パラメータ)CMD <コマンド>
(シェル形式)
Dockerfile
で CMD
命令を一度だけ指定できます。複数の CMD
がある場合、最も後ろの CMD
のみ有効です。
CMD
の主な目的は、 コンテナ実行時のデフォルトを提供します 。 デフォルトには、実行可能なコマンドが含まれているか、あるいは省略されるかもしれません。省略時は ENTRYPOINT
命令で同様に指定する必要があります。
注釈
ENTRYPOINT
命令のデフォルトの引数として CMD
を使う場合、 CMD
と ENTRYPOINT
命令の両方が JSON 配列フォーマットになっている必要があります。
注釈
exec 形式は JSON 配列でパースされます。つまり、文字を囲むのはシングル・クォート(‘) ではなくダブル・クォート(”)を使う必要があります。
注釈
シェル 形式と異なり、 exec 形式はコマンド・シェルを呼び出しません。つまり、通常のシェルによる処理が行われません。例えば CMD [ "echo", "$HOME" ]
は $HOME
の変数展開を行いません。シェルによる処理を行いたい場合は、 シェル 形式を使うか、あるいはシェルを直接使います。例: CMD [ "sh", "-c", "echo", "$HOME" ]
。
シェルあるいは exec 形式を使う時、 CMD
命令はイメージで実行するコマンドを指定します。
CMD
を シェル 形式で使えば、 <コマンド>
は /bin/sh -c
で実行されます。
FROM ubuntu
CMD echo "This is a test." | wc -
<コマンド>をシェルを使わずに実行 したい場合、コマンドを JSON 配列で記述し、実行可能なフルパスで指定する必要があります。 配列の形式が CMD では望ましい形式です 。あらゆる追加パラメータは個々の配列の文字列として指定する必要があります。
FROM ubuntu
CMD ["/usr/bin/wc","--help"]
もしコンテナで毎回同じものを実行するのであれば、 CMD
と ENTRYPOINT
の使用を検討ください。詳細は ENTRYPOINT をご覧ください。
ユーザが docker run
で引数を指定した時、これらは CMD
で指定したデフォルトを上書きします。
注釈
RUN
と CMD
を混同しないでください。 RUN
が実際に行っているのは、コマンドの実行と結果のコミットです。一方の CMD
は構築時には何もしませんが、イメージで実行するコマンドを指定します。
LABEL¶
LABEL <key>=<value> <key>=<value> <key>=<value> ...
LABEL
命令はイメージにメタデータを追加します。 LABEL
はキーとバリューのペアです。 LABEL
の値に空白スペースを含む場合はクォートを使いますし、コマンドラインの分割にバックスラッシュを使います。使用例:
LABEL "com.example.vendor"="ACME Incorporated"
LABEL com.example.label-with-value="foo"
LABEL version="1.0"
LABEL description="This text illustrates \
that label-values can span multiple lines."
イメージは複数のラベルを持てます。複数のラベルを指定したら、 Docker は可能であれば1つの LABEL
にすることをお勧めします。各 LABEL
命令は新しいレイヤを準備しますが、多くのラベルを使えば、それだけレイヤを使います。次の例は1つのイメージ・レイヤを使うものです。
LABEL multi.label1="value1" multi.label2="value2" other="value3"
上記の例は、次のようにも書き換えられます。
LABEL multi.label1="value1" \
multi.label2="value2" \
other="value3"
ラベルには、FROM
イメージが使う LABEL
も含まれています。ラベルのキーが既に存在している時、Docker は特定のキーを持つラベルの値を上書きします。
イメージが使っているラベルを確認するには、 docker inspect
コマンドを使います。
"Labels": {
"com.example.vendor": "ACME Incorporated"
"com.example.label-with-value": "foo",
"version": "1.0",
"description": "This text illustrates that label-values can span multiple lines.",
"multi.label1": "value1",
"multi.label2": "value2",
"other": "value3"
},
EXPOSE¶
EXPOSE <port> [<port>...]
EXPOSE
命令は、特定のネットワーク・ポートをコンテナが実行時にリッスンすることを Docker に伝えます。 EXPOSE
があっても、これだけではホストからコンテナにアクセスできるようにしません。アクセスするには、 -p
フラグを使ってポートの公開範囲を指定するか、 -P
フラグで全ての露出ポートを公開する必要があります。外部への公開時は他のポート番号も利用可能です。
ホストシステム上でポート転送を使うには、 -P フラグを使う をご覧ください。Docker のネットワーク機能は、ネットワーク内でポートを公開しないネットワークを作成可能です。詳細な情報は 機能概要 をご覧ください。
ENV¶
ENV <key> <value>
ENV <key>=<value> ...
ENV
命令は、環境変数 <key>
と 値 <value>
のセットです。値は Dockerfile
から派生する全てのコマンド環境で利用でき、 インラインで置き換え も可能です。
ENV
命令は2つの形式があります。1つめは、 ENV <key> <value>
であり、変数に対して1つの値を設定します。はじめの空白以降の文字列が <value>
に含まれます。ここには空白もクォートも含まれます。
2つめの形式は ENV <key>=<value> ...
です。これは一度に複数の変数を指定できます。先ほどと違い、構文の2つめにイコールサイン(=)があるので気を付けてください。コマンドラインの分割、クォート、バックスラッシュは、空白スペースも含めて値になります。
例:
ENV myName="John Doe" myDog=Rex\ The\ Dog \
myCat=fluffy
そして
ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat fluffy
この例では、どちらも最終的に同じ結果をコンテナにもたらしますが、私たちが推奨するのは前者です。理由は前者であれば単一のキャッシュ・レイヤしか使わないからです。
環境変数の設定に ENV
を使えば、作成したイメージを使ってコンテナを実行しても有効です。どのような値が設定されているかは docker inspect
で確認でき、変更するには docker run --env <key>=<value>
を使います。
注釈
環境変数の一貫性は予期しない影響を与える場合があります。例えば、 ENV DEBIAN_FRONTEND noninteractive
が設定されていると、Debian ベースのイメージで apt-get の利用者が混乱するかもしれません。1つのコマンドだけで値を設定するには、 RUN <key>=<value> <コマンド>
を使います。
ADD¶
Add は2つの形式があります。
ADD <ソース>... <送信先>
ADD ["<ソース>", ... "<送信先>"]
(この形式はパスに空白スペースを使う場合に必要)
ADD
命令は <ソース>
にある新しいファイルやディレクトリをコピー、あるいはリモートの URL からコピーします。それから、コンテナ内のファイルシステム上にある 送信先
に指定されたパスに追加します。
複数の <ソース>
リソースを指定できます。この時、ファイルやディレクトリはソースディレクトリ(構築時のコンテクスト)からの相対パス上に存在しないと構築できません。
それぞれの <ソース>
にはワイルドカードと Go 言語の filepath.Match ルールに一致するパターンが使えます。例えば、次のような記述です。
ADD hom* /mydir/ # "hom" で始まる全てのファイルを追加
ADD hom?.txt /mydir/ # ? は1文字だけ一致します。例: "home.txt"
<送信先>
は絶対パスです。あるいは、パスは WORKDIR
からの相対パスです。ソースにあるものが、対象となる送信先コンテナの中にコピーされます。
ADD test relativeDir/ # "test" を `WORKDIR`/relativeDir/ (相対ディレクトリ)に追加
ADD test /absoluteDir/ # "test" を /absoluteDir/ (絶対ディレクトリ)に追加
追加される新しいファイルやディレクトリは、全て UID と GID が 0 として作成されます。
<ソース>
がリモート URL の場合は、送信先のパーミッションは 600 にします。もしリモートのファイルが HTTP Last-Modified
ヘッダを返す場合は、このヘッダの情報を元に送信先ファイルの mtime
を指定するのに使います。しかしながら、 ADD
を使ったファイルをコピーする手順では、 mtime
はファイルが更新されたかどうかの決定には使われず、ファイルが更新されればキャッシュも更新されます。
注釈
Dockerfile
を標準入力( docker build - < 何らかのファイル
)を通して構築しようとしても。構築時のコンテントは存在しないため、 Dockerfile
には URL を指定する ADD
命令のみ記述可能です。また、圧縮ファイルを標準入力( docker build - < archive.tar.gz
)を通すことができ、アーカイブに含まれるルートに Dockerfile
があれば、構築時のコンテクストとしてアーカイブが使われます。
注釈
URL で指定したファイルに認証がかかっている場合は、 RUN wget
や RUN curl
や他のツールを使う必要があります。これは ADD
命令が認証機能をサポートしていないからです。
注釈
ADD
命令の処理時、まず <ソース>
に含まれる内容が変更されていれば、以降の Dockerfile
に書かれている命令のキャッシュを全て無効化します。これは RUN
命令のキャッシュ無効化も含まれます。より詳細な情報については Dockerfile
の ベスト・プラクティス・ガイド をご覧ください。
ADD
は以下のルールに従います。
<ソース>
パスは、構築時の コンテント 内にある必要があります。そのため、ADD ../something /something
の指定はできません。docker build
の最初のステップで、コンテクストのディレクトリ(と、サブディレクトリ)を docker デーモンに送るためです。
<ソース>
が URL であり、<送信先>
の末尾にスラッシュが無い場合、URL からファイルをダウンロードし、<送信先>
にコピーします。
- もし
<ソース>
が URL であり、<送信先>
の末尾がスラッシュの場合、URL からファイル名を推測し、ファイルを<送信先>/<ファイル名>
にダウンロードします。例えば、ADD http://example.com/foobar /
は、/foobar
ファイルを作成します。URL には何らかのパスが必要です。これは適切なファイル名を見つけられない場合があるためです(今回の例では、http://example.com
の指定は動作しません)。
<ソース>
がディレクトリの場合、ディレクトリの内容の全てをコピーします。これにはファイルシステムのメタデータを含みます。
注釈
ディレクトリ自身はコピーされません。ディレクトリは単なるコンテントの入れ物です。
- もし
<ソース>
が ローカル にある tar アーカイブの場合、圧縮フォーマットを認識します(gzip、bzip2、xz を認識)。それからディレクトリに展開します。 リモート の URL が指定された場合は展開 しません。ディレクトリにコピーまたは展開する時は、tar -x
と同じ働きをします。結果は次の処理を同時に行います。
- 送信先のパスが存在しているかどうか
- ファイル単位の原則に従って、ソース・ツリーの内容と衝突しないかどうか「2」を繰り返す
注釈
ファイルが圧縮フォーマットと認識するか、あるいはファイルの集まりをベースにしているのかは、ファイルの名前では判断しません。例えば、空のファイル名の拡張子が .tar.gz
だとしても、圧縮ファイルと認識しないため、展開エラーのメッセージを表示 しません 。そして単純に送信先にファイルをコピーします。
- もし
<ソース>
がファイル以外であれば、個々のメタデータと一緒にコピーします。<送信先>
の末尾がスラッシュ/
で終わる場合は、ディレクトリであるとみなし、<ソース>
の内容を<送信先>/base(<ソース>)
に書き込みます。
- もし複数の
<ソース>
リソースが指定された場合や、ディレクトリやワイルドカードを使った場合、<送信先>
は必ずディレクトリになり、最後はスラッシュ/
にしなければいけません。
- もし
<送信先>
の末尾がスラッシュで終わらなければ、通常のファイルとみなされ、<ソース>
の内容は<送信先>
として書き込まれます。
<送信先>
が存在しなければ、パスに存在しないディレクトリを作成します。
COPY¶
COPY は2つの形式があります。
COPY <ソース>... <送信先>
COPY ["<ソース>",... "<送信先>"]
(この形式はパスに空白スペースを使う場合に必要)
COPY
命令は <ソース>
にある新しいファイルやディレクトリをコピーするもので、コンテナ内のファイルシステム上にある <送信先>
に指定されたパスに追加します。
複数の <ソース>
リソースを指定できます。この時、ソースディレクトリ(構築時のコンテクスト)からの相対パス上に存在しないと構築できません。
それぞれの <ソース>
にはワイルドカードと Go 言語の filepath.Match ルールに一致するパターンが使えます。例えば、次のような記述です。
COPY hom* /mydir/ # "hom" で始まる全てのファイルを追加
COPY hom?.txt /mydir/ # ? は1文字だけ一致します。例: "home.txt"
<送信先>
は絶対パスです。あるいは、パスは WORKDIR
からの相対パスです。ソースにあるものが、対象となる送信先コンテナの中にコピーされます。
COPY test relativeDir/ # "test" を `WORKDIR`/relativeDir/ (相対ディレクトリ)に追加
COPY test /absoluteDir/ # "test" を /absoluteDir/ (絶対ディレクトリ)に追加
追加される新しいファイルやディレクトリは、全て UID と GID が 0 として作成されます。
注釈
標準入力( docker build - < 何らかのファイル
)を使って構築しようとしても、構築時のコンテントは存在しないため、 COPY
を使えません。
COPY
は以下のルールに従います。
<ソース>
パスは、構築時の コンテント 内にある必要があります。そのため、COPY ../something /something
の指定はできません。docker build
の最初のステップで、コンテクストのディレクトリ(と、サブディレクトリ)を docker デーモンに送るためです。
<ソース>
がディレクトリの場合、ディレクトリ内容の全てをコピーします。これにはファイルシステムのメタデータを含みます。
注釈
ディレクトリ自身はコピーしません。ディレクトリは単なるコンテントの入れ物です。
- もし
<ソース>
がファイル以外であれば、個々のメタデータと一緒にコピーします。<送信先>
の末尾がスラッシュ/
で終わる場合は、ディレクトリであるとみなし、<ソース>
の内容を<送信先>/base(<ソース>)
に書き込みます。
- もし複数の
<ソース>
リソースが指定された場合や、ディレクトリやワイルドカードを使った場合、<送信先>
は必ずディレクトリになり、最後はスラッシュ/
にしなければいけません。
- もし
<送信先>
の末尾がスラッシュで終わらなければ、通常のファイルとみなされ、<ソース>
の内容は<送信先>
として書き込まれます。
<送信先>
が存在しなければ、パスに存在しないディレクトリを作成します。
ENTRYPOINT¶
ENTRYPOINT には2つの形式があります。
ENTRYPOINT ["実行可能なもの", "パラメータ1", "パラメータ2"]
( exec 形式、推奨)ENTRYPOINT コマンド パラメータ1 パラメータ2
( シェル 形式)
ENTRYPOINT
はコンテナが実行するファイルを設定します。
例えば、次の例は nginx をデフォルトの内容で開始し、ポート 80 を開きます。
docker run -i -t --rm -p 80:80 nginx
コマンドラインで docker run <イメージ>
コマンドに引数を付けますと、exec 形式 の ENTRYPOINT
で指定した全要素の後に追加します。そして、この時に CMD
を使って指定していた要素を上書きします。この動きにより、引数はエントリ・ポイント(訳者注:指定されたバイナリ)に渡されます。例えば、 docker run <イメージ> -d
は、引数 -d
をエントリポイントに渡します。 ENTRYPOINT
命令を上書きするには、 docker run --entrypoint
フラグを使います。
シェル 形式では CMD
や run
コマンド行の引数を使えないという不利な点があります。 ENTRYPOINT
は /bin/sh -c
のサブコマンドとして実行されるため、シグナルを渡せません。つまり、何かを実行してもコンテナの PID 1
にはなりません。そして、 Unix シグナルを受け付け ません。そのため、実行ファイルは docker stop <コンテナ>
を実行しても、 SIGTERM
を受信しません。
なお、 Dockerfile
の最後に現れた ENTRYPOINT
命令のみ有効です。
exec 形式の ENTRYPOINT 例¶
ENTRYPOINT
の exec 形式を使い、適切なデフォルトのコマンドと引数を指定します。それから CMD
を使い、変更する可能性のある追加のデフォルト引数も指定します。
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]
コンテナを実行したら、 top
のプロセスが1つだけ見えます。
$ docker run -it --rm --name test top -H
top - 08:25:00 up 7:27, 0 users, load average: 0.00, 0.01, 0.05
Threads: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.1 us, 0.1 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem: 2056668 total, 1616832 used, 439836 free, 99352 buffers
KiB Swap: 1441840 total, 0 used, 1441840 free. 1324440 cached Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 19744 2336 2080 R 0.0 0.1 0:00.04 top
より詳細なテストをするには、 docker exec
コマンドが使えます。
$ docker exec -it test ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 2.6 0.1 19752 2352 ? Ss+ 08:24 0:00 top -b -H
root 7 0.0 0.1 15572 2164 ? R+ 08:25 0:00 ps aux
それから、docker stop test
を使い top
を停止するよう、通常のリクエストを行えます。
次の Dockerfile
は ENTRYPOINT
を使って Apache をフォアグラウンドで実行します(つまり、 PID 1
として)。
FROM debian:stable
RUN apt-get update && apt-get install -y --force-yes apache2
EXPOSE 80 443
VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"]
ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]
もし実行するだけの起動スクリプトを書く必要があれば、最後に実行するコマンドが Unix シグナルを受信できるよう、 exec
と gosu
コマンドを使うことで可能になります。
#!/bin/bash
set -e
if [ "$1" = 'postgres' ]; then
chown -R postgres "$PGDATA"
if [ -z "$(ls -A "$PGDATA")" ]; then
gosu postgres initdb
fi
exec gosu postgres "$@"
fi
exec "$@"
もしも、シャットダウン時に何らかの追加クリーンアップ(あるいは、他のコンテナとの通信)が必要な場合や、1つ以上の実行ファイルと連携したい場合は、 ENTRYPOINT
のスクリプトが Unix シグナルを受信出来るようにし、それを使って様々な処理を行います。
#!/bin/sh
# メモ:これは sh を使っていますので、busyboy コンテナでも動きます
# サービス停止時に手動でもクリーンアップが必要な場合は trap を使います。
# あるいは1つのコンテナ内に複数のサービスを起動する必要があります。
trap "echo TRAPed signal" HUP INT QUIT TERM
# ここからバックグラウンドでサービスを開始します
/usr/sbin/apachectl start
echo "[hit enter key to exit] or run 'docker stop <container>'"
read
# ここからサービスを停止し、クリーンアップします
echo "stopping apache"
/usr/sbin/apachectl stop
echo "exited $0"
このイメージを docker run -it --rm -p 80:80 --name test apache
で実行したら、コンテナのプロセス状態を docker exec
や docker top
で調べられます。それから、スクリプトに Apache 停止を依頼します。
$ docker exec -it test ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.1 0.0 4448 692 ? Ss+ 00:42 0:00 /bin/sh /run.sh 123 cmd cmd2
root 19 0.0 0.2 71304 4440 ? Ss 00:42 0:00 /usr/sbin/apache2 -k start
www-data 20 0.2 0.2 360468 6004 ? Sl 00:42 0:00 /usr/sbin/apache2 -k start
www-data 21 0.2 0.2 360468 6000 ? Sl 00:42 0:00 /usr/sbin/apache2 -k start
root 81 0.0 0.1 15572 2140 ? R+ 00:44 0:00 ps aux
$ docker top test
PID USER COMMAND
10035 root {run.sh} /bin/sh /run.sh 123 cmd cmd2
10054 root /usr/sbin/apache2 -k start
10055 33 /usr/sbin/apache2 -k start
10056 33 /usr/sbin/apache2 -k start
$ /usr/bin/time docker stop test
test
real 0m 0.27s
user 0m 0.03s
sys 0m 0.03s
注釈
ENTRYPIONT
設定は --entrypoint
を使って上書きできますが、設定できるのはバイナリが実行可能な場合のみです( sh -c
が使われていない時のみ )。
注釈
exec 形式は JSON 配列でパースされます。つまり、語句はシングルクォート(‘)ではなく、ダブルクォート(”)で囲む必要があります。
注釈
シェル 形式とは異なり、 exec 形式はシェルを呼び出しません。つまり、通常のシェル上の処理はされません。例えば、 ENTRYPOINT ["echo", "$HOME"]
は $HOME
を変数展開しません。シェル上の処理が必要であれば、 シェル 形式を使うか、シェルを直接実行します。例: ENTRYPOINT [ "sh", "-c", "echo $HOME" ]
。変数は Dockerfile
で ENV
を使って定義することができ、 Dockerfile
パーサー上で展開されます。
シェル形式の ENTRYPOINT 例¶
ENTRYPOINT
に文字列を指定したら、 /bin/sh -c
で実行されます。この形式はシェルの処理を使いますので、シェル上の環境変数を展開し、 CMD
や docker run
コマンド行の引数を無視します。 docker stop
で ENTRYPOINT
で指定している実行ファイルにシグナルを送りたい場合は、 exec
を使う必要があるのを思い出してください。
FROM ubuntu
ENTRYPOINT exec top -b
このイメージを実行したら、単一の PID 1
プロセスが表示されます。
$ docker run -it --rm --name test top
Mem: 1704520K used, 352148K free, 0K shrd, 0K buff, 140368121167873K cached
CPU: 5% usr 0% sys 0% nic 94% idle 0% io 0% irq 0% sirq
Load average: 0.08 0.03 0.05 2/98 6
PID PPID USER STAT VSZ %VSZ %CPU COMMAND
1 0 root R 3164 0% 0% top -b
終了するには、 docker stop
を実行します。
$ /usr/bin/time docker stop test
test
real 0m 0.20s
user 0m 0.02s
sys 0m 0.04s
ENTRYPOINT
に exec
を追加し忘れたとします。
FROM ubuntu
ENTRYPOINT top -b
CMD --ignored-param1
次のように実行します(次のステップで名前を使います)。
$ docker run -it --name test top --ignored-param2
Mem: 1704184K used, 352484K free, 0K shrd, 0K buff, 140621524238337K cached
CPU: 9% usr 2% sys 0% nic 88% idle 0% io 0% irq 0% sirq
Load average: 0.01 0.02 0.05 2/101 7
PID PPID USER STAT VSZ %VSZ %CPU COMMAND
1 0 root S 3168 0% 0% /bin/sh -c top -b cmd cmd2
7 1 root R 3164 0% 0% top -b
top
の出力から、 ENTRYPOINT
が PID 1
ではないことが分かるでしょう。
それから docker stop test
を実行しても、コンテナはすぐに終了しません。これは stop
コマンドがタイムアウト後、SIGKILL
を強制送信したからです。
$ docker exec -it test ps aux
PID USER COMMAND
1 root /bin/sh -c top -b cmd cmd2
7 root top -b
8 root ps aux
$ /usr/bin/time docker stop test
test
real 0m 10.19s
user 0m 0.04s
sys 0m 0.03s
CMD と ENTRYPOINT がどのように作用するか学ぶ¶
CMD
と ENTRYPOINT
命令はコンテナ起動時に実行するコマンドを定義します。両方を記述する時、動作には複数のルールがあります。
- Dockerfile には少なくとも1つの
CMD
またはENTRYPOINT
命令を含むべきです。
ENTRYPOINT
は実行可能なコンテナとして定義する時に使うべきです。
- コンテナをアドホック(その場その場)で実行するコマンドを
ENTRYPOINT
にする場合、そのデフォルトの引数の指定としてCMD
を指定すべきです。
CMD
はコンテナ実行時に引数を指定すると上書します。
以下の表は ENTRYPOINT
/ CMD
を組み合わせたコマンドの実行結果です。
ENTRYPOINT なし | ENTRYPOINT exec_entry p1_entry | ENTRYPOINT [“exec_entry”, “p1_entry”] | |
---|---|---|---|
CMD なし | エラー。実行できない。 | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry |
CMD [“exec_cmd”, “p1_cmd”] | exec_cmd p1_cmd | /bin/sh -c exec_entry p1_entry exec_cmd p1_cmd | exec_entry p1_entry exec_cmd p1_cmd |
CMD [“p1_cmd”, “p2_cmd”] | p1_cmd p2_cmd | /bin/sh -c exec_entry p1_entry p1_cmd p2_cmd | exec_entry p1_entry p1_cmd p2_cmd |
CMD exec_cmd p1_cmd | /bin/sh -c exec_cmd p1_cmd | /bin/sh -c exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd | exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd |
VOLUME¶
VOLUME ["/data"]
VOLUME
命令は指定した名前でマウントポイントを作成し、他のホストやコンテナから外部マウント可能なボリュームにします。指定する値は VOLUME ["/var/log"]
といったJSON 配列になるべきです。あるいは文字列で VOLUME /var/log
や VOLUME /var/log /var/db
のように、複数の引数を書くこともできます。Docker クライアントを使ったマウント命令や詳しい情報やサンプルは ボリュームを経由してディレクトリを共有 をご覧ください。
docker run
コマンドは、ベース・イメージから指定した場所に、データを保存する場所として新規作成したボリュームを初期化します。例えば、次の Dockerfile をご覧ください。
FROM ubuntu
RUN mkdir /myvol
RUN echo "hello world" > /myvol/greeting
VOLUME /myvol
この Dockerfile によって作られたイメージは、 docker run
を実行したら、新しいマウント・ポイント /myvol
を作成し、greeting
ファイルを直近で作成したボリュームにコピーします。
注釈
構築ステップでボリューム内においてあらゆる変更を加えても、宣言後に内容は破棄されます。
注釈
リストは JSON 配列でパースされます。これが意味するのは、単語はシングルクォート(‘)で囲むのではなく、ダブルクォート(”)を使う必要があります。
USER¶
USER daemon
USER
命令セットはユーザ名か UID を使います。これはイメージを RUN
、 CMD
、 ENTRYPOINT
命令で実行時のものであり、 Dockerfile
で指定します。
WORKDIR¶
WORKDIR /path/to/workdir
WORKDIR
命令セットは Dockerfile
で RUN
、 CMD
、 ENTRYPOINT
、 COPY
、 ADD
命令実行時の作業ディレクトリ(working directory)を指定します。もし WORKDIR
が存在しなければ、 Dockerfile
命令内で使用しなくてもディレクトリを作成します。
1つの 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
になります。
ARG¶
ARG <名前>[=<デフォルト値>]
ARG
命令は、構築時に作業者が docker build
コマンドで使う変数、 --build-arg <変数名>=<値>
フラグを定義するものです。ユーザが構築時に引数を指定しても Dockerfile で定義されていなければ、構築時に次のようなエラーが出ます。
One or more build-args were not consumed, failing build.
Dockerfile の作者は ARG
変数を1度だけ定義するだけでなく、複数の ARG
を指定可能です。有効な Dockerfile の例:
FROM busybox
ARG user1
ARG buildno
...
Dockerfile の作者は、オプションで ARG
命令のデフォルト値を指定できます。
FROM busybox
ARG user1=someuser
ARG buildno=1
...
ARG
がデフォルト値を持っている場合、構築時に値の指定が無ければ、このデフォルト値を使います。
ARG
変数は Dockerfile
で記述した行以降で効果があります。ただし、コマンドライン上で引数の指定が無い場合です。次の Dockerfile の例を見てみましょう。
FROM busybox
USER ${user:-some_user}
ARG user
USER $user
...
$ docker build --build-arg user=what_user Dockerfile
2行めの USER
は some_user
を、3行めサブシーケントで定義された user
変数として評価します。4行めでは what_user
を USER
で定義したものと評価し、 what_user
値はコマンドラインで指定したものになります。 ARG
命令で定義するまで、あらゆる変数は空の文字列です。
注釈
構築時の変数として、GitHub の鍵やユーザの証明書などの秘密情報を含むのは、推奨される使い方ではありません。
ARG
や ENV
命令を RUN
命令のための環境変数にも利用できます。 ENV
命令を使った環境変数の定義は、常に同じ名前の ARG
命令を上書きします。Dockerfile における ENV
と ARG
命令を考えましょう。
FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER v1.0.0
RUN echo $CONT_IMG_VER
それから、イメージを次のように起動します。
$ docker build --build-arg CONT_IMG_VER=v2.0.1 Dockerfile
この例では、 RUN
命令は v1.0.0
の代わりに、 ARG
でユーザから渡された v2.0.1
を使います。この動作はシェルスクリプトの挙動に似ています。ローカルのスコープにある環境変数が、与えられた引数や上位の環境変数によって上書きするようなものです。
上記の ENV
指定の他にも、更に ARG
と ENV
を使いやすくする指定も可能です。
FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER ${CONT_IMG_VER:-v1.0.0}
RUN echo $CONT_IMG_VER
ARG
命令とは異なり、構築時の ENV
値は常に一定です。docker build で –build-arg フラグを使わない場合を考えてみましょう。
$ docker build Dockerfile
この Dockerfile の例では、 CONT_IMG_VER
はイメージの中では変わりませんが、3行めの ENV
命令でデフォルト値を設定することにより、値は v1.0.0
となります。
この例における変数展開のテクニックは、コマンドラインから引数を渡せるようにし、 ENV
命令を使うことで最終的に一貫したイメージを作成します。サポートされている変数展開は Dockerfile 命令の一部 のみです。
Docker は Dockerfile に対応する ARG
命令が無くても、既定の ARG
変数セットを持っています。
HTTP_PROXY
http_proxy
HTTPS_PROXY
https_proxy
FTP_PROXY
ftp_proxy
NO_PROXY
no_proxy
これらを使うには、コマンドラインで --build-arg <変数名>=<値>
フラグを単に渡すだけです。
構築キャッシュの影響¶
ARG
変数は、イメージ構築時の ENV
変数のように残り続けません。しかし、 ARG
変数は構築キャッシュで似たような方法として扱えます。もし Dockerfile で ARG
変数を定義したら、この値が以前の値と違う時は、以降で ARG
変数が出た時「キャッシュ・ミス」を発生します。これは、値を定義していなくても発生します。特に、すべての RUN
命令は ARG
変数を(環境変数から)暗黙的に使おうとするため、結果としてキャッシュ・ミスを引き起こします。
たとえば、2つの Dockerfile を考えます。
1 2 3 | FROM ubuntu
ARG CONT_IMG_VER
RUN echo $CONT_IMG_VER
|
1 2 3 | FROM ubuntu
ARG CONT_IMG_VER
RUN echo hello
|
--build-arg CONT_IMG_VER=<値>
をコマンドライン上で指定すると、どちらの場合も2行目はキャッシュ・ミスを引き起こします。さらに3行目もキャッシュ・ミスになります。 ARG CONT_IMG_VER
は RUN 行で CONT_IMG_VER=<値>
で echo hello と同じにしたいのであれば、 <値>
の編億がキャッシュ・ミスになります。
同じコマンド行で別の例を考えます。
1 2 3 4 | FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER $CONT_IMG_VER
RUN echo $CONT_IMG_VER
|
この例では、キャッシュミスが3行めで発生します。ミスが起こるのは ENV
変数が ARG
変数を参照しているのと、この変数がコマンドラインで変わるためです。例における ENV
コマンドはイメージの中で処理されるものです。
もし ENV
命令を同じ名前の ARG
命令で、次のように上書きしたらどうでしょう。
1 2 3 4 | FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER hello
RUN echo $CONT_IMG_VER
|
3行めはキャッシュミスを引き起こしません。 CONT_IMG_VAR
は固定( hello
)だからです。そのため、環境変数と値は RUN
(4行め)で使われますが、構築時に変わりません。
ONBUILD¶
ONBUILD [命令]
イメージは他で構築したイメージを元にしている時、ONBUILD
命令はイメージに対して最終的に実行する トリガ 命令を追加します。トリガは構築後に行うもので、 Dockerfile
で FROM
命令のあとに書くことができます。
あらゆる構築時の命令をトリガとして登録可能です。
これは他のイメージからイメージを構築する時に役立つでしょう。例えば、アプリケーションの開発環境やデーモンは、ユーザごとに設定をカスタマイズする可能性があります。
例えば、イメージが Python アプリケーション・ビルダーを再利用する時、アプリケーションのソースコードを適切なディレクトリに追加し、その後、構築スクリプトを実行することもあるでしょう。この時点では ADD
と RUN
を呼び出せません。なぜなら、まだアプリケーションのソースコードにアクセスしておらず、個々のアプリケーション構築によって異なるからです。アプリケーションの開発者は、ボイラープレートである Dockerfile
をコピーペーストでアプリケーションを入れるように編集するだけです。ですが、これは効率的ではなく、エラーを引き起こしやすく、アプリケーション固有のコードが混在することで更新が大変になります。
この解決方法として、 ONBLUID
を使い、実行後に別の構築ステージに進む上位命令を登録することです。
これは次のように動作します。
ONBUILD
命令が呼び出されると、ビルダーはイメージ構築時のメタデータの中にトリガを追加します。
- 構築が完了したら、全てのトリガはイメージのマニフェスト内の
OnBuild
キー配下に保管されます。この構築時点では、命令は何ら影響を与えません。
- このイメージは後で何らかのイメージの元になります。その時は
FROM
命令で呼び出されます。FROM
命令の処理の一部として、ダウンストリームのビルダーはONBULID
トリガを探し、登録された順番で実行します。もしトリガが失敗したら、FROM
命令は処理を中断し、ビルドを失敗とします。もし全てのトリガが成功したら、FROM
命令は完了し、以降は通常の構築が進みます。
- 実行する前に、最終的なイメージ上からトリガが削除されます。言い替えると構築された「孫」には、何ら親子関係がありません。
次のような例の記述を追加するでしょう。
[...]
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
[...]
警告
ONBUILD ONBUILD
命令を使って ONBULID
命令の上書きはできません。
STOPSIGNAL¶
STOPSIGNAL シグナル
STOPSIGNAL
命令は、コンテナを終了する時に送信するための、システム・コール・シグナルを設定します。シグナルはカーネルの syscall テーブルと一致する、有効な番号の必要があります。例えば、9 あるいはシグナル名 SIGNAME や、 SIGKILL などです。
HEALTHCHECK¶
HEALTHCHECK
命令は2つの形式があります:
HEALTHCHECK [オプション] CMD コマンド
(コンテナ内でコマンドを実行して、コンテナの正常性を確認)HEALTHCHECK NONE
(ベース・イメージからのヘルスチェック継承を無効化)
HEALTHCHECK
命令は、 Docker に対してコンテナの正常性をどのように確認(テスト)するかを伝えます。これはウェブ・サーバがループで塞がってしまい、新しい接続を受け付けられないような状態を検出できます。サーバプロセスが実行中でも、応答が無ければ検出します。
コンテナのヘルスチェック(healthcheck)を有効化すると、通常の状態に加え、ヘルス・ステータス(health status)を追加します。こちらの初期ステータスは starting
(起動中)です。ヘルスチェックが正常であれば、ステータスは(以前の状態にかかわらず) healthy
(正常)になります。特定回、連続して失敗したら、ステータスは unhealthy
(異常)になります。
CMD
より前に記述するオプションは、以下の通りです。
--interval=間隔
(デフォルト: 30s)--timeout=間隔
(デフォルト: 30s)--retries=N
(デフォルト: 3)
ヘルス・チェックは、まず最初の interval (間隔)秒の後、コンテナを起動します。そして interval 秒後に直近の確認を行います。
確認に timeout (タイムアウト)秒を越えるようであれば、確認は失敗とみなします。
コンテナに対するヘルスチェックが連続して失敗したら、コンテナは unhealthy
とみなします。
これらの処理は Dockerfile
で命令がある場合のみです。複数の HEALTHCHECK
があれば、最後の1つだけ有効です。
CMD
キーワード後のコマンドは、シェル・コマンド(例: HEALTHCHECK CMD /bin/check-running
)あるいは exec 配列(こちらは Dockerfile の他コマンドと同様です。例えば ENTRYPOINT
の詳細をご覧ください )です。
コマンドはコンテナのヘルス・ステータスの終了コードを検出できます。値は以下の通りです。
- 0: success(成功) - コンテナは正常であり、使う準備が整っています
- 1: unhealthy(障害) - コンテナは正常に動作していません
- 2: starting(起動中) - まだコンテナの利用準備が整っていませんが、正常に動作しています
監視結果が 2(”starting”)であれば、コンテナは起動しはじめており「起動中」の状態であり、「unhealthy」状態ではありません。
たとえば、5分ごとにウエブ・サーバがサイトのメインページを3秒以内に表示するかどうかを確認するには、次のように指定します。
HEALTHCHECK --interval=5m --timeout=3s \
CMD curl -f http://localhost/ || exit 1
監視失敗時はデバッグしやすくなるように、コマンド実行時の標準出力や標準エラー出力といった、あらゆる出力テキスト(UTF-8 エンコード)はヘルス・ステータスに格納され、 docker inspect
で確認可能です。この出力結果は短くして保存されます(現時点では始めから 4096 バイトのみ保存)。
コンテナのヘルス・ステータスが変われば、 health_status
イベントが新しいステータスを生成します。
HEALTHCHECK
機能は Docker 1.12 で追加されました。
SHELL¶
SHELL ["実行可能なファイル", "パラメータ"]
SHELL
命令は、シェル形式でコマンド実行時における、デフォルトのシェルを上書きします。 Linux 上でのデフォルトのシェルは ["/bin/sh", "-c"]
です。Windows は ["cmd", "/S", "/C"]
です。 SHELL
命令は Dockerfile で JSON 形式での記述が必要です。
SHELL
命令はとりわけ Windows で便利です。全く異なるネイティブなシェル cmd
と powershell
だけでなく、代わりのシェルとして sh
も指定できます。
SHELL
命令は複数回指定できます。 SHELL
命令ごとに、これまでの SHELL
命令を上書きし、以降の命令全てに反映します。例:
FROM windowsservercore
# cmd /S /C echo default として実行する
RUN echo default
# cmd /S /C powershell -command Write-Host default として実行する
RUN powershell -command Write-Host default
# powershell -command Write-Host hello として実行する
SHELL ["powershell", "-command"]
RUN Write-Host hello
# cmd /S /C echo hello として実行する
SHELL ["cmd", "/S"", "/C"]
RUN echo hello
Dockerfile の RUN
、 CMD
、ENTRYPOINT
のシェルは、 SHELL
命令以後にあれば影響を受けます。
次の例は Windows で一般的に見受けられるパターンですが、 SHELL
命令で簡素化できます。
...
RUN powershell -command Execute-MyCmdlet -param1 "c:\foo.txt"
...
このコマンドは、Docker によって次のように処理されます。
cmd /S /C powershell -command Execute-MyCmdlet -param1 "c:\foo.txt"
これが非効率なのは、2つの理由があります。1つは不要な cmd.exe プロセッサ(いわゆるシェル)が呼び出されること。もう1つは各 RUN
命令ごとに追加の powershell -command
コマンドが実行されるためです。
効率的にするには、2つの仕組みを採用します。1つは RUN 命令を次のように JSON 形式で使います。
...
RUN ["powershell", "-command", "Execute-MyCmdlet", "-param1 \"c:\\foo.txt\""]
...
JSON 形式は明確なものであり、不確実な cmd.exe を使いません。そのため、JSON 形式はダブル・クォートで囲み、エスケープするといった冗長な記述が必要です。他の方法としては、 SHELL
命令でシェル形式を使えば、Windows 利用者にとっても自然な構文になります。 escape
パーサ・ディレクティブと一緒に使えば尚更です。
# escape=`
FROM windowsservercore
SHELL ["powershell","-command"]
RUN New-Item -ItemType Directory C:\Example
ADD Execute-MyCmdlet.ps1 c:\example\
RUN c:\example\Execute-MyCmdlet -sample 'hello world'
実行結果:
PS E:\docker\build\shell> docker build -t shell .
Sending build context to Docker daemon 3.584 kB
Step 1 : FROM windowsservercore
---> 5bc36a335344
Step 2 : SHELL powershell -command
---> Running in 87d7a64c9751
---> 4327358436c1
Removing intermediate container 87d7a64c9751
Step 3 : RUN New-Item -ItemType Directory C:\Example
---> Running in 3e6ba16b8df9
Directory: C:\
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 6/2/2016 2:59 PM Example
---> 1f1dfdcec085
Removing intermediate container 3e6ba16b8df9
Step 4 : ADD Execute-MyCmdlet.ps1 c:\example\
---> 6770b4c17f29
Removing intermediate container b139e34291dc
Step 5 : RUN c:\example\Execute-MyCmdlet -sample 'hello world'
---> Running in abdcf50dfd1f
Hello from Execute-MyCmdlet.ps1 - passed hello world
---> ba0e25255fda
Removing intermediate container abdcf50dfd1f
Successfully built ba0e25255fda
PS E:\docker\build\shell>
SHELL
命令はシェルの実行者でも変更できます。たとえば Windows 上で SHELL cmd /S /C /V:ON|OFF
を使うと、環境変数の遅延拡張セマンティクス(delayed environment variable expansion semantics)を変更できます。
SHELL
命令は Linux 上でも利用できます。 zsh
、 csh
、tcsh
など別のシェルを指定できます。
SHELL
機能は Docker 1.12 で追加されました。
Dockerfile の例¶
以下で Dockerfile 構文の例を参照できます。実際の環境に興味があれば、 Docker 化の例 をご覧ください。
# Nginx
#
# VERSION 0.0.1
FROM ubuntu
MAINTAINER Victor Vieux <victor@docker.com>
LABEL Description="This image is used to start the foobar executable" Vendor="ACME Products" Version="1.0"
RUN apt-get update && apt-get install -y inotify-tools nginx apache2 openssh-server
# Firefox over VNC
#
# VERSION 0.3
FROM ubuntu
# 「フェイク」(偽)のディスプレイ用の vnc, xvfb と firefox をインストール
RUN apt-get update && apt-get install -y x11vnc xvfb firefox
RUN mkdir ~/.vnc
# パスワードをセットアップ
RUN x11vnc -storepasswd 1234 ~/.vnc/passwd
# firefox の自動起動(ベストな方法ではありませんが、動きます)
RUN bash -c 'echo "firefox" >> /.bashrc'
EXPOSE 5900
CMD ["x11vnc", "-forever", "-usepw", "-create"]
# 複数のイメージ例
#
# VERSION 0.1
FROM ubuntu
RUN echo foo > bar
# 「===> 907ad6c2736f」 のような出力があります
FROM ubuntu
RUN echo moo > oink
# 「===> 695d7793cbe4」 のような出力があります
# You᾿ll now have two images, 907ad6c2736f with /bar, and 695d7793cbe4 with
# /oink.
# これで2つのイメージができました。
# /bar がある 907ad6c2736f と、/oink がある 695d7793cbe4 です
参考
- Dockerfile reference
- https://docs.docker.com/engine/reference/builder/