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 '何か良いものを # で実行しています'

ここでは、 Dockerfile でイメージ構築時に利用可能な命令セットを紹介します。

環境変数の置き換え

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 ファイルの例です:

*/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 <コマンド> (シェル形式、コマンドをシェル /bin/sh -c で実行する)
  • RUN ["実行バイナリ", "パラメータ1", "パラメータ2"]exec 形式)

RUN 命令は既存イメージ上の新しいレイヤで、あらゆるコマンドを実行し、その結果をコミットする命令です。コミットの結果得られたイメージは、 Dockerfile の次のステップで使われます。

RUN 命令の積み重ねとコミットによるイメージ生成は、 Docker の中心となるコンセプト(概念)に従ったものです。コミットは簡単であり、ソース・コントロールのように、イメージの履歴上のあらゆる場所からコンテナを作成可能です。

exec 形式はシェルの文字列を変更できないようにします。また、 /bin/sh がベース・イメージに含まれなくても RUN コマンドを使えます。

シェル 形式では、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" ]

次の構築時、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.Mach ルールに一致するパターンが使えます。例えば、次のような記述です。

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.Mach ルールに一致するパターンが使えます。例えば、次のような記述です。

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 KILL 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 変数が出た時「キャッシュ・ミス」を発生します。

1 FROM ubuntu
2 ARG CONT_IMG_VER
3 RUN echo $CONT_IMG_VER
1 FROM ubuntu
2 ARG CONT_IMG_VER
3 RUN echo hello
1 FROM ubuntu
2 ARG CONT_IMG_VER
3 ENV CONT_IMG_VER $CONT_IMG_VER
4 RUN echo $CONT_IMG_VER

この例では、キャッシュミスが3行めで発生します。ミスが起こるのは ENV 変数が ARG 変数を参照しているのと、この変数がコマンドラインで変わるためです。例における ENV コマンドはイメージの中で処理されるものです。

もし ENV 命令を同じ名前の ARG 命令で、次のように上書きしたらどうでしょう。

1 FROM ubuntu
2 ARG CONT_IMG_VER
3 ENV CONT_IMG_VER hello
4 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 などです。

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