はじめに
1. Docker インストラクション
インストラクションってなに?
公式ドキュメントで Dockerfile 内に書く ARG
, ENV
, RUN
などを総称してインストラクション, instruction と表記していました。
2. Linux コマンド
-
docker image build
- キーバリュー指定
--build-arg
- ファイル指定
- ない
- キーバリュー指定
-
docker container run
- キーバリュー指定
--env
- ファイル指定
--env-file
- キーバリュー指定
◯ サンプルコード
ディレクトリの名前と問題が対応しています
gh repo clone domodomodomo/docker-env-sample
cd docker-env-sample/part1/1
# for Bash - macOS, Ubuntu
bash cmd.sh
# for PowerShell - Windows
powershell ./cmd.ps1
問題 1 - Docker インストラクション
◯ 設問
echo コマンド
の実行結果 A
, B
, C
にはそれぞれ何が表示されますか?
FROM debian:12.5-slim
ARG BLUE
ARG RED="Charmander"
# 1. RUN export
RUN export GREEN="Bulbasaur" && \
echo "'${BLUE}' | '${RED}' | '${GREEN}'"
#
# A
#
# 2. ARG
RUN export GREEN="Bulbasaur"
RUN echo "'${BLUE}' | '${RED}' | '${GREEN}'"
#
# B
#
# 3. ENV
ENV BLUE="Squirtle"
CMD echo "'${BLUE}' | '${RED}' | '${GREEN}'"
#
# C
#
docker image build --build-arg BLUE="Squirtle" --tag app --progress=plain --no-cache .
# --progress=plain
# これが無いと RUN echo コマンドが表示されなくなる
#
# --no-cache
# これが無いと 1 回目の実行結果がキャッシュされて
# 2 回目、3 回目コマンドを打ったときにRUN echo が実行されなくなる
docker container run app
◯ 選択
1.
'Squirtle' | '' | ''
2.
'Squirtle' | 'Charmander' | ''
3.
'Squirtle' | 'Charmander' | 'Bulbasaur'
◯ 解答
解答
FROM debian:12.5-slim
ARG BLUE
ARG RED="Charmander"
# 1. RUN export
RUN export GREEN="Bulbasaur" \
&& echo "'${BLUE}' | '${RED}' | '${GREEN}'"
#
# 'Squirtle' | 'Charmander' | 'Bulbasaur'
#
# 2. ARG
RUN export GREEN="Bulbasaur"
RUN echo "'${BLUE}' | '${RED}' | '${GREEN}'"
#
# 'Squirtle' | 'Charmander' | ''
#
# GREEN is not saved as an environment variable
# Each RUN command has a different shell session
# 3. ENV
ENV BLUE="Squirtle"
CMD echo "'${BLUE}' | '${RED}' | '${GREEN}'"
#
# 'Squirtle' | '' | ''
#
# RED is not saved as an environment variable
# Environment variables set by ARG command are not applied to CMD
◯ シェルセッション
詳細
- Docker は
RUN
コマンドごとにシェルセッションがわかれているらしいです- 根拠となる公式ドキュメントは見つけられませんでした...
- なので
RUN export
しても次のRUN
では忘れられてしまう -
COPY
,ADD
はわかるけどENV
,ARG
はなんでわざわざ別にあるんだろう?🤔って思ってたのですが、そういうことだったんですね...
Docker インストラクション | Linux コマンド |
---|---|
ENV, ARG | export, source |
WORKDIR | cd |
USER | su |
SHELL | sh, bash |
- シェルセッションとはなにかについて調べてみたのですがわかりませんでした...orz
- シェルとはなにかに関する記事... 読んでもわからない... orz
◯ ARG とキャッシュ
詳細
ARG
で定義した環境変数が同じ場合、キャッシュが使われます。サンプルコードで --no-cache
を指定したのはこの挙動を避けるためです。
Dockerfile
で定義されたARG
変数の値が前回のビルドと異なる場合、その値が最初に使用されたときに「キャッシュミス」が発生します(定義されたときではありません)。
If a Dockerfile defines an ARG variable whose value is different from a previous build, then a "cache miss" occurs upon its first usage, not its definition.
Impact on build caching - Docker Docs
ARG
を直接使わないRUN
にもキャッシュミスを起こすので、ARG
は使用するRUN
の直前で書いたほうが良い。
Dockerfile
のARG
はビルドキャッシュにどう影響するのか、どこに書くべきなのか - Akashic Records
◯ 疑問
プロンプト
ChatGPT 4o 先生に質問したところそれっぽい答えを返してくれますが理解はできませんでした。ChatGPT 10 の登場を待ちたいと思います。
# 疑問 1. なぜ `ENV` コマンドがあるのですか?
1. なぜ Dcoker はシェルセッションを `RUN` コマンドごとにわけたのですか?
2. もし Docker に `ENV` コマンドが存在せず、
`RUN` コマンドごとにシェルセッションが別れておらず、
一度 `RUN export` すると以降その環境変数が参照できるような場合、
どのような不都合が生じますか?
# 疑問 2. なぜ `ARG` コマンドがあるのですか?
1. なぜ `ARG` で設定された環境変数は `CMD` では参照できないようになっているのですか?
2. もし Docker に `ARG` コマンドが存在せず `--build-arg` で指定した値が
そのまま `ENV` で指定された環境変数に代入される場合、どのような不都合が生じますか?
# 疑問 3. `ENV` や `ARG` で設定された環境変数はどこで保存されていますか?
1. Docker イメージが持つメタ情報
* `RUN` コマンドが実行される度に
Docker が OS のシェルセッションに環境変数と値を書き込む
2. Docker イメージが持つ OS
* `RUN` コマンドが実行される度に
OS が OS のシェルセッションに環境変数と値を書き込む
場所は可能な限り **具体的に** 教えてください。
問題 2 - Linux コマンド
◯ 設問
Docker コンテナのみに環境変数を書き込み Docker イメージに環境変数を残さない方法として誤っているものは A
, B
, C
のうちどれですか?
FROM debian:12.5-slim
# A ENV GREEN="Bulbasaur"
ENV GREEN="Bulbasaur"
RUN echo "'${BLUE}' | '${RED}' | '${GREEN}'"
CMD echo "'${BLUE}' | '${RED}' | '${GREEN}'"
RED=Charmander
コマンド
PowerShell - Windows
docker image build `
--tag app `
--progress=plain `
--no-cache `
.
# Docker イメージの環境変数を表示
docker image inspect app `
--format '{{range .Config.Env}}{{println .}}{{end}}'
# B --env BLUE=Bulbasau
# C --env-file .env.local
docker container run `
--env BLUE=Bulbasaur `
--env-file .env.local `
--name app `
app
# Docker コンテナの環境変数を表示
docker container inspect app `
--format '{{range .Config.Env}}{{println .}}{{end}}'
docker container rm app
bash - macOS, Ubuntu
#!/bin/bash
docker image build \
--tag app \
--progress=plain \
--no-cache \
.
# Docker イメージの環境変数を表示
docker image inspect app \
--format '{{range .Config.Env}}{{println .}}{{end}}'
# B --env BLUE=Bulbasau
# C --env-file .env.local
docker container run \
--env BLUE=Bulbasaur \
--env-file .env.local \
--name app \
app
# Docker コンテナの環境変数を表示
docker container inspect app \
--format '{{range .Config.Env}}{{println .}}{{end}}'
docker container rm app
◯ 解答
解答
A
セキュリティ
ビルド時にパスワードなどの機密情報を取り扱う際、
環境変数
ではなく
ファイル
を通してやり取りしてくださいという記述が見られます。まだ整理できていません...orz がとり急ぎ大事そうな記事を列挙させていただきます。
- "環境変数の方法" より推奨される "ファイルの方法" による機密情報の受け渡し方法
- Docker
-
RUN --mount=type=secret
オプション
-
- Docker Compse
-
secrets
プロパティ
-
- Docker
◯ Docker
シークレット(ユーザー資格情報、APIトークンなど)を渡すためにビルド引数(訳注:
--build-arg
)を使用することは推奨されません。ビルド引数はdocker history
コマンドで見える ... (中略) ... Docker イメージをビルドする際にシークレットを安全に使用する方法については、RUN --mount=type=secret
セクションを参照してください。
It isn't recommended to use build arguments for passing secrets such as user credentials, API tokens, etc. Build arguments are visible in the docker history command and ... (omitted) ... Refer to theRUN --mount=type=secret
section to learn about secure ways to use secrets when building images.
◯ Docker Compose
Diogo Monicaの記事では、環境変数で秘密情報を扱う問題点として
- 環境はプロセスで暗黙的に利用可能のため、アクセスや利用を追跡することが困難である。
- アプリケーションが環境をデバッグやエラー報告のために出力することはよくある。
- 環境変数は子プロセスに引き継がれるため、意図しないアクセスが可能になる。これは、最小権限の原則を破る。
- アプリケーションがクラッシュした時、デバッグのために、環境変数をログファイルに保存することは一般的であり、これはディスク上に平文の秘密情報があることを意味する。
- 環境変数に秘密情報を入れると、それはすぐにtribal knowledgeになる。特定の環境変数がデリケートをであると認識していない新人のエンジニアは、適切に処理できない。
おわりに
◯ シリーズ
ありがとうございました。