Dockerfile リファレンス¶
Docker は Dockerfile
から命令を読み込み、自動的にイメージをビルドできます。 Dockerfile
はテキストファイルであり、イメージを作り上げるために実行するコマンドライン命令を、すべてこのファイルに含められます。 docker build
を実行すると、順次コマンドライン命令を自動化した処理を行い、ビルド結果となるイメージが得られます。
このページでは、 Dockerfile
で利用可能なコマンドを説明します。このページを読み終えたら、 Dockerfile
の ベストプラクティス を開き、理解を重視するガイドをご覧ください。
使用法¶
docker build コマンドは、 Dockerfile
と PATH
(場所)または URL
にあるファイル一式です。 PATH
はローカル・ファイルシステム上のディレクトリです。 URL
は Git リポジトリの場所です。
PATH
にはすべてのサブディレクトリが含まれ、 URL
にはリポジトリとサブモジュールが含まれます。以下の例における構築コマンドは、現在のディレクトリ( .
)を構築コンテキストとして使用します。
$ docker build .
Sending build context to Docker daemon 6.51 MB
...
構築は Docker デーモンが実行するものであり、 CLI によるものではありません。構築処理でまず行われるのは、コンテキスト全体を(再帰的に)デーモンに送信します。たいていの場合、コンテキストとして空のディレクトリを用意し、そこに Dockerfile を置くのがベストです。そのディレクトリへは、Dockerfile の構築に必要なファイルのみ追加します。
警告
ルート・ディレクトリ /
を構築コンテキストの PATH
として指定しないでください。構築時、ハードディスクの内容すべてを Docker デーモンに転送してしまうからです。
構築コンテキスト内にあるファイルを使う場合、 COPY
命令など、 Dockerfile
の命令で指定されたファイルを参照します。構築時の処理性能を上げるには、コンテキスト・ディレクトリに .dockerignore
ファイルを追加し、不要なファイルやディレクトリを除外します。 .dockerignore
ファイルを作成する詳細は、このページ内の ドキュメント を参照ください。
もともと、 Dockerfile
は Dockerfile
と呼ばれ、コンテキストのルート(対象ディレクトリのトップ)に置かれました。 docker build
で -f
フラグを使えば、Dockerfile がファイルシステム上のどこにあっても指定できます。
$ docker build -f /path/to/a/Dockerfile .
構築の成功時、新しいイメージを保存する
$ docker build -t shykes/myapp .
構築後、複数のリポジトリに対してイメージをタグ付けするには、 build
コマンドの実行時、複数の -t
パラメータを追加します。
docker build -t shykes/myapp:1.0.2 -t shykes/myapp:latest .
Docker デーモンは Dockerfile
内に書かれた命令を実行する前に、事前に Dockerfile
を検証し、構文が間違っている場合はエラーを返します。
$ docker build -t test/myapp .
[+] Building 0.3s (2/2) FINISHED
=> [internal] load build definition from Dockerfile 0.1s
=> => transferring dockerfile: 60B 0.0s
=> [internal] load .dockerignore 0.1s
=> => transferring context: 2B 0.0s
error: failed to solve: rpc error: code = Unknown desc = failed to solve with frontend dockerfile.v0: failed to create LLB definition:
dockerfile parse error line 2: unknown instruction: RUNCMD
Docker デーモンは Dockerfile
内の命令を 1 つずつ実行し、必要な場合にはビルドイメージ内にその処理結果を
各命令は個別に実行され、都度、新しいイメージが生成されますのでご注意ください。したがって、たとえば RUN cd /tmp
という命令があっても、その次の命令には何ら影響を与えません。
Docker は可能な限り docker build
の処理を著しく高速にします。その場合はコンソール出力に CACHED
というメッセージが出ます。(詳細については、 Dockerfile のベストプラクティスガイド を参照ください。)
$ docker build -t svendowideit/ambassador .
[+] Building 0.7s (6/6) FINISHED
=> [internal] load build definition from Dockerfile 0.1s
=> => transferring dockerfile: 286B 0.0s
=> [internal] load .dockerignore 0.1s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/alpine:3.2 0.4s
=> CACHED [1/2] FROM docker.io/library/alpine:3.2@sha256:e9a2035f9d0d7ce 0.0s
=> CACHED [2/2] RUN apk add --no-cache socat 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:1affb80ca37018ac12067fa2af38cc5bcc2a8f09963de 0.0s
=> => naming to docker.io/svendowideit/ambassador 0.0s
構築キャッシュとは、デフォルトでは、構築するマシン上で以前に構築された結果に基づきます。 --cache-from
オプションの指定により、イメージ・レジストリを通して配布された構築キャッシュも使えます。 docker build
コマンドリファレンスの 外部のキャッシュをソースとして指定 セクションをご覧ください。
構築が終われば、 docker scan
で イメージを検査 したり、 Docker Hub にイメージを送信 したりできます。
BuildKit ¶
バージョン 18.09 から、Docker は moby/buildkit プロジェクトによって提供された、新しい構築用バックエンドをサポートしています。古い実装に比べ、BuildKit バックエンドは多くの利点があります。たとえば、 BuildKit は次のことができます。
使用していない 構築ステージ の検出とスキップ
独立している構築ステージを
並列構築 構築コンテキストと構築の間では、変更のあったファイルのみ転送
構築コンテキスト内で、未使用ファイルの検出と、転送のスキップ
多くの新機能がある
拡張 Dockerfile 実装 を使用他の API (中間イメージとコンテナ)による副作用を回避
自動整理 のために、構築キャッシュを優先度付け
BuildKit バックエンドを使うには、 docker build
を実行する前に、CLI 上で環境変数 DOCKER_BUILDKIT=1
を設定する必要があります。
BuildKit を使った構築時に有効となる、拡張 Dockerfile 実装についての詳細を知るには、 BuildKit リポジトリにあるドキュメントを参照ください 。
書式¶
Dockerfile
の書式は次の通りです。
# コメント
命令 引数
Docker は Dockerfile
内の命令を記述順に実行します。 Dockerfile
は必ず FROM
命令で始めなければなりません。 ただし、 パーサ・ディレクティブ 、 コメント 、全体に適用される ARG の後になる場合があります。 FROM
命令で指定するのは、構築時に元となる 親イメージ です。 Dockerfile
の中で、 FROM
行の ARG
命令は、 FROM
よりも前に記述できる唯一の命令です。
Docker は #
で始まる行をコメントとして扱います。ただし、 パーサ・ディレクティブ は例外です。また、行の途中にある #
は単なる引数として扱います。次のような記述ができます。
# コメント
RUN echo 'we are running some # of cool things'
Dockerfile で命令を実行する前に、コメント行は削除されます。つまり、以下の例にあるコメントは echo
コマンドのシェル実行では扱われず、以下両方の例は同じものです。
RUN echo hello \
# コメント
world
RUN echo hello \
world
なお、コメント中では
注釈
空白についての注意
後方互換性のため、コメント( #
)と( RUN
のような)命令よりも前の空白を無視しますが、お勧めしません。以下のような例では、先頭の空白は保持されないため、どちらも同じものです。
# これはコメント行です
RUN echo hello
RUN echo world
# this is a comment-line
RUN echo hello
RUN echo world
ただし注意が必要なのは、以下の RUN
命令のように、命令に対する引数の空白は保持されます。そのため、以下の例では、先頭に空白が指定した通りある「 hello world」を表示します。
RUN echo "\
hello\
world"
パーサ・ディレクティブ ¶
Dockerfile
で指定すると、以降の行での挙動に影響を与えます。パーサ・ディレクティブは構築時にレイヤを追加しないため、構築ステップで表示されません。パーサ・ディレクティブは # ディレクティブ(命令の名前)=値
という形式の、特別なタイプのコメントとして書きます。1つのディレクティブ(命令)は一度しか使えません。
コメントや空行、構築命令を処理すると、Docker はパーサ・ディレクティブを探さなくなります。そのかわり、パーサ・ディレクティブの記述はコメントとして扱われ、パーサ・ディレクティブかどうかは確認しません。そのため、すべてのパーサ・ディレクティブは Dockerfile
の一番上に書く必要があります。
パーサ・ディレクティブは大文字と小文字を区別しません。しかし、小文字を使うのが慣例です。他にも慣例として、パーサ・ディレクティブの次行は空白にします。パーサ・ディレクティブ内では、
これらの規則があるため、次の例はどれも無効です。
(バックスラッシュを使った)行の継続はできません:
# ディレク \
ティブ=値
(ディレクティブが)二度出現するため無効:
# ディレクティブ=値1
# ディレクティブ=値2
FROM イメージ名
構築命令の後に(パーサ・ディレクティブが)あっても、コメントとして扱う:
FROM ImageName
# ディレクティブ=値
コメントの後にパーサ・ディレクティブあれば、(単なる)コメントとして扱う:
# dockerfile についての説明
# ディレクティブ=値
FROM ImageName
不明なディレクティブは認識できないため、コメントとして扱う。さらに、パーサ・ディレクティブではないコメントの後にディレクティブがあっても、(命令としてではなく)コメントとして扱う:
# 不明な命令=値
# 正しい命令=値
パーサ・ディレクティブでは、改行ではない空白(スペース)を書けます。そのため、以下の各行はすべて同じように扱われます。 そこで、以下の各行はすべて同一のものとして扱われます。
#directive=value
# directive =value
# directive= value
# directive = value
# dIrEcTiVe=value
以下のパーサ・ディレクティブをサポートしています。
syntax
escape
syntax¶
# syntax=[リモート・イメージ・リファレンス]
例:
# syntax=docker/dockerfile:1
# syntax=docker.io/docker/dockerfile:1
# syntax=example.com/user/repo:tag@sha256:abcdef...
この機能は、 BuildKit バックエンドを利用時のみ使えます。そのため、古い構築バックエンドの利用時には、無視されます。
syntax ディレクティブ(命令)では、対象の Dockerfile が構築時に使う、 Dockerfile
カスタム Dockerfile 実装により、次のことが可能になります。
Docker デーモンを更新しなくても、自動的にバグ修正をする
すべての利用者が確実に同じ実装を使い、Dockerfile で構築する
Docker デーモンを更新しなくても、最新機能を使う
Docker デーモンに統合前の、新機能やサードパーティ機能を試す
公式リリース¶
stable チャンネルは セマンティック・バージョニング に従います。たとえば、
docker/dockerfile:1
- 最新の1.x.x
マイナー および パッチ・リリースが更新され続けるdocker/dockerfile:1.2
- 最新の1.2.x
パッチ・リリースが更新され続けますが、1.3.0
バージョンがリリースされると更新停止docker/dockerfile:1.2.1
-変わりません :決して更新しない
私たちは docker/dockerfile:1
の使用を推奨します。これは、常にバージョン 1
1.2
や 1.2.1
のようなバージョンを指定すると、バグ修正や新機能を利用するには、 Dockerfile を手動で更新する必要があります。Dockerfile の古いバージョンは、
labs チャンネル¶
「labs」チャンネルが提供するのは、まだ stable チャンネルでは利用できない、 Dockerfile機能に対する早期アクセスです。Labs チャンネル・イメージは stable リリースと連携しています。同じバージョンに -labs
文字が付きます。たとえば、
docker/dockerfile:labs
- labs チャンネルの最新リリースdocker/dockerfile:1-labs
- stable チャンネルのdockerfile:1
と同じで、labs 機能が有効化docker/dockerfile:1.2-labs
- stable チャンネルのdockerfile:1.2
と同じで、labs 機能が有効化docker/dockerfile:1.2.1-labs
-変わりません :決して更新しない。 stable チャンネルのdockerfile:1.2.1
と同じで、labs 機能が有効化
必要に応じて最適なチャンネルを選びます。新機能を活用したい場合は labs チャンネルを使います。labs チャンネルが提供するイメージは、stable チャンネルの stable
機能は セマンティック・バージョニング に従います。しかし「labs」機能は従いません。また、新しいリリースは下位互換性がない可能性もあるため、バージョンが固定されたフルバージョンでの指定をお勧めします。
「labs」機能、
escape¶
# escape=\ (バックスラッシュ)
または
# escape=` (バッククォート)
Dockerfile
内で文字を escape
命令で指定します。指定がなければ、デフォルトのエスケープ文字は \
です。
エスケープ文字は、行の中で文字をエスケープするのに使う場合と、改行をエスケープする(改行文字として使う)場合があります。これにより、 Dockerfile
の命令は、複数の行に書けます。注意点としては、 Dockerfile
で escape
パーサ・ディレクティブの有無にかかわらず、 RUN
命令ではエスケープされませんが、行末のみ(改行文字として)使用できます。
Windows
上では「 \
」がディレクトリ・パスの区切り文字のため、エスケープ文字として「`」 を指定すると、とても使いやすいでしょう。 Windows PowerShell 上でも、「`」はエスケープ文字列として扱います。
以下にあるような、一見すると分かりづらい Windows
上での失敗例を考えます。2行目の行末にある、2つめの \
は、1つめの \
のエスケープ対象ではなく、改行(を表すエスケープ文字)として解釈されます。同じように、3行目の行末にある \
も、実際には命令として処理され、改行として扱われます。結果として、この Dockerfile では2行目と3行目は1つの命令と見なされます。
FROM microsoft/nanoserver
COPY testfile.txt c:\\
RUN dir c:\
結果:
PS E:\myproject> docker build -t cmd .
Sending build context to Docker daemon 3.072 kB
Step 1/2 : FROM microsoft/nanoserver
---> 22738ff49c6d
Step 2/2 : COPY testfile.txt c:\RUN dir c:
GetFileAttributesEx c:RUN: The system cannot find the file specified.
PS E:\myproject>
これを解決する方法の1つは、COPY
命令と dir
の両方で、対象に対して /
文字を使うものです。これはベストですが、 Windows
上のパスとしては自然ではないため、混乱します。また、困ったことに、 Windows
上でコマンドすべてが /
をパスの区切り文字としてサポートしておらず、エラーが発生しがちです。
次の Dockerfile
は escape
パーサ・ディレクティブの追加により、 Windows
上のファイルやパスを通常通りの構文として扱えるようになります(補足説明:デフォルトでは「 \
」が改行文字として扱われています。あえてエスケープ文字を「`」と明示すると、特に「 \
」がエスケープ文字かどうか考慮する必要がなくなり、パスの指定として「 C:\
」の記述がそのまま扱えるようになります)。
# escape=`
FROM microsoft/nanoserver
COPY testfile.txt c:\
RUN dir c:\
結果:
PS E:\myproject> docker build -t succeeds --no-cache=true .
Sending build context to Docker daemon 3.072 kB
Step 1/3 : FROM microsoft/nanoserver
---> 22738ff49c6d
Step 2/3 : COPY testfile.txt c:\
---> 96655de338de
Removing intermediate container 4db9acbb1682
Step 3/3 : RUN dir c:\
---> Running in a2c157f842f5
Volume in drive C has no label.
Volume Serial Number is 7E6D-E0F7
Directory of c:\
10/05/2016 05:04 PM 1,894 License.txt
10/05/2016 02:22 PM <DIR> Program Files
10/05/2016 02:14 PM <DIR> Program Files (x86)
10/28/2016 11:18 AM 62 testfile.txt
10/28/2016 11:20 AM <DIR> Users
10/28/2016 11:20 AM <DIR> Windows
2 File(s) 1,956 bytes
4 Dir(s) 21,259,096,064 bytes free
---> 01c7f3bef04f
Removing intermediate container a2c157f842f5
Successfully built 01c7f3bef04f
PS E:\myproject>
環境変数の置き換え¶
( ENV 命令 で宣言する ) Dockerfile
で特定の命令に対する変数としての使用もできます。また、エスケープを使えば、変数のような構文も、命令文の文字列に入れられます。
Dockerfile
内での環境変数は、 $variable_name
または ${variable_name}
として書きます。どちらも同等に扱われます。中括弧( { }
)を使う書き方は、 ${foo}_bar
のように空白を使わず、変数名に割り当てる(ための変数)として通常は使います。
${variable_name}
の書き方は、次のような(変数展開するための)標準的 bash
${variable:-word}
…variable
(「変数」として何らかの値)が設定されていれば、結果はその(変数が)値になる。variable
(変数)が指定されていなければ、word
が値として設定される。${variable:+word}
…variable
(「変数」として何らかの値)が設定されていれば、結果はword
が(変数の)値になり、それ以外の場合(変数の設定がない場合)は空の文字列になる。
どちらの例でも、(変数の中にある) word
には任意の文字列を指定できますし、環境変数を追加した文字列も指定できます。
変数の前に \
を追加してエスケープできます。たとえば、 \$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
FROM
LABEL
STOPSIGNAL
USER
VOLUME
WORKDIR
ONBUILD
(以上の命令との組み合わせでサポートされます)
環境変数を置き換えでは、各命令(の行)全体を処理する間は、各変数は同じ値です。すなわち、この例では
ENV abc=hello
ENV abc=bye def=$abc
ENV ghi=$abc
結果、 def
の値は hello
であり、 bye
ではありません。しかし、 ghi
の値は bye
です。なぜなら、(変数) abc
に bye
を指定する命令の行と、この ghi
の命令の行が違うからです。(補足説明: ENV abc=bye def=$abc
の命令文の処理が終わるまでは $abc
は hello
のまま。この命令の処理が終わると、 $abc
は bye
になる。そのため、次の命令分 ghi=$abc
で、 $abc
の値 bye
が変数 ghi
に入る )
.dockerignore ファイル¶
Docker CLI が .dockerignore
という名前のファイルを探します。このファイルが存在する場合、CLI はファイル内で書かれたパターンに一致するファイルやディレクトリを、コンテクストから除外します。これにより、容量が大きいか機微情報を含むファイルやディレクトリを、不用意にデーモンに送信するのを回避したり、 ADD
や COPY
を使ってイメージに加えてしまうのも回避したりするのに役立ちます。
CLI は .dockerignore
ファイルを、改行で区切られたパターンの一覧として解釈します。パターンとは、Unix シェルの /foo/bar
と foo/bar
のパターンでは、どちらも PATH
、もしくは git リポジトリの場所を示す URL
のルート以下で、 foo
サブディレクトリ内の bar
という名前のファイルかディレクトリを除外します。それ以外は除外しません。
.dockerignore
ファイルでは、 #
記号で始まる行はコメントとみなされ、 CLI によって解釈される前に無視されます。
これは .dockerignore
ファイルの例です:
# コメント
*/temp*
*/*/temp*
temp?
このファイルは構築時に以下の挙動をします。
ルール |
挙動 |
---|---|
|
無視する。 |
|
root 以下のあらゆるサブディレクトリ内で、 |
|
root から2階層以下のサブディレクトリ内で、 |
|
ルートディレクトリ内で、 |
一致には Go 言語の filepath.Match を使います。前処理として、前後の空白を削除し、.
と ..
要素を削除するのに Go 言語の filepath.Clean を使います。前処理により、空白になった行は無視されます。
Go 言語の filepath.Match ルールを拡張し、 Docker は特別なワイルドカード文字列 **
もサポートします。これは、(0も含む)複数のディレクトリに一致します。たとえば、 **/*.go
は .go
で終わるすべてのファイルを除外します。つまり、構築コンテクストのルートも含む、全てのディレクトリが対象です。
!
(エクスクラメーション・マーク)で始まる行は、除外対象の例外を指定します。次の .dockerignore
ファイル例では、この仕組みを使っています:
*.md
!README.md
コンテクストから README.md
を 例外として除き 、その他すべてのマークダウンファイルを除外します。
!
による除外に対する例外ルールは、他の挙動にも影響します。 .dockerignore
ファイルに書かれた最終行によって、特定のファイルが除外されるかどうかが決まるります。次の例を考えます:
*.md
!README*.md
README-secret.md
README-secret.md
以外の README ファイル( README*.md
)は例外として除外されませんが、その他のマークダウンファイル( *.md
)はコンテキストに含まれません。
次は、こちらの例を考えます:
*.md
README-secret.md
!README*.md
すべての README ファイルが含まれます(除外されません)。 !README*.md
は README-secret.md
と一致し、かつ最後の行にあるので、真ん中の行は無意味です。
.dockerignore
ファイルを使い、 Dockerfile
と .dockerignore
ファイルすら除外できます。除外設定をしても、これらのファイルは構築処理で必要なため、デーモンに送信されます。ですが、 ADD
と COPY
命令で、これらのファイルをイメージにコピーしません。
ほかには、コンテクスト内に特定のファイルを除外するのではなく、入れたいファイルを指定したい場合もあるでしょう。そのためには、1つめのパターンとして *
を指定し(一度、全てを除外する)、以降の行では !
で例外とするパターンを指定します。
注釈
これまでの経緯により、パターン .
は無視されます。
FROM¶
FROM [--platform=<プラットフォーム>] <イメージ名> [AS <名前>]
または
FROM [--platform=<プラットフォーム>] <イメージ名>[:<タグ>] [AS <名前>]
または
FROM [--platform=<プラットフォーム>] <イメージ名>[@<ダイジェスト>] [AS <名前>]
FROM
命令は、新しい Dockerfile
とは FROM
命令で始める必要があります。イメージとは、適切なイメージであれば何でも構いません。 公開リポジトリ から イメージを取得して始める のが、特に簡単です。
Dockerfile
では、FROM
よりも前に書ける命令はARG
だけです。 ARG と FROM の相互作用を理解 をご覧ください。複数のイメージを作成する場合や、ある構築ステージを他からの依存関係として用いる場合のため、1つの
Dockerfile
に複数のFROM
を書けます。新しい各FROM
命令が処理される前には、その直前でコミットされた、最も新しいイメージ ID を単に表示するだけです。FROM
命令があるたびに、それ以前の命令で作成されたあらゆる状態がクリアになります。オプションとして、
FROM
命令にAS 名前
を追加し、新しい構築ステージに名前を付けられます。この名前は、以降のFROM
とCOPY --from=<名前>
命令で使用し、このステージで構築したイメージを参照できます。タグ
やダイジェスト
値はオプションです。どちらも省略すると、ビルダーはlatest
タグだとデフォルトで扱われます。タグ
値(に相当するイメージ名)が見つからなければ、ビルダーはエラーを返します。
FROM
でマルチプラットフォーム対応のイメージを参照する場合には、オプションの --platform
フラグを使うと、特定のプラットフォーム向けイメージを指定できます。たとえば、 linux/amd64
や、 linux/arm64
や、 windows/amd64
です。デフォルトでは、今まさに利用しているプラットフォームを対象として構築します。 --platform=$BUILDPLATFORM
)、これを、そのステージ内で対象プラットフォーム向けのクロス・コンパイルとして利用できます。
ARG と FROM の相互作用を理解¶
1つめの FROM
命令の前に ARG
命令があり、そこで変数が宣言されていれば FROM
命令で参照できます。
ARG CODE_VERSION=latest
FROM base:${CODE_VERSION}
CMD /code/run-app
FROM extras:${CODE_VERSION}
CMD /code/run-extras
FROM
命令より前に宣言された ARG
は構築ステージ外のため、 FROM
命令以降で使えません。1つめの FROM
よりも前に宣言された ARG
のデフォルト値を使うには、構築ステージ内で値を持たない ARG
命令を使います。
ARG VERSION=latest
FROM busybox:$VERSION
ARG VERSION
RUN echo $VERSION > image_version
RUN¶
RUN には2つの形式があります。
RUN <コマンド>
(シェル形式 。コマンドはシェル内で実行される。デフォルトは Linux が/bin/sh -c
で、 Windows はcmd /S /C
)RUN ["実行ファイル", "パラメータ1", "パラメータ2"]
(実行形式 )
RUN
命令は、現在のイメージよりも上にある新しいレイヤでコマンドを実行し、その結果を Dockerfile
の次のステップで使われます。
RUN
命令の実行と、コミット処理によって生成される(イメージ・レイヤの)階層化とは、Docker の中心となる考え方に基づいています。これは、ソースコードを管理するかのように、手軽にコミットができ、イメージ履歴のどの場所からもコンテナを作成できます。
exec 形式は、シェル上の処理で文字列が改変されないようにします。加えて、シェルを実行するバイナリを含まないベース・イメージでも、 RUN
命令を実行できるようにします。
シェル形式で使うデフォルトのシェルは、 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" ]
のように、直接シェルを実行します。 exec 形式もしくは直接シェルを実行する場合は、シェル形式と同じように処理をしているように見えますが、シェルが環境変数を処理しているのであり、 Docker が行っているのではありません。
注釈
(exec 形式の記述方法は JSON です)JSON 形式では、バックスラッシュをエスケープする必要があります。これが特に関係するのは、
RUN ["c:\windows\system32\tasklist.exe"]
この例の正しい構文は、こちらです。
RUN ["c:\\windows\\system32\\tasklist.exe"]
RUN
命令(で処理された内容)のキャッシュは、次回以降の構築時にも、自動的に有効です。 RUN apt-get dist-upgrade -y
のような命令に対するキャッシュは、次の構築時に再利用されます。 RUN
命令に対するキャッシュを無効にするには、 docker build --no-cache
のように --no-cache
フラグを使います。
詳細は、Dockerfile を書くベストプラクティス をご覧ください。
Dockerfile 中に ADD
命令と COPY
命令が出てくると、以降の RUN
命令の内容はキャッシュされません。
判明している問題 (RUN)¶
Issue 783 は、 AUFS ファイルシステム使用時に発生する、ファイルの権限(パーミッション)についての問題です。たとえば、ファイルを
rm
で削除するときに気づくかもしれません。
CMD¶
CMD
命令には3つの形式があります。
CMD ["実行ファイル","パラメータ1","パラメータ2"]
( exec 形式、こちらが望ましい )CMD ["パラメータ1", "パラメータ2"]
( ENTRYPOINT 命令に対するデフォルトのパラメータとして扱う)CMD コマンド パラメータ1 パラメータ2
(シェル形式)
CMD
命令は Dockerfile
中で1度しか使えません。複数の CMD
命令があれば、最後の CMD
のみ有効です。
CMD の主な目的は、コンテナ実行時のデフォルト(初期設定)を指定するためです 。デフォルトには、実行ファイルを含める場合も、そうでない場合もあります。実行ファイルを含まない場合は、 ENTRYPOINT
命令の指定が必要です。
ENTRYPOINT
命令に対するデフォルトの引数を CMD
で指定する場合は、 CMD
命令と ENTRYPOINT
命令の両方を JSON 配列形式で指定する必要があります。
注釈
exec 形式は JSON
シェル形式と異なり、 exec 形式はコマンドとしてのシェルを実行しません。つまり、通常のシェルとしての処理を行いません。たとえば、 CMD [ "echo", "$HOME" ]
では、 $HOME
を変数展開しません。もしも、シェルとしての処理を行いたければ、シェル形式を使うか、 CMD [ "sh", "-c", "echo $HOME" ]
のように、直接シェルを実行します。 exec 形式もしくは直接シェルを実行する場合は、シェル形式と同じように処理をしているように見えますが、シェルが環境変数を処理しているのであり、 Docker が行っているのではありません。
シェル形式か exec 形式の CMD
命令とは、対象イメージの起動時に処理するコマンドを指定します。
CMD
をシェル形式にする場合、 /bin/sh -c
の中で <コマンド>
が実行されます。
FROM ubuntu
CMD echo "This is a test." | wc -
シェルを使わずに <コマンド> を実行 したければ、JSON 配列としてコマンドを記述する必要があり、その実行ファイルはフルパスで指定します。 この、JSON 配列形式が、CMD での望ましいフォーマットです 。パラメータを追加するには、その配列内で1つ1つの文字列として記述します。
FROM ubuntu
CMD ["/usr/bin/wc","--help"]
コンテナを起動するたびに、同じコマンドを毎回実行するのであれば、 ENTRYPOINT
命令と CMD
命令の組み合わせを検討ください。詳しくは をご覧ください。
docker run
で引数を指定すると、 CMD
で指定されているデフォルトの挙動を上書きできます。
注釈
RUN
と CMD
を混同しないでください。 RUN
は実際にコマンドを実行し、その結果をコミットします。対して、 CMD
は構築時には何も実行しませんが、イメージを使って実行したいコマンドを指定するものです。
LABEL¶
LABEL <キー>=<値> <キー>=<値> <キー>=<値> ...
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."
イメージは複数のラベルを持てます。1行で複数のラベルを指定できます。 Docker 1.10 未満では、この手法で最終イメージの容量を減らせましたが、今は違います。それでも1行で書く方法を選択するのであれば、2つの方法があります。
LABEL multi.label1="value1" multi.label2="value2" other="value3"
LABEL multi.label1="value1" \
multi.label2="value2" \
other="value3"
ベース・イメージか親イメージ( FROM
行にあるイメージ)を含むラベルは、イメージで継承されます。ラベルが既に存在していても、その値が違う場合は、直近で追加された値で、以前の値を上書きします。
イメージのラベルを表示するには、 docker image inspect
コマンドを使います。 --format
オプションを使えば、ラベルのみ表示できます。
$ docker image inspect --format='' myimage
"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"
},
MAINTAINER(非推奨)¶
MAINTAINER <名前>
MAINTAINER
命令は、イメージを作成した Author (作成者)のフィールドを設定します。この命令よりも LABEL
命令のほうが、より柔軟であり、こちらを使うべきです。それにより、必要なメタデータの設定が簡単になり、 docker inspect
などで簡単に表示できます。 MAINTAINER
フィールドに相当するラベルは、次のように指定します。
LABEL maintainer="SvenDowideit@home.org.au"
こうしておけば、 docker inspect
で他のラベルと一緒に表示できます。
EXPOSE¶
EXPOSE <ポート> [<ポート>/<プロトコル>...]
コンテナの実行時、指定した EXPOSE
命令です。対象ポートが TCP か UDP か、どちらをリッスンするか指定できます。プロトコルの指定がなければ、 TCP がデフォルトです。
EXPOSE
命令だけは、実際にはポートを docker run
で -p
フラグを使い、公開用のポートと割り当てる(
EXPOSE
はデフォルトで TCP を前提としますが、 UDP も指定できます。
EXPOSE 80/udp
TCP と UDP の両方を公開するには、2行で書きます。
EXPOSE 80/tcp
EXPOSE 80/udp
この例で、 docker run
で -P
オプションを付けると、TCP と UDP のそれぞれにポートを公開します。注意点としては、 -P を使うと、ホスト上で
EXPOSE
の設定に関係なく、実行時に -p
フラグを使い、その設定を上書き出来ます。たとえば、次のようにします。
docker run -p 80:80/tcp -p 80:80/udp ...
ホストシステム上でポート転送を設定するには、 -P フラグの使い方 をご覧ください。 docker network
コマンドはコンテナ間で通信するネットワークの作成をサポートしますが、特定のポートを露出したり公開したりを指定する必要はありません。これは、ネットワークに接続している複数のコンテナは、あらゆるポートを通して相互に通信できるからです。詳細な情報は、 この機能の上書き をご覧ください。
ENV¶
ENV <キー>=<値> ...
ENV
命令は、環境変数 <キー>
に対し、値を <値>
として設定します。この値は、以降に続く構築ステージ中で、環境変数として保持されます。その上、多くの場合、 その途中で置き換え 可能です。値は、他の環境変数を示すものとしても解釈できます。そのため、引用符はエスケープしなければ削除されます。コマンドラインでの構文解釈と同様に、引用符とバックラッシュによって、値のなかで空白を使えるようになります。
例:
ENV MY_NAME="John Doe"
ENV MY_DOG=Rex\ The\ Dog
ENV MY_CAT=fluffy
ENV
命令では、一度に複数の <キー>=<値> ...
変数を指定できます。次の例は、先ほどの例の結果と(環境変数の値が)完全に同じになります。
ENV MY_NAME="John Doe" MY_DOG=Rex\ The\ Dog \
MY_CAT=fluffy
ENV
命令を使い設定した環境変数は、結果として作成されたイメージから実行したコンテナでも維持されます。 docker inspect
を使い、この値を確認できます。そして、それらを変更するには docker run --env <キー>=<値>
を使います。
環境変数の維持は、予期しない悪影響を引き起こす可能性があります。たとえば、 ENV DEBIAN_FRONTEND=noninteractive
を設定すると、 apt-get
の挙動を変えます。そのため、イメージの利用者を混乱させるかもしれません。
環境変数が構築中のみ必要で、最終イメージで不要な場合は、代わりにコマンドで値の指定を検討ください。
RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y ...
あるいは、 ARG
を使えば、最終イメージでは保持されません。
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y ...
注釈
別の書き方
ENV
命令では、 ENV <キー> <値>
のように、 =
を省略する別の構文があります。例:
ENV MY_VAR my-value
この構文では、1つの ENV
命令で、複数の環境変数を設定できません。そのため、混乱を引き起こす可能性があります。たとえば、以下の指定では、1つの環境変数( ONE
)に対し、値 "TWO= THREE=world"
を設定します。
ENV ONE TWO= THREE=world
後方互換のため、この別の書き方がサポートされています。しかし、先述で説明した理由のため、使わないほうが良いでしょう。加えて、将来のリリースでは削除される可能性があります。
ADD¶
ADD には 2 つの形式があります。
ADD [--chown=<ユーザ>:<グループ>] <追加元>... <追加先>
ADD [--chown=<ユーザ>:<グループ>] ["<追加元>",... "<追加先>"]
パスに空白を含む場合には、後者の形式が必要です。
注釈
--chown
機能がサポートされているのは、 Linux コンテナを構築するために使う Dockerfile 上のみです。そのため、 Windows コンテナ上では機能しません。Linux と Windows 間では、ユーザとグループの所有者に関する概念を変換できません。ユーザ名とグループ名を ID に変換するには、 /etc/passwd
と /etc/group
を使いますが、これができるのは Linux OS をベースとしたコンテナのみです。
ADD
命令では、追加したいファイル、ディレクトリ、リモートファイルの URL を <追加元>
で指定すると、これらをイメージのファイルシステム上のパス <追加先>
に追加します。
複数の 追加元
リソースを指定できます。ファイルやディレクトリの場合、それぞれのパスは、構築コンテキストの追加先に対する相対パスとして解釈されます。
それぞれの 追加元
には、Go 言語の filepath.Match ルールを使い、ワイルドカードや一致が処理されます。
"hom" で始まるファイルすべてを追加するには、次のようにします。
ADD hom* /mydir/
次の例では、 ?
は "home.txt" のような1文字に置き換えられます。
ADD hom?.txt /mydir/
<追加先>
は絶対パスか WORKDIR
(作業ディレクトリ)からの相対パスであり、これらの(追加元の)ソースを送信先コンテナ内にコピーします。
以下は相対パスを使う例で、 "test.txt" を <WORKDIR>/relativeDir/
(相対ディレクトリ)に追加します。
ADD test.txt relativeDir/
次は絶対パスを使う例です。 /absoluteDir/
(相対ディレクトリ)に "test.txt" を追加します。
ADD test.txt /absoluteDir/
特殊文字( [
や ]
など)を含むファイルやディレクトリを追加する場合は、Go 言語のルールに従い、各パスをエスケープする必要があります。たとえば、ファイル名 arr[0].txt
を追加するには、次のようにします。
ADD arr[[]0].txt /mydir/
新しいファイルやディレクトリは、オプションの --chown
フラグでユーザ名、グループ名、UID/GID の組み合わせて追加対象の権限指定リクエストを指定しない限り、 UID と GID が 0 として作成されます。 --chown
フラグの書式により、ユーザ名とグループ名の文字列の指定や、整数として直接 UID と GID のあらゆる組み合わせの指定ができます。ユーザ名かグループ名を指定すると、コンテナのルート・ファイルシステム上にある /etc/passwd
と /etc/group
ファイルを使い、その名前から適切な整数の UID や GID にそれぞれ変換する処理が行われます。以下は --chown
フラグを使って適切に定義する例です。
ADD --chown=55:mygroup files* /somedir/
ADD --chown=bin files* /somedir/
ADD --chown=1 files* /somedir/
ADD --chown=10:11 files* /somedir/
もしも、コンテナのルート・ファイルシステムに /etc/passwd
や /etc/group
ファイルが無く、さらに --chown
フラグで使われたユーザ名やグループ名が存在しない場合は、 ADD
の処理段階で構築が失敗します。整数で ID を指定すると、(ユーザ名が存在しているかどうか)検索する必要がなく、コンテナのルート・ファイルシステムの内容に依存しません。
<追加元>
がリモートにあるファイル URL の場合には、追加先のパーミッションは 600 になります。もしも、リモートファイルの取得時に HTTP ヘッダ Last-Modified
があれば、追加先の mtime
を設定するために使います。しかしながら、 ADD
の途中で処理される他のファイルと同じように、ファイルが変更されたかどうかや、キャッシュを更新するかどうかを判断するために mtime
は使われません。
注釈
docker build - < ファイル名
) Dockerfile
を渡して構築する場合は、 Dockerfile
で URL をベースとした ADD
のみ追加可能です。また、標準入力で圧縮したアーカイブも渡せます( docker build - < archive.tar.gz
)ので、アーカイブのルートにある Dockerfile
と、以降に続くアーカイブ内容が、構築用のコンテクストとして利用されます。
もしも URL ファイルが認証によって保護されている場合、ADD
命令は認証をサポートしていないため、コンテナ内で RUN wget
や RUN curl
など他のツールを使う必要があります。
注釈
ADD
命令を処理するにあたり、 <追加元>
の内容が変更されている場合は、その Dockerfile の対象行以降でキャッシュを無効にします。RUN
命令のためのキャッシュも、無効になる対象です。詳しい情報は ベストプラクティス・ガイド - 構築キャッシュの活用 をご覧ください。
ADD
は以下のルールに従います。
<追加元>
のパスは、構築コンテクスト内にある必要があります。つまり、ADD .../どこか /どこか
のように指定できません。これは、docker build
の第一段階が、コンテクスト対象のディレクトリ(とサブディレクトリ)を docker デーモンに対して送信するからです。もしも
<追加元>
が URL で追加先
の最後がスラッシュ記号 で終わっていなければ、URL からファイルをダウンロードした後、<追加先>
にコピーします。もしも
<追加元>
が URL で追加先
の最後がスラッシュ記号 で終わっていれば、URL からファイル名を推測し、それから<追加先>/<ファイル名>
にファイルをダウンロードします。たとえば、ADD http://example.com/foobar /
は、ファイル/foobar
を作成します。その際、適切なファイル名を検出できるようするため、URL には何らかのパスを含める必要があります(http://example.com
は動作しません )。<追加元>
がディレクトリであれば、ファイルシステムのメタデータも含む、ディレクトリの内容すべてがコピーされます。
注釈
対象ディレクトリそのものはコピーしません。ディレクトリの内容のみコピー対象です。
<追加元>
がローカルにあり、認識できる圧縮形式(無圧縮 、 gzip 、 gzip2、xz )の tar アーカイブの場合は、ディレクトリとして展開 します。リモート URL からのリソースは展開 しません 。ディレクトリのコピーまたは展開は、tar -x
の挙動と同じ、次の処理の組み合わせです。送信先に何らかのパスが存在していたら、
1つ1つのファイルごとに、ソースツリーに含まれる方を優先して処理(コピー)する
注釈
ファイルが認識できる圧縮形式かどうかにかかわらず、ファイル名ではなく、対象ファイルの内容に基づいて処理が行われます。たとえば、空ファイルの名前が
.tar.gz
だとしておm、これは圧縮ファイルとは認識されず、ファイル展開に関するエラーメッセージは一切表示 されず 、それどころか、ファイルは単に追加先にコピーされます。
追加元
が何らかのファイルの場合は、そのメタデータと一緒に個別にコピーされます。<追加先>
がスラッシュ記号 で終わっている場合は、これがディレクトリとみなされ、<追加元>
は<追加先>/base(<送信元>)
に書き込まれます。複数の
<追加元>
がスラッシュ記号 で終わっていなければ、対象は通常のファイルとみなされ、<追加元>
の内容が、<追加先>
に書き込まれます。<追加先>
が存在しない場合は、対象パス内に存在していないディレクトリ全てと共に作成されます。
COPY¶
COPY には 2 つの形式があります。
COPY [--chown=<ユーザ>:<グループ>] <コピー元>... <コピー先>
COPY [--chown=<ユーザ>:<グループ>] ["<コピー元>",... "<コピー先>"]
パスに空白を含む場合には、後者の形式が必要です。
注釈
--chown
機能がサポートされているのは、 Linux コンテナを構築するために使う Dockerfile 上のみです。そのため、 Windows コンテナ上では機能しません。Linux と Windows 間では、ユーザとグループの所有者に関する概念を変換できません。ユーザ名とグループ名を ID に変換するには、 /etc/passwd
と /etc/group
を使いますが、これができるのは Linux OS をベースとしたコンテナのみです。
COPY
命令では、追加したいファイル、ディレクトリを <コピー元>
で指定すると、これらをイメージのファイルシステム上のパス <コピー先>
に追加します。
複数の コピー元
リソースを指定できます。ファイルやディレクトリの場合、それぞれのパスは、構築コンテキストのコピー先に対する相対パスとして解釈されます。
それぞれの コピー元
には、Go 言語の filepath.Match ルールを使い、ワイルドカードや一致が処理されます。
"hom" で始まるファイルすべてを追加するには、次のようにします。
COPY hom* /mydir/
次の例では、 ?
は "home.txt" のような1文字に置き換えられます。
COPY hom?.txt /mydir/
<コピー先>
は絶対パスか WORKDIR
(作業ディレクトリ)からの相対パスであり、これらの(コピー元の)ソースを送信先コンテナ内にコピーします。
以下は相対パスを使う例で、 "test.txt" を <WORKDIR>/relativeDir/
(相対ディレクトリ)に追加します。
COPY test.txt relativeDir/
次は絶対パスを使う例です。 /absoluteDir/
(相対ディレクトリ)に "test.txt" を追加します。
COPY test.txt /absoluteDir/
特殊文字( [
や ]
など)を含むファイルやディレクトリを追加する場合は、Go 言語のルールに従い、各パスをエスケープする必要があります。たとえば、ファイル名 arr[0].txt,
を追加するには、次のようにします。
COPY arr[[]0].txt /mydir/
新しいファイルやディレクトリは、オプションの --chown
フラグでユーザ名、グループ名、UID/GID の組み合わせて追加対象の権限指定リクエストを指定しない限り、 UID と GID が 0 として作成されます。 --chown
フラグの書式により、ユーザ名とグループ名の文字列の指定や、整数として直接 UID と GID のあらゆる組み合わせの指定ができます。ユーザ名かグループ名を指定すると、コンテナのルート・ファイルシステム上にある /etc/passwd
と /etc/group
ファイルを使い、その名前から適切な整数の UID や GID にそれぞれ変換する処理が行われます。以下は --chown
フラグを使って適切に定義する例です。
COPY --chown=55:mygroup files* /somedir/
COPY --chown=bin files* /somedir/
COPY --chown=1 files* /somedir/
COPY --chown=10:11 files* /somedir/
もしも、コンテナのルート・ファイルシステムに /etc/passwd
や /etc/group
ファイルが無く、さらに --chown
フラグで使われたユーザ名やグループ名が存在しない場合は、 COPY
の処理段階で構築が失敗します。整数で ID を指定すると、(ユーザ名が存在しているかどうか)検索する必要がなく、コンテナのルート・ファイルシステムの内容に依存しません。
注釈
docker build - < ファイル名
) Dockerfile
を渡して構築しようとしても、 COPY
は使えません。
オプションで、これまでの( FROM .. AS <名前>
として作成した)構築ステージをコピー元(ソース)の場所として指定するために、 COPY
で --from=<名前>
フラグを利用できます。これは、ユーザ自身が構築コンテキストを送る作業の替わりとなります。
COPY
は以下のルールに従います。
<コピー元>
のパスは、構築コンテクスト内にある必要があります。つまり、COPY .../どこか /どこか
のように指定できません。これは、docker build
の第一段階が、コンテクスト対象のディレクトリ(とサブディレクトリ)を docker デーモンに対して送信するからです。<コピー元>
がディレクトリであれば、ファイルシステムのメタデータも含む、ディレクトリの内容すべてがコピーされます。
注釈
対象ディレクトリそのものはコピーしません。ディレクトリの内容のみコピー対象です。
コピー元
が何らかのファイルの場合は、そのメタデータと一緒に個別にコピーされます。<コピー先>
がスラッシュ記号 で終わっている場合は、これがディレクトリとみなされ、<コピー元>
は<コピー先>/base(<コピー元>)
に書き込まれます。複数の
<コピー元>
がスラッシュ記号 で終わっていなければ、対象は通常のファイルとみなされ、<コピー元>
の内容が、<コピー先>
に書き込まれます。<コピー先>
が存在しない場合は、対象パス内に存在していないディレクトリ全てと共に作成されます。
注釈
COPY
命令を処理するにあたり、 <コピー元>
の内容が変更されている場合は、その Dockerfile の対象行以降でキャッシュを無効にします。RUN
命令のためのキャッシュも、無効になる対象です。詳しい情報は ベストプラクティス・ガイド - 構築キャッシュの活用 をご覧ください。
ENTRYPOINT¶
ENTRYPOINT には2つの形式があります。
exec 形式は、推奨されている形式です:
ENTRYPOINT ["実行ファイル", "パラメータ1", "パラメータ2"]
shell 形式:
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
フラグを使います。
shell 形式では、 CMD
や run
コマンドラインの引数を使えません。 ENTRYPOINT
は /bin/sh -c
のサブコマンドとして起動されるため、シグナルを渡せません。そのため、実行ファイルはコンテナの PID 1
ではなく、Unix シグナルを受信できません。つまり、 docker stop <コンテナ名>
を実行しても、実行ファイルは SIGTERM
シグナルを受信しません。
ENTRYPOINT
を複数書いても、Dockerfile
中で一番最後の 命令しか処理されません。
exec 形式の ENTRYPOINT 例¶
ENTRYPOINT
の exec 形式は、確実に実行するデフォルトのコマンドと引数を設定するために使います。そして、 CMD
のどちらかの形式を使い、変わる可能性があるデフォルト(のパラメータや引数)を指定します。
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]
このコンテナを実行すると、唯一のプロセスとして top
が見えます。
$ 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
top
を適切に終了するには、docker stop test
を実行します。
以下の Dockerfile
は、Apache をフォアグラウンドで実行するために(つまり、 PID 1
として) ENTRYPOINT
を使います。
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"]
実行ファイルの exec
と gosu
コマンドを使えます。
#!/usr/bin/env 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 "$@"
最後に、 ENTRYPOINT
スクリプトが Unix シグナルを受信し、続いて、他の処理を行うようにする必要があります。
#!/bin/sh
# メモ:sh でスクリプトを書いたため、buxybox コンテナも動作する
# サービスの停止後に、必要があれば手動でクリーンアップできるようにする場合や、
# 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
注釈
ENTRYPOINT
設定は --entrypoint
を使って上書きできます。しかし、ここでの設定は、実行ファイルとしての sh -c
は使いません)。
注釈
exec 形式は JSON 配列として解釈されますので、単語を囲むにはシングルクオート(')ではなくダブルクオート(")を使う必要があります。
シェル 形式とは異なり、 exec 形式はコマンドシェルを呼び出しません。つまり、通常のシェルとしての処理が起こらないのを意味します。たとえば、 ENTRYPOINT [ "echo", "$HOME" ]
では、 $HOME
を変数展開しません。シェルとしての処理を行いたい場合には、 シェル 形式を使うか、 ENTRYPOINT [ "sh", "-c", "echo $HOME" ]
のようにシェルを直接実行します。exec 形式を使って直接シェルを実行する場合は、シェル形式の場合と同様に、環境変数の展開をするのはシェルであり、 Docker ではありません。
シェル形式の ENTRYPOINT 例¶
単に文字列を ENTRYPOINT
で指定するだけで、 /bin/sh -c
の中で実行できます。この形式では、環境変数を展開するためにシェルの処理を使います。そして、 CMD
や docker run
コマンドラインでの引数は無視されます。長期に実行している ENTRYPOINT
の実行バイナリに対し、 docker stop
で適切にシグナルを送るには、 exec
で起動する必要があるのを念頭に置いてください。
FROM ubuntu
ENTRYPOINT exec top -b
このイメージで実行すると、 PID 1
のプロセスが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
は、コンテナを実行バイナリ のように扱いたい場合に定義すべきです。CMD
は、ENTRYPOINT
コマンドに対するデフォルトの引数を定義するためか、、コンテナ内でその場その場でコマンドを実行するために使うべきです。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_entry p1_entry exec_cmd p1_cmd |
CMD [“p1_cmd”, “p2_cmd”] |
p1_cmd p2_cmd |
/bin/sh -c exec_entry p1_entry |
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 |
exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd |
注釈
ベース・イメージで CMD
が定義されている場合でも、 ENTRYPOINT
の設定によって CMD
は空の値にリセットされます。このような場合は、現在のイメージで何らかの値を持つように、 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
ファイルを新規作成するボリュームへコピーします。
ボリューム指定についての注意¶
Dockerfile
内でのボリュームの扱いについては、以下の点に注意してください。
Windows ベースのコンテナ上のボリューム : Windows ベースのコンテナを利用する場合、コンテナ内でのボリューム指定先は、次のどちらかの必要があります。
存在していないディレクトリ、または、空のディレクトリ
C ドライブ以外
Dockerfile 内からのボリューム変更 :ボリュームを宣言後、構築ステップでボリューム内のデータに対する変更があったとしても、それらの変更は破棄されます(反映されません)。
JSON 形式 :引数リストは JSON 配列として扱われます。文字を囲むにはシングル・クォート(
'
)ではなくダブル・クォート("
)を使う必要があります。コンテナ実行時に宣言されるホスト側ディレクトリ :
ホスト側ディレクトリ (マウントポイント)は、その性質上、ホストに依存します。これはイメージの移植性を維持するためであり、指定対象のホスト側ディレクトリが、全てのホスト上で利用可能である保証がないためです。この理由により、Dockerfile 内からホスト側ディレクトリをマウントできません。VOLUME
命令は、一切のホスト側ディレクトリ
に対するパラメータ指定をサポートしません。(ホスト側を指定する必要がある場合は)コンテナの実行時や作成時に、マウントポイントを指定しなくてはいけません。
USER¶
USER <ユーザ>[:<グループ>]
または
USER <UID>[:<GID>]
注釈
ユーザに対してグループを指定する場合、そのユーザは指定したグループに「のみ」所属しますので注意してください。他のグループ所属の設定は無視されます。
警告
ユーザに対して所属グループの指定が無ければ、イメージ(または以降の命令)の実行時に root
グループとして実行されます。
Windows では、まず、初期に作成済みではないユーザを作成が必要です。これをするには、 Dockerfile で net user
コマンドを使う必要があります。
FROM microsoft/windowsservercore
# コンテナ内で Windows ユーザを作成
RUN net user /add patrick
# 次のコマンドを指定
USER patrick
WORKDIR¶
WORKDIR /path/to/workdir
WORKDIR
命令は、Dockerfile
内で以降に続く RUN
、 CMD
、 ENTRYPOINT
、 COPY
、 ADD
命令の処理時に(コマンドを実行する場所として)使う WORKDIR
が存在していなければ作成されます。これは、以降の Dockerfile
で使われなくてもです。
WORKDIR
命令は Dockerifle
内で何度も利用できます。相対パスを指定すると、その前の 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 中で指定が無ければ、構築時に警告が出ます。
[Warning] One or more build-args [foo] were not consumed.
([警告] build-arg [foo] は使われませんでした)
Dockerfile に1つまたは複数の ARG
命令を入れられます。たとえば、以下は正しい Dockerfile です。
FROM busybox
ARG user1
ARG buildno
# ...
警告
GitHub キーやユーザの認証情報のような docker history
コマンドを使えば、構築時の変数を表示できるからです。
イメージ構築時に機微情報(シークレット)を安全に使う方法を学ぶには、 BuildKit でイメージを構築 をご覧ください。
デフォルトの値¶
オプションで、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 .
構築時、コマンドラインの引数で変数の値を指定していても、 ARG
命令で変数を定義するまでは、その変数の値は空白の文字列として扱われます。2行目の USER
命令は、この時点では変数 $user
に対する値が定義されていないため、変数 $user
の値は some_user
として処理されます(シェルなどの変数展開と同じで、 ${user:-some_user}
は、変数 $user
の値が未定義であれば、 some_user
を値に入れる処理です )。続く3行目の ARG
命令により、コマンドラインで指定した引数 what_user
が、変数 $user
に対する値として設定されます。そして、4行目の USER
命令で使われている $user
は what_user
になります。
ARG
命令が有効な範囲は、構築ステージの定義が終わるまでです。複数のステージで ARG
命令が必要です。
FROM busybox
ARG SETTINGS
RUN ./run/setup $SETTINGS
FROM busybox
ARG SETTINGS
RUN ./run/other $SETTINGS
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
命令で使われる値は、ユーザが(コマンドラインで)指定した ARG
命令の値 v2.0.1
ではなく v1.0.0
です。これは、シェルスクリプトの挙動に似ています。定義した時点から、
先ほどの例と違う 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
の値は常に構築イメージ内に保持されます。 --build-arg
フラグがない docker build を考えます。
$ docker build .
この Dockerfile 例を使うと、 (docker build 時に引数の指定がなかったため)3行目の ENV
命令によってデフォルトの v1.0.0
が変数 CONT_IMG_VER
の値となり、この値がイメージ内でずっと保持されます。
今回の例にある変数展開の手法によって、コマンドラインから引数を渡し、 ENV
命令を利用して最終的なイメージまで保持できます。なお、変数展開をサポートしているのは、 Dockerfile 命令の一部 のみです。
定義済みの ARG 変数¶
Docker には、Dockerfile 内で対応する ARG
命令を使わなくても利用できる ARG
変数があります。
HTTP_PROXY
http_proxy
HTTPS_PROXY
https_proxy
FTP_PROXY
ftp_proxy
NO_PROXY
no_proxy
これらの変数を使うには、コマンドライン上の --build-arg
フラグで渡します。
$ docker build --build-arg CONT_IMG_VER=v2.0.1 .
デフォルトでは、これらの定義済み変数は docker history
の出力から除外されます。それらを除外することで、 HTTP_PROXY
変数のような機微情報が漏洩する危険性を減らします。
たとえば、次の Dockerifle を使い、 --build-arg HTTP_PROXY=http://user:pass@proxy.lon.example.com
で構築する例を考えます。
FROM ubuntu
RUN echo "Hello World"
こうすると、 HTTP_PROXY
変数の値は docker history
で見えなくなり、キャッシュもされません。もしも、プロキシサーバの場所が http://user:pass@proxy.sfo.example.com
に変わったとしても、以後の構築で(誤った情報を)キャッシュすることによる構築失敗もありません。
このような挙動を上書きしたい場合は、次の Dockerfile のような ARG
命令を追加します。
FROM ubuntu
ARG HTTP_PROXY
RUN echo "Hello World"
この Dockerfile で構築すると、 HTTP_PROXY
は docker history
(で見えるように)保存され、この値を変更すると構築キャッシュは無効化されます。
グローバル範囲での自動的なプラットフォーム ARG 変数¶
この機能が利用できるのは、 BuildKit バックエンドを利用する時のみです。
構築を処理するノード( ARG
変数として定義済みです。ターゲット・プラットフォームは docker build
の --platform
フラグで指定できます。
以下の ARG
変数は自動的に設定されます。
TARGETPLATFORM
…構築対象 のプラットフォーム。例:linux/amd64
、linux/arm/v7
、windows/amd64
TARGETOS
… TARGETPLATFORM の OS コンポーネントTARGETARCH
… TARGETPLATFORM のアーキテクチャ・コンポーネントTARGETVARIANT
… TARGETPLATFORM の派生コンポーネントBUILDPLATFORM
… 構築を処理するノードのプラットフォームBUILDOS
… BUILDPLATFORM の OS コンポーネントBUILDARCH
… BUILDPLATFORM のアーキテクチャ・コンポーネントBUILDVARIANT
… BUILDPLATFORM の派生コンポーネント
これらの引数はグローバル RUN
コマンドから自動的に利用できません。これらの引数を構築ステージの中で利用するには、値なしで定義します。
例:
FROM alpine
ARG TARGETPLATFORM
RUN echo "I'm building for $TARGETPLATFORM"
構築キャッシュへの影響¶
ARG
変数は ENV
変数と異なり、構築イメージ内に保持されません。しかしながら、 ARG
変数も( ENV と)同じように構築キャッシュに影響を与えます。たとえば、 Dockerfile で以前に構築されたものと異なる ARG
変数が定義された場合は、定義がどうであろうと、真っ先に「 RUN
命令は ARG
命令で指定された ARG
変数を自動的に(環境変数として)扱います。つまり、これによって失敗が発生する可能性があります。全ての定義済み ARG
変数は、 Dockerfile
内で一致する ARG
命令がなければ、キャッシュから除外されます。
たとえば、これら2つの Dockerfile で考えましょう。
FROM ubuntu
ARG CONT_IMG_VER
RUN echo $CONT_IMG_VER
FROM ubuntu
ARG CONT_IMG_VER
RUN echo hello
コマンドラインで --build-arg CONT_IMG_VER=<値>
を指定すると、どちらの場合も2行目まではキャッシュ失敗は発生せず、3行目で失敗が発生します。 ARG CONT_IMG_VER
によって、 RUN 行を CONT_IMG_VER=<value> echo hello
として実行するように定義するのと同じです。つまり、 <値>
が変わったため、キャッシュに失敗します。
同じコマンドラインで、別の例を考えます。
FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER=$CONT_IMG_VER
RUN echo $CONT_IMG_VER
この例では、3行目でキャッシュに失敗します。失敗するのは、 ENV
にある変数の値が ARG
変数を参照し、かつ、その変数がコマンドラインを通して変更されるからです。この例では、 ENV
命令によってイメージの中に値が含まれます。
ENV
命令を、同じ名前の ARG
で上書きする場合は、次のような Dockerifle になります。
FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER=hello
RUN echo $CONT_IMG_VER
3行目の CONT_IMG_VER
値は定数( hello
)のため、キャッシュ失敗は発生しません。結果として、 RUN
(4行目)で使われる環境変数と値は、構築の間は変わりません。
ONBUILD¶
ONBUILD [命令]
ONBUILD
命令は、後から他のイメージ構築の基礎(ベース)として使われる時に、「 Dockerfile
にある FROM
命令の直後に、直ちに実行されます。
あらゆる構築命令をトリガとして登録できます。
他のイメージを構築する時に、その基礎となるイメージを構築するのに役立ちます。たとえば、アプリケーションの構築環境や、ユーザ固有の設定でカスタマイズ可能なデーモンです。
たとえば、イメージが再利用可能な Python アプリケーションを構築するもの(ビルダ)であれば、特定のディレクトリに対してアプリケーションのソースコードを追加する必要があり、さらに、後から構築用スクリプトを呼び出す必要がある場合もあるでしょう。この時点では ADD
と RUN
命令を呼び出せません。なぜなら、まだアプリケーションのソースコードにアクセスできず、さらに、アプリケーションごとにソースコードが異なるからです。これをシンプルに実現するには、アプリケーション開発者がテンプレートとなる Dockerfile
をアプリケーションごとにコピー&ペーストする方法があります。しかしこれは、アプリケーション固有のコードが混在すると、効率が悪く、エラーも発生しがちであり、更新も困難です。
この解決策は ONBUILD
命令を使い、次の構築ステージ中に、後で実行する高度な命令を登録します。
これは、次のように動作します:
ONBUILD
命令が発生すると、ビルダーは構築中のイメージ内に、トリガをメタデータとして追加します。現時点での構築では、この命令は何ら影響を与えません。構築後、すべてのトリガ一覧は、イメージのマニフェスト
OnBuild
キー以下に保管されます。これらはdocker inspect
コマンドで調べられます。後ほど、このイメージを新しい構築のベースとして用いるため、
FROM
命令で呼び出されるでしょう。FROM
命令の処理の一部として、ダウンストリームのビルダーはONBUILD
トリガを探します。そして、トリガが登録されたのと同じ順番で、トリガを実行します。もしもトリガの1つでも失敗すると、FROM
命令は処理が中止となり、構築は失敗します。全てのトリガ処理が成功すると、FROM
命令は完了し、以降は通常通り構築が進行します。最後の処理がおわった後、最終イメージからトリガは削除されます。言い換えると、「
孫 」ビルドには継承されません。
たとえば、次のような追加となるでしょう。
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
警告
ONBUILD
命令をつなげ、 ONBUILD ONBUILD
としては使えません。
警告
ONBUILD
命令は FROM
や MAINTAINER
命令をトリガにできません。
STOPSIGNAL¶
STOPSIGNAL 信号
STOPSIGNAL
命令は、 SIGKILL
のような信号名の書式の
HEALTHCHECK¶
HEALTHCHECK
命令は2つの形式があります。
HEALTHCHECK [オプション] CMD コマンド
(コンテナ内部でコマンドを実行し、コンテナの正常性を確認)HEALTHCHECK NONE
(ベースイメージから、ヘルスチェック設定の継承を無効化)
HEALTHCHECK
命令は、Docker に対してコンテナのテスト方法を伝え、コンテナが動作し続けているかどうか確認します。これにより、ウェブサーバなどでサーバのプロセスが実行中にかかわらず、無限ループして詰まっていたり、新しい接続を処理できなかったりするような問題を検出します。
コンテナで starting
(起動中)です。ヘルスチェックが正常であれば、(以前の状況にかかわらず)ステータスは healthy
(正常)になります。何度か連続した unhealthy
(障害発生)になります。
CMD
よりも前に書いて、オプションを指定できます。
--interval=期間
(デフォルト: 30s) ※ヘルスチェックの間隔--timeout=期間
(デフォルト: 30s) ※タイムアウトの長さ--start-period=期間
(デフォルト: 0s) ※ 開始時の間隔--retries=
(デフォルト: 3) ※リトライ回数
コンテナが起動し、(間隔で指定した) interval 秒後に、1回目のヘルスチェックを実行します。それから、再びヘルスチェックを前回のチェック完了後から、 interval 秒を経過するごとに行います。
信号を送信しても、 タイムアウト 秒まで処理に時間がかかっていれば、チェックに失敗したとみなされます。
コンテナに対するヘルスチェックの失敗が、 retries で指定したリトライ回数に達すると、コンテナは unhealthy
とみなされます。
コンテナが起動に必要な時間を、初期化する時間として start period で指定します。この期間中に
これらを指定できるのは、Dockerfile の HEALTHCHECK
命令のみです。複数の HEALTHCHECK
命令があれば、最後の1つのみ有効です。
CMD
キーワードの後に書くコマンドは、シェルコマンド(例: HEALTHCHECK CMD /bin/check-running
)か exec 配列(他の Dockerfile コマンドと同様です。詳細は ENTRYPOINT
をご覧ください)のどちらか一方を使えます。
そのコマンドの終了ステータスが、対象となるコンテナのヘルスステータスになります。可能性のある値は、次の通りです。
0:
成功 コンテナは正常1:
障害 2:
予約済み - この終了コードは使いません
たとえば、ウェブサーバがサイトのメインページを3秒以内に提供できるかどうかを、5分ごとにチェックするには、次の様にします。
HEALTHCHECK --interval=5m --timeout=3s \
CMD curl -f http://localhost/ || exit 1
ヘルスチェックの失敗時に調査(デバッグ)をしやすくするために、コマンドが書き込んだ標準出力や標準エラー出力といった、あらゆる出力文字(UTF-8 エンコード方式)がヘルスステータスに保存され、これらは docker inspect
で調べられます。この出力は短く保たれます(初めから 4096 バイトのみ保存します)。
コンテナのヘルスステータスが変われば、新しいステータスで health_status
イベントが作成されます。
SHELL¶
SHELL ["実行ファイル", "パラメータ"]
SHELL
命令は、 シェル 形式で使われるデフォルトのコマンドを上書きできます。 Linux 上でのデフォルトのシェルは ["/bin/sh", "-c"]
で、Windows は ["cmd", "/S", "/C"]
です。Dockerfile では、 SHELL
命令を JSON 形式で書く必要があります。
Windows 上では特に SHELL
命令が役立ちます。これは、一般的に使われるシェルが cmd
と powershell
の2種類ありますし、 sh
を含む他のシェルも利用できるからです。
SHELL
命令は何度も指定できます。それぞれの SHELL
命令は、以前すべての SHELL
命令を上書きし、以降の命令で(新しく指定したシェルが)有効になります。以下は例です。
FROM microsoft/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
SHELL
命令によって効果があるのは、 Dockerfile でシェル形式として RUN
、 CMD
、 ENTRYPOINT
を使う時です。
以下の例は、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 コマンドのプロセッサ(シェル)が呼び出されるからです。2つめは、各 RUN
命令はシェル形式のため、コマンドの前に追加で powershell -command
の実行が必要になるからです。
これを効率的にするには、2つの仕組みのどちらか1つを使います。1つは、次のように RUN 命令のコマンドを JSON 形式で書きます。
RUN ["powershell", "-command", "Execute-MyCmdlet", "-param1 \"c:\\foo.txt\""]
JSON 形式(で使うコマンドの指定)は明確であり、不要な cmd.exe を使いません。しかし、二重引用符(ダブルクォータ)やエスケープ処理が必要といった、冗長さがあります。もう1つの仕組みは、 SHELL
命令を使ってシェル形式にしますが、 escape
パーサ・ディレクティブの指定があれば、 Windows ユーザにとって、より普通の書式で書けます(訳者補足: Dockerfile では、デフォルトのエスケープ文字は「 \
」ですが、Windows の場合「 \
」はパスの文字です。そのため、次の例のようにエスケープ文字を「`」などに変えると、Windows のパスがシェル形式でそのまま利用できるため、便利です)。
# escape=`
FROM microsoft/nanoserver
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:\myproject> docker build -t shell .
Sending build context to Docker daemon 4.096 kB
Step 1/5 : FROM microsoft/nanoserver
---> 22738ff49c6d
Step 2/5 : SHELL powershell -command
---> Running in 6fcdb6855ae2
---> 6331462d4300
Removing intermediate container 6fcdb6855ae2
Step 3/5 : RUN New-Item -ItemType Directory C:\Example
---> Running in d0eef8386e97
Directory: C:\
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 10/28/2016 11:26 AM Example
---> 3f2fbf1395d9
Removing intermediate container d0eef8386e97
Step 4/5 : ADD Execute-MyCmdlet.ps1 c:\example\
---> a955b2621c31
Removing intermediate container b825593d39fc
Step 5/5 : RUN c:\example\Execute-MyCmdlet 'hello world'
---> Running in be6d8e63fe75
hello world
---> 8e559e9bf424
Removing intermediate container be6d8e63fe75
Successfully built 8e559e9bf424
PS E:\myproject>
また、シェルの動作を変更するためにも SHELL
命令が使えます。たとえば、 Windows 上で SHELL cmd /S /C /V:ON|OFF
を使えば、
さらに、SHELL
命令によって、Linux であれば zsh
、 csh
、 tcsh
といった、他のシェルに切り替えられます。