Dockerfile 記述のベスト・プラクティス¶
Docker は Dockerfile
に書かれる指示を読み込んで、自動的にイメージを構築します。
これは、あらゆる命令を含んだテキストファイルであり、順に処理することで指定されたイメージを構築するために必要となるものです。
Dockerfile
は所定のフォーマットにこだわっていて、特定の指示を用いることにしています。
基本的なことは Dockerfile リファレンス で学ぶことができます。
Dockerfile を書き慣れていない方は、そのリファレンスから始めてください。
このドキュメントでは、Docker 社や Docker コミュニティが推奨するベストプラクティスおよび方法を示しています。 Dockerfile を簡単に作り出して利用できるように、効率的な Dockerfile の書き方を示すものです。 みなさんには、ここに示す推奨方法を強くお勧めします(さらに、公式イメージを生成するときには、この推奨方法に従うことが必要になってきます)。
このベストプラクティスや推奨する手法の多くは、更新中の buildpack-deps の Dockerfile
に見ることができます。
注釈
ここで説明する Dockerfile コマンドの詳しい説明は Dockerfile リファレンス を参照してください。
一般的なガイドラインとアドバイス¶
コンテナは "はかない" もの¶
Dockerfile
が定義するイメージによって生成されるコンテナは、できる限り "はかないもの"(ephemeral)と考えておくべきです。
"はかない" という語を使うのは、コンテナが停止、破棄されて、すぐに新たなものが作り出されるからです。
最小限の構成や設定があれば稼動できます。
.dockerignore ファイルの利用¶
Dockerfile は、たいていは空のディレクトリに配置するのが適当です。
その後にそのディレクトリへは、Dockerfile の構築に必要となるファイルのみを追加します。
ビルドの効率をよくするために、ファイルやディレクトリを除外指定する .dockerignore
ファイルをそのディレクトリに置く方法もあります。
このファイルがサポートする除外パターンの指定方法は .gitignore
と同様です。
このファイルの作成に関しては .dockerignore ファイル を参照してください。
不要なパッケージをインストールしない¶
複雑さ、依存関係、ファイルサイズ、構築時間をそれぞれ減らすためには、余分な、または必須ではない「あった方が良いだろう」程度のパッケージをインストールすべきではありません。例えば、データベース・イメージであればテキストエディタは不要でしょう。
コンテナごとに1つのプロセスだけ実行¶
アプリケーションを複数のコンテナに分けることにより、スケールアウトやコンテナの再利用が行いやすくなります。 たとえばウェブ・アプリケーションが3つの独立したコンテナにより成り立っているとします。 それらは個々のイメージを持つものとなり、それぞれに分かれてウェブ・アプリケーション、データベース、メモリキャッシュを管理するようになります。
「1つのコンテナには1つのプロセス」とすべき、ということを聞いたことがあるかもしれません。 この標語には見習うべきところはあるのですが、1つのコンテナに1つのオペレーティング・システムのプロセスだけを割り当てるのかというと、必ずしもそうではありません。 最近のコンテナは 初期プロセスにおいて起動 するという現実もあり、プログラムの中には都合に応じて追加のプロセスを起動するようなものもあります。 例をあげると、 Celery はワーカ・プロセスを複数起動し、 Apache はリクエストごとにプロセスを生成します。 「1つのコンテナには1つのプロセス」というのは、優れた経験則となることがありますが、決して厳密な規則というわけでもありません。 コンテナはできる限りすっきりとモジュラ化されるように、適切な判断をしてください。
コンテナが互いに依存している場合は、Docker container ネットワーク を用いることで、コンテナ間の通信を確実に行うことができます。
レイヤの数を最小に¶
Dockerfile
は可読性とレイヤ数のバランスを考慮する必要があります。
Dockerfile
を読みやすくする(つまり長期にわたって保守しやすくする)のか、利用するレイヤ数をできるだけ減らすのかということです。
使用するレイヤ数は、計画的に注意して取り決めてください。
複数行にわたる引数は並びを適切に¶
複数行にわたる引数は、できるなら後々の変更を容易にするために、その並びはアルファベット順にしましょう。 そうしておけば、パッケージを重複指定することはなくなり、一覧の変更も簡単になります。 プルリクエストを読んだりレビューしたりすることも、さらに楽になります。 バックスラッシュ(\) の前に空白を含めておくことも同様です。
以下は buildpack-deps
イメージ の記述例です。
RUN apt-get update && apt-get install -y \
bzr \
cvs \
git \
mercurial \
subversion
ビルドキャッシュ¶
イメージ構築の過程において Docker は、Dockerfile
内に示されている命令を記述順に実行していきます。
個々の命令が検査される際に Docker は、既存イメージのキャッシュが再利用できるかどうかを調べます。
そこでは新たな(同じ)イメージを作ることはしません。
キャッシュをまったく使いたくない場合は docker build
コマンドに --no-cache=true
オプションをつけて実行します。
一方で Docker のキャッシュを利用する場合、Docker が適切なイメージを見つけた上で、どのようなときにキャッシュを利用し、どのようなときには利用しないのかを理解しておくことが必要です。Docker が従っている規則は以下のとおりです。
- キャッシュ内にすでに存在している親イメージから処理を始めます。 そのベースとなるイメージから派生した子イメージに対して、次の命令が合致するかどうかが比較され、子イメージのいずれかが同一の命令によって構築されているかを確認します。 そのようなものが存在しなければ、キャッシュは無効になります。
- ほとんどの場合、
Dockerfile
内の命令と子イメージのどれかを単純に比較するだけで十分です。 しかし命令によっては、多少の検査や解釈が必要となるものもあります。
ADD
命令やCOPY
命令では、イメージに含まれるファイルの内容が検査され、個々のファイルについてチェックサムが計算されます。 この計算において、ファイルの最終更新時刻、最終アクセス時刻は考慮されません。 キャッシュを探す際に、このチェックサムと既存イメージのチェックサムが比較されます。 ファイル内の何かが変更になったとき、たとえばファイル内容やメタデータが変わっていれば、キャッシュは無効になります。
ADD
とCOPY
以外のコマンドの場合、キャッシュのチェックは、コンテナ内のファイル内容を見ることはなく、それによってキャッシュと合致しているかどうかが決定されるわけでありません。 たとえばRUN apt-get -y update
コマンドの処理が行われる際には、コンテナ内にて更新されたファイルは、キャッシュが合致するかどうかの判断のために用いられません。 この場合にはコマンド文字列そのものが、キャッシュの合致判断に用いられます。
キャッシュが無効になると、次に続く Dockerfile
コマンドは新たなイメージを生成し、そのキャッシュは使われなくなります。
Dockerfile コマンド¶
以下は Dockerfile
記述にて推奨するベストな方法を示すものです。
Dockerfile
に記述できるさまざまなコマンドの記述方法を示します。
FROM¶
イメージのベースは、できるだけ現時点での公式リポジトリを利用してください。 Debian イメージ がお勧めです。 このイメージはしっかりと管理されていて、充実したディストリビューションであるにもかかわらず、非常にコンパクトなものになっています(現在 150 MB 以下)。
LABEL¶
イメージにラベルを追加するのは、プロジェクト内でのイメージ管理をしやすくしたり、ライセンス情報の記録や自動化の助けとするなど、さまざまな目的があります。
ラベルを指定するには、 LABEL
で始まる行を追加して、そこにキーと値のペア(key-value pair)をいくつか設定します。
以下に示す例は、いずれも正しい構文です。
説明をコメントとしてつけています。
注釈
文字列に空白が含まれる場合は、引用符でくくるか あるいは エスケープする必要があります。 文字列内に引用符がある場合も、同様にエスケープしてください。
# 個別のラベルを設定
LABEL com.example.version="0.0.1-beta"
LABEL vendor="ACME Incorporated"
LABEL com.example.release-date="2015-02-12"
LABEL com.example.version.is-production=""
# 1行でラベルを設定
LABEL com.example.version="0.0.1-beta" com.example.release-date="2015-02-12"
# 複数のラベルを一度に設定、ただし行継続の文字を使い、長い文字列を改行する
LABEL vendor=ACME\ Incorporated \
com.example.is-beta= \
com.example.is-production="" \
com.example.version="0.0.1-beta" \
com.example.release-date="2015-02-12"
ラベルにおける利用可能なキーと値のガイドラインとしては オブジェクトラベルを理解する を参照してください。またラベルの検索に関する情報は オブジェクト上のラベルの管理 のフィルタリングに関する項目を参照してください。
RUN¶
いつものことながら Dockerfile
は読みやすく理解しやすく、そして保守しやすくすることが必要です。
RUN
コマンドが複数行にわたって長く複雑になるなら、バックスラッシュを使って行を分けてください。
apt-get¶
おそらく RUN
において一番利用する使い方が apt-get
アプリケーションの実行です。
RUN apt-get
はパッケージをインストールするものであるため、注意点がいくつかあります。
RUN apt-get upgrade
や dist-upgrade
の実行は避けてください。
ベース・イメージに含まれる重要パッケージは、権限が与えられていないコンテナ内ではほとんど更新できないからです。
ベース・イメージ内のパッケージが古くなっていたら、開発者に連絡をとってください。
foo
というパッケージを更新する必要があれば、 apt-get install -y foo
を利用してください。
これによってパッケージは自動的に更新されます。
RUN apt-get update
と apt-get install
は、同一の RUN
コマンド内にて同時実行するようにしてください。
たとえば以下のようにします。
RUN apt-get update && apt-get install -y \
package-bar \
package-baz \
package-foo
1つの RUN
コマンド内で apt-get update
だけを使うとキャッシュに問題が発生し、その後の apt-get install
コマンドが失敗します。
たとえば Dockerfile を以下のように記述したとします。
FROM ubuntu:14.04
RUN apt-get update
RUN apt-get install -y curl
イメージが構築されると、レイヤーがすべて Docker のキャッシュに入ります。
この次に apt-get install
を編集して別のパッケージを追加したとします。
FROM ubuntu:14.04
RUN apt-get update
RUN apt-get install -y curl nginx
Docker は当初のコマンドと修正後のコマンドを見て、同一のコマンドであると判断するので、前回の処理において作られたキャッシュを再利用します。
キャッシュされたものを利用して処理が行われるわけですから、結果として apt-get update
は実行 されません 。
apt-get update
が実行されないということは、つまり curl
にしても nginx
にしても、古いバージョンのまま利用する可能性が出てくるということです。
RUN apt-get update && apt-get install -y
というコマンドにすると、 Dockerfile が確実に最新バージョンをインストールしてくれるものとなり、さらにコードを書いたり手作業を加えたりする必要がなくなります。
これは「キャッシュ・バスティング(cache busting)」と呼ばれる技術です。
この技術は、パッケージのバージョンを指定することによっても利用することができます。
これはバージョン・ピニング(version pinning)というものです。
以下に例を示します。
RUN apt-get update && apt-get install -y \
package-bar \
package-baz \
package-foo=1.3.*
バージョン・ピニングでは、キャッシュにどのようなイメージがあろうとも、指定されたバージョンを使ってビルドが行われます。 この手法を用いれば、そのパッケージの最新版に、思いもよらない変更が加わっていたとしても、ビルド失敗を回避できることもあります。
以下の RUN
コマンドはきれいに整えられていて、 apt-get
の推奨する利用方法を示しています。
RUN apt-get update && apt-get install -y \
aufs-tools \
automake \
build-essential \
curl \
dpkg-sig \
libcap-dev \
libsqlite3-dev \
mercurial \
reprepro \
ruby1.9.1 \
ruby1.9.1-dev \
s3cmd=1.1.* \
&& rm -rf /var/lib/apt/lists/*
s3cmd
のコマンド行は、バージョン 1.1.*
を指定しています。
以前に作られたイメージが古いバージョンを使っていたとしても、新たなバージョンの指定により apt-get update
のキャッシュ・バスティングが働いて、確実に新バージョンがインストールされるようになります。
パッケージを各行に分けて記述しているのは、パッケージを重複して書くようなミスを防ぐためです。
apt キャッシュをクリーンアップし /var/lib/apt/lists
を削除するのは、イメージサイズを小さくするためです。
そもそも apt キャッシュはレイヤー内に保存されません。
RUN
コマンドを apt-get update
から始めているので、 apt-get install
の前に必ずパッケージのキャッシュが更新されることになります。
注釈
公式の Debian と Ubuntu のイメージは 自動的に apt-get clean を実行する ので、明示的にこのコマンドを実行する必要はありません。
パイプの利用¶
RUN
コマンドの中には、その出力をパイプを使って他のコマンドへ受け渡すことを前提としているものがあります。
そのときにはパイプを行う文字( |
)を使います。
たとえば以下のような例があります。
RUN wget -O - https://some.site | wc -l > /number
Docker はこういったコマンドを /bin/sh -c
というインタープリタ実行により実現します。
正常処理されたかどうかは、パイプの最後の処理の終了コードにより評価されます。
上の例では、このビルド処理が成功して新たなイメージが生成されるかどうかは、wc -l
コマンドの成功にかかっています。
つまり wget
コマンドが成功するかどうかは関係がありません。
パイプ内のどの段階でも、エラーが発生したらコマンド失敗としたい場合は、頭に set -o pipefail &&
をつけて実行します。
こうしておくと、予期しないエラーが発生しても、それに気づかずにビルドされてしまうことはなくなります。
たとえば以下です。
RUN set -o pipefail && wget -O - https://some.site | wc -l > /number
注釈
すべてのシェルが -o pipefail
オプションをサポートしているわけではありません。
その場合(例えば Debian ベースのイメージにおけるデフォルトシェル dash
である場合)、RUN
コマンドにおける exec 形式の利用を考えてみてください。
これは pipefail
オプションをサポートしているシェルを明示的に指示するものです。
たとえば以下です。
RUN ["/bin/bash", "-c", "set -o pipefail && wget -O - https://some.site | wc -l > /number"]
CMD¶
CMD
コマンドは、イメージ内に含まれるソフトウェアを実行するために用いるもので、引数を指定して実行します。
CMD
はほぼ、CMD ["実行モジュール名", "引数1", "引数2" …]
の形式をとります。
Apache や Rails のようにサービスをともなうイメージに対しては、たとえば CMD ["apache2","-DFOREGROUND"]
といったコマンド実行になります。
実際にサービスベースのイメージに対しては、この実行形式が推奨されます。
上記以外では、 CMD
に対して bash、python、perl などインタラクティブシェルを与えることが行われます。
たとえば CMD ["perl", "-de0"]
、 CMD ["python"]
、 CMD ["php", "-a"]
といった具合です。
この実行形式を利用するということは、たとえば docker run -it python
というコマンドを実行したときに、指定したシェルの中に入り込んで、処理を進めていくことを意味します。
CMD
と ENTRYPOINT
を組み合わせて用いる CMD ["引数", "引数"]
という実行形式がありますが、これを利用するのはまれです。
開発者自身や利用者にとって ENTRYPOINT
がどのように動作するのかが十分に分かっていないなら、用いないようにしましょう。
EXPOSE¶
Dockerfile リファレンスの EXPOSE コマンド
EXPOSE
コマンドは、コンテナが接続のためにリッスンするポートを指定します。
当然のことながらアプリケーションにおいては、標準的なポートを利用します。
たとえば Apache ウェブ・サーバを含んでいるイメージに対しては EXPOSE 80
を使います。
また MongoDB を含んでいれば EXPOSE 27017
を使うことになります。
外部からアクセスできるようにするため、これを実行するユーザは docker run
にフラグをつけて実行します。
そのフラグとは、指定されているポートを、自分が取り決めるどのようなポートに割り当てるかを指示するものです。
Docker のリンク機能においては環境変数が利用できます。
受け側のコンテナが提供元をたどることができるようにするものです(例: MYSQL_PORT_3306_TCP
)。
ENV¶
新しいソフトウェアに対しては ENV
を用いれば簡単にそのソフトウェアを実行できます。
コンテナがインストールするソフトウェアに必要な環境変数 PATH
を、この ENV
を使って更新します。
たとえば ENV PATH /usr/local/nginx/bin:$PATH
を実行すれば、 CMD ["nginx"]
が確実に動作するようになります。
ENV
コマンドは、必要となる環境変数を設定するときにも利用します。
たとえば Postgres の PGDATA
のように、コンテナ化したいサービスに固有の環境変数が設定できます。
また ENV
は普段利用している各種バージョン番号を設定しておくときにも利用されます。
これによってバージョンを混同することなく、管理が容易になります。
たとえば以下がその例です。
ENV PG_MAJOR 9.3
ENV PG_VERSION 9.3.4
RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && …
ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH
プログラムにおける(ハードコーディングではない)定数定義と同じことで、この方法をとっておくのが便利です。
ただ1つの ENV
コマンドを変更するだけで、コンテナ内のソフトウェアバージョンは、いとも簡単に変えてしまうことができるからです。
ADD と COPY¶
Dockerfile リファレンスの ADD コマンド Dockerfile リファレンスの COPY コマンド
ADD
と COPY
の機能は似ていますが、一般的には COPY
が選ばれます。
それは ADD
よりも機能がはっきりしているからです。
COPY
は単に、基本的なコピー機能を使ってローカルファイルをコンテナにコピーするだけです。
一方 ADD
には特定の機能(ローカルでの tar 展開やリモート URL サポート)があり、これはすぐにわかるものではありません。
結局 ADD
の最も適切な利用場面は、ローカルの tar ファイルを自動的に展開してイメージに書き込むときです。
たとえば ADD rootfs.tar.xz /
といったコマンドになります。
Dockerfile
内の複数ステップにおいて異なるファイルをコピーするときには、一度にすべてをコピーするのではなく、 COPY
を使って個別にコピーしてください。
こうしておくと、個々のステップに対するキャッシュのビルドは最低限に抑えることができます。
つまり指定されているファイルが変更になったときのみキャッシュが無効化されます(そのステップは再実行されます)。
例:
COPY requirements.txt /tmp/
RUN pip install /tmp/requirements.txt
COPY . /tmp/
RUN
コマンドのステップより前に COPY . /tmp/
を実行していたとしたら、それに比べて上の例はキャッシュ無効化の可能性が低くなっています。
イメージ・サイズの問題があるので、 ADD
を用いてリモート URL からパッケージを取得することはやめてください。
かわりに curl
や wget
を使ってください。
こうしておくことで、ファイルを取得し展開した後や、イメージ内の他のレイヤにファイルを加える必要がないのであれば、その後にファイルを削除することができます。
たとえば以下に示すのは、やってはいけない例です。
ADD http://example.com/big.tar.xz /usr/src/things/
RUN tar -xJf /usr/src/things/big.tar.xz -C /usr/src/things
RUN make -C /usr/src/things all
そのかわり、次のように記述します。
RUN mkdir -p /usr/src/things \
&& curl -SL http://example.com/big.tar.xz \
| tar -xJC /usr/src/things \
&& make -C /usr/src/things all
ADD
の自動展開機能を必要としないもの(ファイルやディレクトリ)に対しては、常に COPY
を使うようにしてください。
ENTRYPOINT¶
Dockerfile リファレンスの ENTRYPOINT コマンド
ENTRYPOINT
の最適な利用方法は、イメージに対してメインのコマンドを設定することです。
これを設定すると、イメージをそのコマンドそのものであるかのようにして実行できます(その次に CMD
を使ってデフォルトフラグを指定します)。
コマンドライン・ツール s3cmd
のイメージ例から始めます。
ENTRYPOINT ["s3cmd"]
CMD ["--help"]
このイメージが実行されると、コマンドのヘルプが表示されます。
$ docker run s3cmd
あるいは適正なパラメータを指定してコマンドを実行します。
$ docker run s3cmd ls s3://mybucket
このコマンドのようにして、イメージ名がバイナリへの参照としても使えるので便利です。
ENTRYPOINT
コマンドはヘルパースクリプトとの組み合わせにより利用することもできます。
そのスクリプトは、上記のコマンド例と同じように機能させられます。
たとえ対象ツールの起動に複数ステップを要するような場合でも、それが可能です。
たとえば Postgres 公式イメージ は次のスクリプトを ENTRYPOINT
として使っています。
#!/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 "$@"
注釈
このスクリプトは Bash コマンドの exec を用います。 このため最終的に実行されたアプリケーションが、コンテナの PID として 1 を持つことになります。 こうなるとそのアプリケーションは、コンテナに送信された Unix シグナルをすべて受信できるようになります。 詳細は ENTRYPOINT を参照してください。
ヘルパースクリプトはコンテナの中にコピーされ、コンテナ開始時に ENTRYPOINT
から実行されます。
COPY ./docker-entrypoint.sh /
ENTRYPOINT ["/docker-entrypoint.sh"]
このスクリプトを用いると、Postgres との間で、ユーザがいろいろな方法でやり取りできるようになります。
以下は単純に Postgres を起動します。
$ docker run postgres
あるいは、PostgreSQL 実行時、サーバに対してパラメータを渡せます。
$ docker run postgres postgres --help
または Bash のような全く異なるツールを起動するために利用することもできます。
$ docker run --rm -it postgres bash
VOLUME¶
Dockerfile リファレンスの VOLUME コマンド
VOLUME
コマンドは、データベース・ストレージ領域、設定用ストレージ、Docker コンテナによって作成されるファイルやフォルダの公開に使います。
イメージの可変的な部分、あるいはユーザが設定可能な部分については VOLUME の利用が強く推奨されます。
USER¶
サービスが特権ユーザでなくても実行できる場合は、 USER
を用いて非 root ユーザに変更します。
ユーザとグループを生成するところから始めてください。
Dockerfile
内にてたとえば RUN groupadd -r postgres && useradd -r -g postgres postgres
のようなコマンドを実行します。
注釈
イメージ内のユーザとグループに割り当てられる UID、GID は確定的なものではありません。 イメージが再構築されるかどうかには関係なく、「次の」値が UID、GID に割り当てられます。 これが問題となる場合は、UID、GID を明示的に割り当ててください。
注釈
Go 言語の archive/tar パッケージが取り扱うスパースファイルにおいて
未解決のバグ があります。
これは Docker コンテナ内にて非常に大きな値の UID を使ってユーザを生成しようとするため、ディスク消費が異常に発生します。
コンテナ・レイヤ内の /var/log/faillog
が NUL (\0) キャラクタにより埋められてしまいます。
useradd に対して --no-log-init
フラグをつけることで、とりあえずこの問題は回避できます。
ただし Debian/Ubuntu の adduser
ラッパーは --no-log-init
フラグをサポートしていないため、利用することはできません。
sudo
のインストールとその利用は避けてください。
TTY やシグナル送信が予期しない動作をするため、解決できることは少なく、多くの問題を引き起こすことになります。
sudo
と同様の機能(たとえばデーモンの初期化を root により行い、起動は root 以外で行うなど)を実現する必要がある場合は、 gosu を使うとよいかもしれません。
レイヤ数を減らしたり複雑にならないようにするためには、 USER
の設定を何度も繰り返すのは避けてください。
WORKDIR¶
Dockerfile リファレンスの WORKDIR コマンド
WORKDIR
に設定するパスは、分かり易く確実なものとするために、絶対パス指定としてください。
また RUN cd … && do-something
といった長くなる一方のコマンドを書くくらいなら、 WORKDIR
を利用してください。
そのような書き方は読みにくく、トラブル発生時には解決しにくく保守が困難になるためです。
ONBUILD¶
Dockerfile リファレンスの ONBUILD コマンド
ONBUILD
コマンドは、 Dockerfile
によるビルドが完了した後に実行されます。
ONBUILD
は、現在のイメージから FROM
によって派生した子イメージにおいて実行されます。
つまり ONBUILD
とは、親の Dockerfile
から子どもの Dockerfile
へ与える命令であると言えます。
Docker によるビルドにおいては ONBUILD
の実行が済んでから、子イメージのコマンド実行が行われます。
ONBUILD
は、所定のイメージから FROM
を使ってイメージをビルドしようとするときに利用できます。
たとえば特定言語のスタックイメージは ONBUILD
を利用します。
Dockerfile
内にて、その言語で書かれたどのようなユーザ・ソフトウェアであってもビルドすることができます。
その例として Ruby's ONBUILD variants があります。
ONBUILD
によって構築するイメージは、異なったタグを指定してください。
たとえば ruby:1.9-onbuild
と ruby:2.0-onbuild
などです。
ONBUILD
において ADD
や COPY
を用いるときは注意してください。
"onbuild" イメージが新たにビルドされる際に、追加しようとしているリソースが見つからなかったとしたら、このイメージは復旧できない状態になります。上に示したように個別にタグをつけておけば、 Dockerfile
の開発者にとっても判断ができるようになるので、不測の事態は軽減されます。
その他の情報¶
参考
- Best practices for writing Dockerfiles
- https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/