Dockerfile リファレンス

Docker は Dockerfile から命令を読み込み、自動的にイメージを構築できます。 Dockerfile はテキスト形式のドキュメントであり、コマンドライン上でイメージを作り上げる命令を全て記述します。ユーザは docker build を使い、複数のコマンド行の命令を順次実行し、イメージを自動構築します。

このページでは Dockerfile 内で利用可能な命令を説明します。ページを読み終えたら、より便利に使うための Dockerfileベスト・プラクティス もご覧ください。

使い方

docker build コマンドは Dockerfileコンテクスト(context;イメージに含まれる「内容」の意味) に従ってイメージを構築します。構築用コンテクストとは、ファイルを示す PATHURL の場所です。 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 の命令を複数行に記述できます。 Dockerfileescape パーサ・ディレクティブを指定しなければ 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 パーサ・ディレクティブを追加することで、次の DockerfileWindows 上のファイル・パスを期待通りに処理します。

# 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 になります。これは abcbye に設定するのと同じコマンド行ではないためです。

.dockerignore ファイル

docker CLI がコンテクストを docker デーモンに送る前に、コンテクストのルートディレクトリ内の .dockerignore ファイルを探します。もしファイルが存在していれば、CLI はコンテクストからパターンに一致するファイルとディレクトリを除外します。これは不必要に大きくならないようにします。また、取り扱いに注意が必要なファイルやディレクトリをデーモンに送らないようにします。ですが、 ADDCOPY でイメージに追加されるかもしれません。

CLI は .dockerignore ファイルを行ごとに隔てて解釈します。行の一致パターンは Unix シェル上のものに似ています。パターンがコンテクストの root に一致すると考えられる場合は、root ディレクトリとして動作します。例えば、パターン /foo/barfoo/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 ファイルの除外にも使えます。それでも、これらのファイルはジョブを処理するためデーモンに送信されます。しかし ADDCOPY コマンドは、これらをイメージ内にコピーしません。

最後に、特定のファイルのみコンテクストに含め、他を除外したい場合があるでしょう。実行するには、始めに * パターンに指定し、以下1つまたは複数の ! 例外パターンを記述します。

注釈

歴史的な理由により、 . パターンは無視されます。

FROM

FROM <イメージ>

または

FROM <イメージ>:<タグ>

または

FROM <イメージ>@<digest>

FROM 命令は、以降の命令で使う ベース・イメージ を指定します。あるいは、有効な Dockerfile は、1行めを FROM 命令で指定する必要があります。イメージとは、あらゆる有効なものが利用できます。 パブリック・リポジトリ から イメージを取得する 方法が一番簡単です。

  • Dockerfile では、コメント以外では FROM を一番始めに書く必要があります。
  • 単一の Dockerfile から複数のイメージを作成するため、複数の FROM を指定できます。各 FROM 命令ごとに自動的にコミットし、最新のイメージ ID が出力されるのを覚えておいてください。
  • タグdigest 値はオプションです。省略した場合、ビルダーはデフォルトの latest とみなします。ビルダーは一致する tag 値が無ければエラーを返します。

MAINTAINER

MAINTAINER <名前>

MAINTAINER 命令は、生成するイメージの Author (作者)フィールドを指定します。

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 <コマンド> (シェル形式)

DockerfileCMD 命令を一度だけ指定できます。複数の CMD がある場合、最も後ろの CMD のみ有効です。

CMD の主な目的は、 コンテナ実行時のデフォルトを提供します 。 デフォルトには、実行可能なコマンドが含まれているか、あるいは省略されるかもしれません。省略時は ENTRYPOINT 命令で同様に指定する必要があります。

注釈

ENTRYPOINT 命令のデフォルトの引数として CMD を使う場合、 CMDENTRYPOINT 命令の両方が 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"]

もしコンテナで毎回同じものを実行するのであれば、 CMDENTRYPOINT の使用を検討ください。詳細は ENTRYPOINT をご覧ください。

ユーザが docker run で引数を指定した時、これらは CMD で指定したデフォルトを上書きします。

注釈

RUNCMD を混同しないでください。 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 wgetRUN 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 と同じ働きをします。結果は次の処理を同時に行います。
  1. 送信先のパスが存在しているかどうか
  2. ファイル単位の原則に従って、ソース・ツリーの内容と衝突しないかどうか「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 フラグを使います。

シェル 形式では CMDrun コマンド行の引数を使えないという不利な点があります。 ENTRYPOINT/bin/sh -c のサブコマンドとして実行されるため、シグナルを渡せません。つまり、何かを実行してもコンテナの PID 1 にはなりません。そして、 Unix シグナルを受け付け ません。そのため、実行ファイルは docker stop <コンテナ> を実行しても、 SIGTERM を受信しません。

なお、 Dockerfile の最後に現れた ENTRYPOINT 命令のみ有効です。

exec 形式の ENTRYPOINT 例

ENTRYPOINTexec 形式を使い、適切なデフォルトのコマンドと引数を指定します。それから 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 を停止するよう、通常のリクエストを行えます。

次の DockerfileENTRYPOINT を使って 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 シグナルを受信できるよう、 execgosu コマンドを使うことで可能になります。

#!/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 execdocker 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" ]。変数は DockerfileENV を使って定義することができ、 Dockerfile パーサー上で展開されます。

シェル形式の ENTRYPOINT 例

ENTRYPOINT に文字列を指定したら、 /bin/sh -c で実行されます。この形式はシェルの処理を使いますので、シェル上の環境変数を展開し、 CMDdocker run コマンド行の引数を無視します。 docker stopENTRYPOINT で指定している実行ファイルにシグナルを送りたい場合は、 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

ENTRYPOINTexec を追加し忘れたとします。

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 の出力から、 ENTRYPOINTPID 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 がどのように作用するか学ぶ

CMDENTRYPOINT 命令はコンテナ起動時に実行するコマンドを定義します。両方を記述する時、動作には複数のルールがあります。

  1. Dockerfile には少なくとも1つの CMD または ENTRYPOINT 命令を含むべきです。
  1. ENTRYPOINT は実行可能なコンテナとして定義する時に使うべきです。
  1. コンテナをアドホック(その場その場)で実行するコマンドを ENTRYPOINT にする場合、そのデフォルトの引数の指定として CMD を指定すべきです。
  1. 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/logVOLUME /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 を使います。これはイメージを RUNCMDENTRYPOINT 命令で実行時のものであり、 Dockerfile で指定します。

WORKDIR

WORKDIR /path/to/workdir

WORKDIR 命令セットは DockerfileRUNCMDENTRYPOINTCOPYADD 命令実行時の作業ディレクトリ(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行めの USERsome_user を、3行めサブシーケントで定義された user 変数として評価します。4行めでは what_userUSER で定義したものと評価し、 what_user 値はコマンドラインで指定したものになります。 ARG 命令で定義するまで、あらゆる変数は空の文字列です。

注釈

構築時の変数として、GitHub の鍵やユーザの証明書などの秘密情報を含むのは、推奨される使い方ではありません。

ARGENV 命令を RUN 命令のための環境変数にも利用できます。 ENV 命令を使った環境変数の定義は、常に同じ名前の ARG 命令を上書きします。Dockerfile における ENVARG 命令を考えましょう。

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 指定の他にも、更に ARGENV を使いやすくする指定も可能です。

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 命令はイメージに対して最終的に実行する トリガ 命令を追加します。トリガは構築後に行うもので、 DockerfileFROM 命令のあとに書くことができます。

あらゆる構築時の命令をトリガとして登録可能です。

これは他のイメージからイメージを構築する時に役立つでしょう。例えば、アプリケーションの開発環境やデーモンは、ユーザごとに設定をカスタマイズする可能性があります。

例えば、イメージが Python アプリケーション・ビルダーを再利用する時、アプリケーションのソースコードを適切なディレクトリに追加し、その後、構築スクリプトを実行することもあるでしょう。この時点では ADDRUN を呼び出せません。なぜなら、まだアプリケーションのソースコードにアクセスしておらず、個々のアプリケーション構築によって異なるからです。アプリケーションの開発者は、ボイラープレートである Dockerfile をコピーペーストでアプリケーションを入れるように編集するだけです。ですが、これは効率的ではなく、エラーを引き起こしやすく、アプリケーション固有のコードが混在することで更新が大変になります。

この解決方法として、 ONBLUID を使い、実行後に別の構築ステージに進む上位命令を登録することです。

これは次のように動作します。

  1. ONBUILD 命令が呼び出されると、ビルダーはイメージ構築時のメタデータの中にトリガを追加します。
  1. 構築が完了したら、全てのトリガはイメージのマニフェスト内の OnBuild キー配下に保管されます。この構築時点では、命令は何ら影響を与えません。
  1. このイメージは後で何らかのイメージの元になります。その時は FROM 命令で呼び出されます。 FROM 命令の処理の一部として、ダウンストリームのビルダーは ONBULID トリガを探し、登録された順番で実行します。もしトリガが失敗したら、 FROM 命令は処理を中断し、ビルドを失敗とします。もし全てのトリガが成功したら、 FROM 命令は完了し、以降は通常の構築が進みます。
  1. 実行する前に、最終的なイメージ上からトリガが削除されます。言い替えると構築された「孫」には、何ら親子関係がありません。

次のような例の記述を追加するでしょう。

[...]
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 で便利です。全く異なるネイティブなシェル cmdpowershell だけでなく、代わりのシェルとして 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 の RUNCMDENTRYPOINT のシェルは、 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 上でも利用できます。 zshcshtcsh など別のシェルを指定できます。

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 です