1. 概述
Docker 提供了多阶段构建(Multi-stage builds)功能,允许开发者定义多个构建阶段,每个阶段都可以拥有独立的指令集。这有助于优化最终镜像的体积。在多阶段构建中,一个常见的挑战是在不同阶段之间传递变量。
跨阶段复制变量非常有用,它允许我们在后续阶段中复用前面阶段中定义的值。
在本教程中,我们将探讨三种在多阶段 Docker 构建中复制变量的方法:使用 ARG、使用 ENV 和使用文件。我们将了解每种方法的工作原理,并通过示例说明其使用方式。
2. 使用 ARG
在多阶段 Docker 构建中,使用 ARG 是在不同阶段之间复制变量的最简单方式。ARG 变量可以在构建过程中使用,并且可以通过 docker build
命令以构建参数的形式传入。
来看一个使用 ARG 的 Dockerfile 示例:
# 阶段 1
FROM alpine AS stage1
ARG MY_VAR
RUN echo MY_VAR value is $MY_VAR
# 阶段 2
FROM alpine AS stage2
ARG MY_VAR
RUN echo MY_VAR is still set to: $MY_VAR
在上面的例子中,我们在两个阶段中都使用 ARG
定义了一个名为 MY_VAR
的变量,并通过 RUN
指令在每个阶段中打印其值。
构建镜像并传入变量值的命令如下:
$ docker build --build-arg MY_VAR=somevalue -t my_image .
输出结果如下:
$ docker build --build-arg MY_VAR=somevalue -t my_image .
Sending build context to Docker daemon 2.048kB
Step 1/6 : FROM alpine AS stage1
---> b2aa39c304c2
Step 2/6 : ARG MY_VAR
---> Running in 38b3ef3093c3
Removing intermediate container 38b3ef3093c3
---> 5619deec98e2
Step 3/6 : RUN echo MY_VAR value is $MY_VAR
---> Running in 1bf3db328de6
MY_VAR value is somevalue
Removing intermediate container 1bf3db328de6
---> 5e9f9bc1255a
Step 4/6 : FROM alpine AS stage2
---> b2aa39c304c2
Step 5/6 : ARG MY_VAR
---> Running in 19d18d18f055
Removing intermediate container 19d18d18f055
---> 842a58a44c8e
Step 6/6 : RUN echo MY_VAR is still set to: $MY_VAR
---> Running in 88ba4ea6188b
MY_VAR is still set to: somevalue
Removing intermediate container 88ba4ea6188b
---> 6b912736db48
Successfully built 6b912736db48
Successfully tagged my_image:latest
从输出中可以看出,ARG
定义的 MY_VAR
在两个阶段中都保留了其值。
✅ 优点:适用于构建时传参,不污染最终镜像
❌ 缺点:不能持久化到最终镜像中
3. 使用 ENV
另一种在多阶段 Docker 构建中传递变量的方式是使用 ENV。ENV 定义的变量在构建过程中可用,并且会持久化到最终镜像中。
来看一个使用 ENV 的 Dockerfile 示例:
FROM alpine:latest as base
ENV MY_ENV="my_env"
FROM base
RUN echo ${MY_ENV}
在这个例子中,我们在第一个阶段定义了一个环境变量 MY_ENV
,它在后续阶段中仍然可用。
执行构建命令如下:
$ docker build --no-cache -t my_image .
输出如下:
Step 1/4 : FROM alpine:latest as base
---> b2aa39c304c2
Step 2/4 : ENV MY_ENV="my_env"
---> Running in 7ffd89e261a2
Removing intermediate container 7ffd89e261a2
---> 17e87c9864c3
Step 3/4 : FROM base
---> 17e87c9864c3
Step 4/4 : RUN echo ${MY_ENV}
---> Running in 337470d2041d
my_env
Removing intermediate container 337470d2041d
---> 8e6abc31f316
Successfully built 8e6abc31f316
Successfully tagged my_image:latest
从 Step 4 的输出可以看到,MY_ENV
的值成功从第一个阶段传递到了第二个阶段。
⚠️ 注意:ENV 变量是持久化的,适用于需要在最终镜像中访问的变量。
✅ 优点:变量可持久化到最终镜像中
❌ 缺点:无法通过命令行传参
4. 使用文件
当需要在多个阶段之间共享大量变量时,使用文件是一个非常实用的方法。这种方式通过在前一个阶段将变量写入文件,在后续阶段读取该文件来实现变量传递。
来看一个使用文件传递变量的 Dockerfile 示例:
# 阶段 1 - Build
FROM alpine AS build
# 将变量值写入文件
RUN echo "hello" > /tmp/myvar
# 阶段 2 - Run
FROM alpine AS run
# 从 build 阶段复制文件
COPY --from=build /tmp/myvar /tmp/myvar
# 读取文件内容并赋值给变量
RUN MY_VAR=$(cat /tmp/myvar) && echo "Running the app with MY_VAR=$MY_VAR"
构建命令如下:
$ docker build --no-cache -t my_image .
输出如下:
Step 1/6 : FROM alpine AS build
---> b2aa39c304c2
Step 2/6 : RUN echo "hello" > /tmp/myvar
---> Running in db7cbe1a060f
Removing intermediate container db7cbe1a060f
---> 3c64d471fe3a
Step 3/6 : RUN echo "Building the app..."
---> Running in 08c4d8024c53
Building the app...
Removing intermediate container 08c4d8024c53
---> 4cc6f20768e5
Step 4/6 : FROM alpine AS run
---> b2aa39c304c2
Step 5/6 : COPY --from=build /tmp/myvar /tmp/myvar
---> 3dc259424cb6
Step 6/6 : RUN MY_VAR=$(cat /tmp/myvar) && echo "Running the app with MY_VAR=$MY_VAR"
---> Running in 961f02320a71
Running the app with MY_VAR=hello
Removing intermediate container 961f02320a71
---> 1a8c5349f42b
Successfully built 1a8c5349f42b
Successfully tagged my_image:latest
我们还可以验证最终镜像中该文件的内容:
$ docker run --rm my_image /bin/sh -c "cat /tmp/myvar"
hello
✅ 优点:适合传递多个变量,支持在最终镜像中访问
❌ 缺点:需要手动处理文件读写,略微繁琐
5. 总结
在本教程中,我们介绍了三种在多阶段 Docker 构建中复制变量的方法:
方法 | 是否持久化 | 是否可传参 | 适用场景 |
---|---|---|---|
ARG | ❌ 否 | ✅ 是 | 构建时传参,不污染镜像 |
ENV | ✅ 是 | ❌ 否 | 需要在最终镜像中使用的变量 |
文件 | ✅ 是 | ✅ 是 | 多变量传递或需持久化 |
根据不同的使用场景选择合适的方式,可以更高效地管理多阶段构建流程。希望本文能帮助你避免在实际项目中“踩坑”!