有关多阶段构建的Dockerfile中常见的ARG错误问题
太长不看 (TL;DR)
在使用多阶段构建的 Dockerfile 中使用 ARG 时,请注意作用域仅限于各个阶段内!确保为每个阶段声明使用的 ARG 变量!
此外,如果在多个阶段中使用的 ARG 变量有默认值,则应定义全局的 ARG 变量,并在其中列出默认值!
如果用这种方式制作的话,没问题!
# 各ステージで共通して利用する ARG にデフォルト値があるのなら、最初の FROM より前に定義する。
ARG YOUR_ARG="Default value"
FROM alpine:latest as first_stage
# ARG は、利用する各ステージごとに宣言する必要がある。
ARG YOUR_ARG
RUN echo "1st stage: ${YOUR_ARG}"
FROM alpine:latest as second_stage
# ARG は、利用する各ステージごとに宣言する必要がある。
ARG YOUR_ARG
RUN echo "2nd stage: ${YOUR_ARG}"
关于多阶段构建和 ARG 的关系
根据多阶段构建,据说 ARG 和 ENV 的作用域将受到每个阶段的限制。大家知道这一点吗?我在创建 Dockerfile 的阶段内,才意识到无法在其中引用 ARG 的值,才第一次了解到这一点。
根据这条评论来看,
正确的,Dockerfile的指令,包括ENV变量和ARG,它们的作用范围是每个构建阶段,并且不会在下一个阶段中保留;这是按照设计的。然而,您可以在每个构建阶段中使用一个全局的ARG(在第一个构建阶段之前设置),并且在每个构建阶段中使用该值。
听说了。真的吗?
这个公式文档上也很清楚写着呢。
当使用 ARG 命令时,变量的作用域仅限于定义它的构建阶段结束之前。如果在多个构建阶段中使用 ARG,则需要为每个阶段单独指定 ARG 命令。
最初にドキュメント読んだときは、ビルドステージの意味も分からないまま「あーそーゆーことね完全に理解した」と思ったような気が。。。
我們來看看 ARG 的運作情況
让我们实际观察一下使用多阶段构建的 ARG 的运作。
這篇稿件的測試環境。
在写这篇稿件时,我们已经在以下版本上进行了操作确认。
# docker --version
Docker version 18.09.1, build 4c52b90
Dockerfile: Dockerfile
以下是本次验证使用的Dockerfile。
# ARG1 は、グローバルに宣言し、グローバルなデフォルト値を設定する。
ARG ARG1="arg1 global default value"
# ARG2 は、グローバルに宣言するが、グローバルなデフォルト値は設定しない。
ARG ARG2
# ARG3 は、グローバルに宣言しない。
# ARG ARG3
FROM alpine:latest as first_stage
# first_stage では、各 ARG を宣言し、スコープ内のデフォルト値を設定する。
ARG ARG1="arg1 first stage value"
ARG ARG2="arg2 first stage value"
ARG ARG3="arg3 first stage value"
RUN echo -e "first_stage:\n\tARG1=${ARG1}\n\tARG2=${ARG2}\n\tARG3=${ARG3}"
FROM alpine:latest as second_stage
# second_stage では、各 ARG を宣言するが、スコープ内のデフォルト値は設定しない。
ARG ARG1
ARG ARG2
ARG ARG3
RUN echo -e "second_stage:\n\tARG1=${ARG1}\n\tARG2=${ARG2}\n\tARG3=${ARG3}"
FROM alpine:latest as third_stage
# third_stage では、すべての ARG を宣言しない。
# ARG ARG1
# ARG ARG2
# ARG ARG3
RUN echo -e "third_stage:\n\tARG1=${ARG1}\n\tARG2=${ARG2}\n\tARG3=${ARG3}"
每一個參數都設置如下所示。
此外,每个阶段都是这样设定的。
–build-arg 指定なしでビルド
在构建 Docker 镜像时,尝试不使用 –build-arg 指定值进行构建。
# docker build . --no-cache
Sending build context to Docker daemon 14.85kB
...snip...
Step 7/14 : RUN echo -e "1st stage:\n\tARG1=${ARG1}\n\tARG2=${ARG2}\n\tARG3=${ARG3}"
---> Running in 2bbe78634ee8
first_stage:
ARG1=arg1 first stage value
ARG2=arg2 first stage value
ARG3=arg3 first stage value
...snip...
Step 12/14 : RUN echo -e "2nd stage:\n\tARG1=${ARG1}\n\tARG2=${ARG2}\n\tARG3=${ARG3}"
---> Running in 0c28af93ea9b
second_stage:
ARG1=arg1 global default value
ARG2=
ARG3=
...snip...
Step 14/14 : RUN echo -e "3rd stage:\n\tARG1=${ARG1}\n\tARG2=${ARG2}\n\tARG3=${ARG3}"
---> Running in cbca9ed88691
third_stage:
ARG1=
ARG2=
ARG3=
...snip...
-
- first_stage では、いずれもステージ内のデフォルト値となりました。 ARG1 を見ると、グローバルなデフォルト値よりもステージ内のデフォルト値が優先されていることがわかります。これは、よりスコープが狭い方の値を優先的に使う動きになっており、理にかなっている動きと言えるでしょう。
-
- second_stage では、 ARG1 のみグローバルなデフォルト値が表示されています。ステージ内で ARG を宣言はしたものの、何も値を指定しなければグローバルなデフォルト値を利用するということですね。 ARG2, ARG3 には何も表示されていませんが、これはその値を指定している場所がどこにもないからですね。
- third_stage ではすべての値が表示されていません。 ARG1 のグローバルなデフォルト値すら表示されていないということは、 ARG を利用するためにはそのステージごとに宣言する必要があるということでしょう。
用 build-arg 参数进行构建
这次我们尝试使用 –build-arg 来指定值进行构建。
# docker build --build-arg ARG1="build arg1 value" --build-arg ARG2="build arg2 value" --build-arg ARG3="build arg3 value" . --no-cache
Sending build context to Docker daemon 14.85kB
...snip...
Step 7/14 : RUN echo -e "1st stage:\n\tARG1=${ARG1}\n\tARG2=${ARG2}\n\tARG3=${ARG3}"
---> Running in 10b37c5a524b
first_stage:
ARG1=build arg1 value
ARG2=build arg2 value
ARG3=build arg3 value
...snip...
Step 12/14 : RUN echo -e "2nd stage:\n\tARG1=${ARG1}\n\tARG2=${ARG2}\n\tARG3=${ARG3}"
---> Running in e70e3ff9fe9b
second_stage:
ARG1=build arg1 value
ARG2=build arg2 value
ARG3=build arg3 value
...snip...
Step 14/14 : RUN echo -e "3rd stage:\n\tARG1=${ARG1}\n\tARG2=${ARG2}\n\tARG3=${ARG3}"
---> Running in e675e8f648e8
third_stage:
ARG1=
ARG2=
ARG3=
...snip...
-
- first_stage, second_stage ともにすべての値が –build-arg で指定した値になりました。これは公式ドキュメントの ARG のデフォルト値で説明されている内容と一致しますね。 –build-arg で指定された値はグローバルなデフォルト値及びステージ内のデフォルト値のどちらよりも優先されます。
- third_stage ではまたもやすべての値が表示されていません。 –build-arg で値が指定されていても、そのステージ内で宣言されていなければやはり利用できないようです。
总结
-
- マルチステージビルドな Dockerfile で ARG を利用する際には、すべてのステージでそれぞれ利用する ARG を宣言する必要がある。
-
- 複数のステージで利用している ARG のデフォルト値を定義したければ、グローバルな ARG を定義(最初の FROM よりも前に定義)してそこにデフォルト値を記載すると良い。
-
- ARG の値の優先度は
–build-arg での指定値 [優先度高]
ステージ内のデフォルト値
グローバルなデフォルト値 [優先度低]
结束了。
广告
这是一项宣传多阶段构建的 ARG 行为的项目,引发了注意的契机。
- docker-minecraft-bedrock
Minecraft の Bedrock Server を楽に運用しようぜ!という趣旨のプロジェクトです。
Minecraft ってたまーにアップデートが入るのですが、クライアントは大体勝手にアップデートされるものの、 Bedrock Server は自動的にアップデートされず、手動で新しいバージョンをダウンロードして Zip ファイルを展開してバイナリファイルを置き換えてサービスを再起動して・・・とかやる必要がありました。
このプロジェクトは、一度セットアップだけやってしまえば、あとは全自動で Bedrock Server をアップデートしてくれます!ついでに Docker を使って Bedrock Server を構築するので環境を汚しません!
请多多关照!
真的结束了。