有关多阶段构建的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}"

每一個參數都設置如下所示。

ARG 名設定内容ARG1グローバルな ARG として設定。デフォルト値も指定。ARG2グローバルな ARG として設定。デフォルト値は指定しない。ARG3グローバルな ARG として設定しない。

此外,每个阶段都是这样设定的。

Stage 名設定内容first_stage各 ARG を宣言。ステージ内のデフォルト値を設定。second_stage各 ARG を宣言。ステージ内のデフォルト値は設定しない。third_stageARG を宣言しない。

–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 を構築するので環境を汚しません!

请多多关照!

真的结束了。

广告
将在 10 秒后关闭
bannerAds