Dockerfile の ARG は先頭に書くべきではない

Docker はビルドする際、レイヤーに変更がなければキャッシュを利用してくれる。

レイヤーとは Dockerfile のひとつひとつのコマンドに対応し、レイヤーが積み重なることで Docker のイメージは構築される。

ARG のキャッシュ

ARG はビルド時に引数として渡すことができる。渡した場合はその値が最初に使われるレイヤーまではキャッシュが利用される。

ところが、 ARG が使われるレイヤーまでにひとつでも RUN があると、そこでキャッシュが効かなくなってしまうのである。

キャッシュが効かないとビルド時間がかかるのはもちろん、データ容量も余計に消費してしまう。

試してみる

このような Dockerfile を用意する。

FROM alpine

ARG GREETING=Hello
RUN echo "Start process..."

# ~~~~~~~~~~~~~~~~
# 様々な処理
# ~~~~~~~~~~~~~~~~

RUN echo "$GREETING, Docker!"

ビルドを実行すると、 Hello, Docker! と表示される。

> docker build .                         
...
Step 4/4 : RUN echo "$GREETING, Docker!"
 ---> Running in df2779271f25
Hello, Docker!
Removing intermediate container df2779271f25
 ---> 8a4fd0687bc8
Successfully built 8a4fd0687bc8

その後、 引数として Bye を渡してみる。

> docker build --build-arg GREETING=Bye .
...
Step 2/4 : ARG GREETING=Hello
 ---> Using cache
 ---> 6858212af28a
Step 3/4 : RUN echo "Start process..."
 ---> Running in eb41f2f79f53
Start process...
Removing intermediate container eb41f2f79f53
 ---> 36699bbb8c43
Step 4/4 : RUN echo "$GREETING, Docker!"
 ---> Running in 530836dfaa9e
Bye, Docker!
Removing intermediate container 530836dfaa9e
 ---> bbcfb9810a67
Successfully built bbcfb9810a67

Step 3/4 には変更がないのでキャッシュが利用されると嬉しいが、実際には利用されない。キャッシュが利用されていれば Step 2/4 のように Using cache と表示される。

解説

RUN のスクリプト中で ARG の値を使うことができるのは、暗黙で再宣言されるためだ。 ARG はあくまで Docker の引数であり、そのままではスクリプト中で参照できないのだと思われる。以下のようなイメージになる。

RUN $GREETING=Bye; echo "$GREETING, Docker!"

問題なのは、実際にスクリプト中でその値が使われていなくても、暗黙で宣言されるためにキャッシュが効かなくなってしまうことだ。

対策

長々と解説したが、使う直前に宣言すれば解決できる。

FROM alpine

RUN echo "Start process..."

# ~~~~~~~~~~~~~~~~
# 様々な処理
# ~~~~~~~~~~~~~~~~

ARG GREETING=Hello
RUN echo "$GREETING, Docker!"

参考

公式リファレンス: https://docs.docker.com/engine/reference/builder/#impact-on-build-caching

コメントを残す

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