【Docker使えるようになりたい】【#10 ENV & ARG】ARGとENVの基本と使い所の違い

はじめに

DockerfileにはENVとARGという似たような動作をする命令があり、それらの「細かい違い」や「使いどころの違い」などをまとめて比較できるようまとめました。

  • ARG : イメージビルド時にのみ利用可能な変数を定義
  • ENV : イメージビルド時からコンテナの実行時まで有効な環境変数を定義

まずは、それぞれの命令の使い方の解説、その後、それぞれの命令の使いどころや違いなどを比較していければと思います。

ARGコマンド

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コマンド

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の違い

ARGENVはどちらも変数を定義するためのコマンドですが、その有効範囲と用途が異なります。

特性ARGENV
有効範囲イメージのビルド時のみビルド時からコンテナ実行時まで
イメージ内への永続化されないされる
コンテナ実行時に参照可能か不可可能
デフォルト値の設定可能可能
機密情報の取り扱い注意(ビルド履歴に残る可能性)注意(イメージに含まれる)

使い分けのポイント

  • ビルド時のみ必要な情報: ARGを使用。ビルド時の設定や一時的な値を渡すのに適しています。
  • ビルド後も必要な情報: ENVを使用。アプリケーションの設定やコンテナ実行時に必要な環境変数を定義します。

ARGENVの連携

ARGで定義した値をENVに渡すことも可能です。

FROM ubuntu:20.04

# ビルド時引数の定義
ARG DEFAULT_PORT=80

# 環境変数にビルド時引数の値を設定
ENV PORT=${DEFAULT_PORT}

# アプリケーションの実行
CMD ["python", "app.py"]

この場合、ビルド時にDEFAULT_PORTを指定し、その値が環境変数PORTに設定されます。

ARGとENVの注意点

ARGの注意点

  • ビルドキャッシュへの影響: ARGの値が変わると、その後のレイヤーキャッシュが無効になります。頻繁に変わるARGは、できるだけ後半に配置するとビルド効率が上がります。
  • 機密情報の扱い: ARGで機密情報を扱うと、docker historyコマンドで履歴に残る可能性があります。機密情報はビルド時シークレットなどを使用して安全に扱いましょう。

ENVの注意点

  • 機密情報の扱い: ENVで設定した値はイメージに含まれるため、パスワードやAPIキーなどの機密情報を設定するのは避けましょう。
  • イメージサイズへの影響: ENVで設定する環境変数は、レイヤーとして追加されます。不要な環境変数を設定すると、イメージサイズが増加します。

ベストプラクティス

  • デフォルト値の設定: どちらのコマンドでもデフォルト値を設定し、必要に応じて上書きできるようにします。
  • 機密情報の安全な扱い: 機密情報は環境変数ではなく、シークレット管理システムやビルド時シークレットを使用します。
  • レイヤーの最適化: 不要なレイヤーの増加を防ぐために、RUN命令をまとめて記述します。
  • 変数の再利用: 一度定義した変数は積極的に再利用し、Dockerfileの可読性と保守性を高めます。

おわりに

ARGとENVはどちらも似たようなものに見えますが、使用時に明確な意図を伝えることができるので、意識して使い分けることが重要だと思います。

以下の公式のドキュメントも参考にしてみてください

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA