DockerfileにはENVとARGという似たような動作をする命令があり、それらの「細かい違い」や「使いどころの違い」などをまとめて比較できるようまとめました。
- ARG : イメージビルド時にのみ利用可能な変数を定義
- ENV : イメージビルド時からコンテナの実行時まで有効な環境変数を定義
まずは、それぞれの命令の使い方の解説、その後、それぞれの命令の使いどころや違いなどを比較していければと思います。
ARG命令
はARG
とつくだけあり引数を指定する命令になります。
そして、この引数の対象はイメージのビルド時に使用する変数です。
基本的な使い方
ARG 変数名=値
また記述方法も、=
を使わずにスペースを使って変数名と値の間をあけて指定することもできますが、基本は=
を使う方法が一般的です。
変数を使うには、以下のように${}
の{}
内に変数名を記述します
RUN echo ${変数名}
docker image build
コマンドを実行時にDockerfile内で定義したARG変数の値をとして使用する場合は、以下のように--build-arg
オプションを使うことで設定できます。
docker image build --build-arg {変数名}="値" .
例 : コンパイルするファイルを外部から指定する
以下のように、gccを使ってホストOS上のファイルをイメージ内にコピーして対象のファイルをコンパイルしてコンテナ実行時に自動で実行する場合、
FROM gcc
# コピー
COPY main.c main.c
# コンパイル実行
RUN gcc main.c
# コンパイルの結果生成されたa.outをデフォルト実行に設定
CMD ["./a.out"]
このdockerfile
の問題点は以下の2点です。
main.c
というファイル名を何度も記述しているmain.c
以外のファイルをコンパイルして実行したいときにdockerfileを書き換える必要がある
これはとても手間なので、ARG
の出番になります。
いかがARGを使うように修正したdockerfile
になります。
FROM gcc
# コンパイル対象のファイルを指定
ARG TARGET_FILE=main.c
# コピー
COPY ${TARGET_FILE} ${TARGET_FILE}
# コンパイル実行
RUN gcc ${TARGET_FILE}
# コンパイルの結果生成されたa.outをデフォルト実行に設定
CMD ["./a.out"]
main.cを対象としたい場合は、そのままbuildして、別のファイルを対象にしたい場合は、docker build時に--build-arg 変数名="値"
を付ける
今回の例では、main2.c
をコンパイル対象にしたかったのでTARGET_FILE
に対してbuild実行時引数として--build-arg TARGET_FILE="main2.c
とした
docker image build --build-arg TARGET_FILE="main2.c" -t arg-test .
ENV
コマンドは、Dockerfile
内で環境変数を定義するための命令です。
ENV
で設定した環境変数は、ビルド時からコンテナの実行時まで有効であり、イメージ内に永続化されます。
これにより、イメージを基に作成された全てのコンテナで環境変数を利用することができます。
ENV 変数名=値
または、スペースで区切って指定することも可能ですが、これもARG命令
と同じで=
を使った記述方法が一般的です。
複数の環境変数を一度に設定する場合は、バックスラッシュ \
を使って以下のように記述します。
ENV 変数名1=値1 \
変数名2=値2
ENV
で定義した環境変数は、Dockerfile内の後続の命令や、コンテナの実行時に利用できます。
変数を参照する際は、${変数名}
と記述します。
RUN echo "環境変数の値は ${変数名} です"
例:アプリケーションのポート番号を指定する
以下は、ENV
を使ってアプリケーションのポート番号を指定する例です。
FROM node:14
# 環境変数の設定
ENV APP_PORT=3000
# 作業ディレクトリの設定
WORKDIR /app
# アプリケーションのコピー
COPY . .
# 依存関係のインストール
RUN npm install
# コンテナ起動時のコマンド
CMD ["npm", "start"]
# コンテナがリッスンするポートの指定
EXPOSE ${APP_PORT}
コンテナ実行時にポート番号を変更したい場合は、docker run
コマンドで環境変数を上書きできます。
docker run -e APP_PORT=8080 -p 8080:8080 my-node-app
ARG
とENV
はどちらも変数を定義するためのコマンドですが、その有効範囲と用途が異なります。
特性 | ARG | ENV |
---|---|---|
有効範囲 | イメージのビルド時のみ | ビルド時からコンテナ実行時まで |
イメージ内への永続化 | されない | される |
コンテナ実行時に参照可能か | 不可 | 可能 |
デフォルト値の設定 | 可能 | 可能 |
機密情報の取り扱い | 注意(ビルド履歴に残る可能性) | 注意(イメージに含まれる) |
使い分けのポイント
- ビルド時のみ必要な情報:
ARG
を使用。ビルド時の設定や一時的な値を渡すのに適しています。 - ビルド後も必要な情報:
ENV
を使用。アプリケーションの設定やコンテナ実行時に必要な環境変数を定義します。
ARG
とENV
の連携
ARG
で定義した値をENV
に渡すことも可能です。
FROM ubuntu:20.04
# ビルド時引数の定義
ARG DEFAULT_PORT=80
# 環境変数にビルド時引数の値を設定
ENV PORT=${DEFAULT_PORT}
# アプリケーションの実行
CMD ["python", "app.py"]
この場合、ビルド時にDEFAULT_PORT
を指定し、その値が環境変数PORT
に設定されます。
ARGの注意点
- ビルドキャッシュへの影響:
ARG
の値が変わると、その後のレイヤーキャッシュが無効になります。頻繁に変わるARG
は、できるだけ後半に配置するとビルド効率が上がります。 - 機密情報の扱い:
ARG
で機密情報を扱うと、docker history
コマンドで履歴に残る可能性があります。機密情報はビルド時シークレットなどを使用して安全に扱いましょう。
ENVの注意点
- 機密情報の扱い:
ENV
で設定した値はイメージに含まれるため、パスワードやAPIキーなどの機密情報を設定するのは避けましょう。 - イメージサイズへの影響:
ENV
で設定する環境変数は、レイヤーとして追加されます。不要な環境変数を設定すると、イメージサイズが増加します。
- デフォルト値の設定: どちらのコマンドでもデフォルト値を設定し、必要に応じて上書きできるようにします。
- 機密情報の安全な扱い: 機密情報は環境変数ではなく、シークレット管理システムやビルド時シークレットを使用します。
- レイヤーの最適化: 不要なレイヤーの増加を防ぐために、
RUN
命令をまとめて記述します。 - 変数の再利用: 一度定義した変数は積極的に再利用し、Dockerfileの可読性と保守性を高めます。
おわりに
ARGとENVはどちらも似たようなものに見えますが、使用時に明確な意図を伝えることができるので、意識して使い分けることが重要だと思います。
以下の公式のドキュメントも参考にしてみてください