電気ひつじ牧場

技術メモと日常のあれこれ

DockerfileのARGとスコープ

知らなかったので少しハマってしまったARGのお話しです.

結論

  • 変数のスコープはビルドステージごとに保持される.
  • FROMに変数を指定するには一番最初のFROMより上で変数宣言する必要がある.
  • 一番最初のFROMより上で宣言された変数は,そのままビルドステージ内のスコープで利用できない.

検証

環境は

  • macOS
  • docker desktop community 2.3.0.3 stable
  • docker engine 19.03.8

変数のスコープはビルドステージごとに保持される.

Dockerfileをこうする.

FROM alpine
ARG OS=alpine
RUN echo $OS

FROM $OS
RUN echo $OS

ビルドしてみる.

$ docker build --no-cache -t teru01/argtest:1.0 .

Sending build context to Docker daemon  2.048kB
Step 1/5 : FROM alpine
 ---> a24bb4013296
Step 2/5 : ARG OS=alpine
 ---> Running in 3fac749f761b
Removing intermediate container 3fac749f761b
 ---> 99fd0ae09f82
Step 3/5 : RUN echo $OS
 ---> Running in 9dbb6a567f5c
alpine
Removing intermediate container 9dbb6a567f5c
 ---> 0edd0a618174
Step 4/5 : FROM $OS
base name ($OS) should not be blank

Step 3/5では$OSが出力されているが,2つ目のFROMでは$OSが空なのでエラーになっている.

FROMに変数を指定するには一番最初のFROMより上で変数宣言する必要がある.

Dockerfileをこうする.先頭にARGを置いた.

ARG OS=alpine
FROM alpine

FROM $OS
RUN cat /etc/os-release

ビルドする.

$ docker build --no-cache -t teru01/argtest:1.1 .

Sending build context to Docker daemon  2.048kB
Step 1/4 : ARG OS=alpine
Step 2/4 : FROM alpine
 ---> a24bb4013296
Step 3/4 : FROM $OS
 ---> a24bb4013296
Step 4/4 : RUN cat /etc/os-release
 ---> Running in b72e5e69ed0e
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.12.0
PRETTY_NAME="Alpine Linux v3.12"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://bugs.alpinelinux.org/"
Removing intermediate container b72e5e69ed0e
 ---> aa58ec13c264
Successfully built aa58ec13c264
Successfully tagged teru01/argtest:1.1

Step3/4で$OSが使われている.

一番最初のFROMより上で宣言された変数は,そのままビルドステージ内のスコープで利用できない.

Dockerfileを変更し,2つ目のステージ内でRUN$OSを利用する.

ARG OS=alpine
FROM alpine
RUN echo $OS

FROM $OS
RUN echo $OS
$ docker build --no-cache -t teru01/argtest:1.2 .

Sending build context to Docker daemon  2.048kB
Step 1/5 : ARG OS=alpine
Step 2/5 : FROM alpine
 ---> a24bb4013296
Step 3/5 : RUN echo $OS
 ---> Running in c09683cf6b8e

Removing intermediate container c09683cf6b8e
 ---> fb642142e8aa
Step 4/5 : FROM $OS
 ---> a24bb4013296
Step 5/5 : RUN echo $OS
 ---> Running in 998708ee2d3d

Removing intermediate container 998708ee2d3d
 ---> 7589182a6cd2
Successfully built 7589182a6cd2
Successfully tagged teru01/argtest:1.2

Step3/5, 5/5で$OSが空になってるのがわかる.一番最初のFROMより上で宣言された変数は,プログラミング言語グローバル変数と同じ挙動をするわけではないらしい.

一番最初のFROMより上のARGをスコープ内で使いたい時

そのスコープ内のARGで宣言する.先ほどのDockerfileの例なら次のようになる.

ARG OS=alpine
FROM alpine
ARG OS
RUN echo $OS

FROM $OS
RUN echo $OS
$ docker build --no-cache -t teru01/argtest:1.2 .

Sending build context to Docker daemon  2.048kB
Step 1/6 : ARG OS=alpine
Step 2/6 : FROM alpine
 ---> a24bb4013296
Step 3/6 : ARG OS
 ---> Running in e5765868a2c5
Removing intermediate container e5765868a2c5
 ---> 5c8dd4ab5b9e
Step 4/6 : RUN echo $OS
 ---> Running in faee8bee7cf1
alpine
Removing intermediate container faee8bee7cf1
 ---> 5cb5c0e805b4
Step 5/6 : FROM $OS
 ---> a24bb4013296
Step 6/6 : RUN echo $OS
 ---> Running in c707c0304836

Removing intermediate container c707c0304836
 ---> bd77da501ee5
Successfully built bd77da501ee5
Successfully tagged teru01/argtest:1.2

Step4/6で$OSが使われ,スコープ内にARGを使っていないStep6/6では依然として空のままになっている.

参考

https://github.com/moby/moby/issues/37345