掌握了这篇 Dockerfile 中的 ARG 作用域, 就算 Build 镜像 CI 入门了
image
原文链接: https://tangx.in/posts/2020/11/06/dockerfiles-args-scope/
之前我们讨论了 面试问 Dockerfile 的优化, 千万不要只会说减少层数[1], 详细说明 Dockerfile 的优化原理和操作方法, 给大家概括了 简单易记 的口诀。
面试问 Dockerfile 的优化, 千万不要只会说减少层数
今天, 我们继续来探讨一下, Dockerfile 中的另外一个利器 -- ARG
。
如果说掌握 Dockerfile 的优化, 勉强算作读完秘籍的目录。那只有 熟练 掌握了 ARG
的用法, 我们才是打出了神功的 第一拳 。
先来说说 ARG
官网文档中有介绍 Build time variables - build-arg[2] , 从名字可以可以看到, ARG 是在构建时生效的。
通过 --build-arg=KEY=VALUE
这样的参数形式, 我们可以在构建时传入 实际 值, 而非在 Dockerfile 中 预设 值。优势就是使用 ARG
可以有效的复用 Dockerfile。
上面的的文字比较拗口, 换句话说, 我们能完成更 通用
的构建 CI 模版, 兼容更多场景, 早点下班。
简单的 Dockerfile ARG 案例
下面是一个 Dockerfile, 通过 ARG
VERSION 字段传入需要使用的 alpine
版本
# 1.Dockerfile
ARG VERSION
FROM alpine:${VERSION}
通过 Makefile 管理了两个编译命令
代码语言:javascript复制
version ?= latest
build:
docker build -f 1.Dockerfile -t example.com/demo/arg:$(version) . --no-cache --build-arg=VERSION=$(version)
build.1:
version=3.11 make build
version=3.12 make build
执行结果, 注意看 version 部分
代码语言:javascript复制$ make build
docker build -f 1.Dockerfile -t example.com/demo/arg:latest . --no-cache --build-arg=VERSION=latest
=> [internal] load metadata for docker.io/library/alpine:latest
$ make build.1
version=3.11 make build
docker build -f 1.Dockerfile -t example.com/demo/arg:3.11 . --no-cache --build-arg=VERSION=3.11
=> [internal] load metadata for docker.io/library/alpine:3.11
version=3.12 make build
docker build -f 1.Dockerfile -t example.com/demo/arg:3.12 . --no-cache --build-arg=VERSION=3.12
=> [internal] load metadata for docker.io/library/alpine:3.12
ARG 有没有作用域?
但是 ARG
是可以定义在在 Dockerfile 中的任意位置的, 那么 ARG 变量有没有 作用域 呢?或者说 ARG 变量的 生效范围 是什么。
接下来我们通过实验确认
实验过程
- 创建 Dockerfile 如下
## 在第一个 FROM 之前的所有 ARG , 在所有 FROM 中生效, 仅在 FROM 中生效
ARG image
FROM $image as stage1
RUN echo "stage1 -> base from image is : $image "
# result: stage1 -> base from image is :
FROM $image as stage11
RUN echo "stage11 -> base from image is : $image "
# result: stage11 -> base from image is :
FROM alpine:3.12 as stage2
## 在 FROM 后的 ARG, 仅在当前 FROM 作用域生效。 即尽在当前 阶段 (stage) 生效
ARG image
RUN echo "stage2 -> base from image is : $image "
# stage2 -> base from image is : alpine:3.12
FROM alpine:3.12 as stage21
RUN echo "stage21 -> base from image is : $image "
# stage21 -> base from image is :
- 执行
docker build
命令
# docker build --build-arg image=alpine:3.12 --no-cache .
build
结果展示
Sending build context to Docker daemon 3.072kB
Step 1/10 : ARG image
Step 2/10 : FROM $image as stage1
---> d6e46aa2470d
Step 3/10 : RUN echo "stage1 -> base from image is : $image "
---> Running in ecb7be5dd9cc
stage1 -> base from image is : ### image 结果未输出
Removing intermediate container ecb7be5dd9cc
---> 04807c8d53be
Step 4/10 : FROM $image as stage11
---> d6e46aa2470d
Step 5/10 : RUN echo "stage11 -> base from image is : $image "
---> Running in a90e45076345
stage11 -> base from image is : ### image 结果未输出
Removing intermediate container a90e45076345
---> f2dbce837a1b
Step 6/10 : FROM alpine:3.12 as stage2
---> d6e46aa2470d
Step 7/10 : ARG image
---> Running in 5c8cec4c2f22
Removing intermediate container 5c8cec4c2f22
---> 999d9990bd91
Step 8/10 : RUN echo "stage2 -> base from image is : $image "
---> Running in 4407dcb0e0bb
stage2 -> base from image is : alpine:3.12 ### image 结果输出
Removing intermediate container 4407dcb0e0bb
---> e5ddd7a84f81
Step 9/10 : FROM alpine:3.12 as stage21
---> d6e46aa2470d
Step 10/10 : RUN echo "stage21 -> base from image is : $image "
---> Running in 64a0a3bb090c
stage21 -> base from image is : ### image 结果未输出
Removing intermediate container 64a0a3bb090c
---> 82665f9a1037
Successfully built 82665f9a1037
对照组解析
在随后的 Dockerfile 中, 只定义了一个变量 image
, 并在 FROM
和 stage 中重复使用
- 对照组1:
stage1
和stage11
均在FROM
中使用了变量$image
: **作用域在所有FROM
中- 成功拉取
FROM $image
并完成 layer 构建 - 但是在
RUN
中无法正确输出结果,即 image 的值 alpine:3.12
- 成功拉取
- 对照组2:
stage1
vsstage2
: 作用域在 FROM stage 内部
- 在
stage2
的作用域中声明了ARG image
,且能正确输出结果。
- 对照组3:
stage2
vsstage21
: 作用域仅在当前FROM stage
内部- 虽然
stage2
在stage21
上方且声明了ARG image
, 但stage21
仍然不能不能正确输出结果。
- 虽然
结论
在 Docker 官网文档 Understand how ARG and FROM interact[3], 也有提到 ARG 的作用域范围
- FROM instructions support variables that are declared by any ARG instructions that occur before the first FROM.
- An ARG declared before a FROM is outside of a build stage, so it can’t be used in any instruction after a FROM. To use the default value of an ARG declared before the first FROM use an ARG instruction without a value inside of a build stage:
- 在第一个
FROM
之前的所有 ARG , 在所有FROM
中生效, 仅在FROM
中生效 - 在
FROM
后的ARG
, 仅在当前FROM
作用域生效。即尽在当前 阶段 (stage) 生效
这么说比较抽象, 用一个案例概括,一张图可以很好的展示
- VERSION 在
FROM
前, 全局 且 仅在 FROM 中生效, 即3,5,15
行。 - DATE 在
FROM
后, 在 stage1 内, 仅在 stage1 中生效, 即7-14
行。 - TIME 在
FROM
后, 在 stage1 内, 仅在 stage1 中生效, 即11-14
行。
build-arg-scope
参考资料
[1]
面试问 Dockerfile 的优化, 千万不要只会说减少层数: https://tangx.in/posts/2019/03/26/how-to-build-a-image-with-dockerfile/
[2]
Build time variables - build-arg: https://docs.docker.com/engine/reference/commandline/build/#set-build-time-variables---build-arg
[3]
Understand how ARG and FROM interact: https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact