我有以下 Dockerfile:
FROM golang:1.19.2-bullseye@sha256:2fddf0539591f8e364c9adb3d495d1ba2ca8a8df420ad23b58e7bcee7986ea6c as gobuilder
ARG CGO_ENABLED
ARG CGO_CFLAGS
RUN set -x && \
printf 'package main\nimport (\n\t"fmt"\n\t"os"\n)\nfunc main(){\n\tfmt.Println(os.Getenv("CGO_CFLAGS"))\n}\n' > main.go && \
cat main.go && \
CGO_ENABLED="${CGO_ENABLED}" CGO_CFLAGS="${CGO_CFLAGS}" go build main.go && \
ls && \
CGO_CFLAGS="${CGO_CFLAGS}" ./main
(是的,我知道
as gobuilder
行中的FROM
部分不是必需的,我们暂时忽略它。)
当我构建这个时,
set -x
输出没有正确引用构建参数:
$ docker build -t mre --build-arg CGO_ENABLED=1 --build-arg CGO_CFLAGS='-Wall -Wextra' .
Sending build context to Docker daemon 13.82kB
Step 1/4 : FROM golang:1.19.2-bullseye@sha256:2fddf0539591f8e364c9adb3d495d1ba2ca8a8df420ad23b58e7bcee7986ea6c as gobuilder
---> dce494d5814b
Step 2/4 : ARG CGO_ENABLED
---> Using cache
---> 39d4b3dd0c4e
Step 3/4 : ARG CGO_CFLAGS
---> Using cache
---> bfdf4834ae3b
Step 4/4 : RUN set -x && printf 'package main\nimport (\n\t"fmt"\n\t"os"\n)\nfunc main(){\n\tfmt.Println(os.Getenv("CGO_CFLAGS"))\n}\n' > main.go && cat main.go && CGO_ENABLED="${CGO_ENABLED}" CGO_CFLAGS="${CGO_CFLAGS}" go build main.go && ls && CGO_CFLAGS="${CGO_CFLAGS}" ./main
---> Running in 1936a6076a84
+ printf package main\nimport (\n\t"fmt"\n\t"os"\n)\nfunc main(){\n\tfmt.Println(os.Getenv("CGO_CFLAGS"))\n}\n
+ cat main.go
package main
import (
"fmt"
"os"
)
func main(){
fmt.Println(os.Getenv("CGO_CFLAGS"))
}
+ CGO_ENABLED=1 CGO_CFLAGS=-Wall -Wextra go build main.go
+ ls
bin
main
main.go
src
+ CGO_CFLAGS=-Wall -Wextra ./main
-Wall -Wextra
Removing intermediate container 1936a6076a84
---> a5c293ba4ba8
Successfully built a5c293ba4ba8
Successfully tagged mre:latest
请注意,
main
二进制文件输出预期的环境变量值,但我希望看到CGO_CFLAGS
部分像+ CGO_CFLAGS='-Wall -Wextra' ./main
一样被引用。同样,我期望在 go build
命令中引用以下内容:
+ CGO_ENABLED=1 CGO_CFLAGS='-Wall -Wextra' go build main.go
为了进行比较,我尝试了一个使用不同 Dockerfile 和不同基础映像的示例。
Dockerfile:
FROM alpine:3.17.3
ARG ENV_VAR1
RUN set -x && ENV_VAR1="${ENV_VAR1}" ENV_VAR2='hello world' true 'minimal reproducible example'
构建输出:
$ docker build -t mre2 --build-arg ENV_VAR1='foo bar' .
Sending build context to Docker daemon 13.82kB
Step 1/3 : FROM alpine:3.17.3
---> 9ed4aefc74f6
Step 2/3 : ARG ENV_VAR1
---> Running in 182a965d4c83
Removing intermediate container 182a965d4c83
---> fc5f7f7f2e01
Step 3/3 : RUN set -x && ENV_VAR1="${ENV_VAR1}" ENV_VAR2='hello world' true 'minimal reproducible example'
---> Running in 6ebec9bed99a
+ ENV_VAR1='foo bar' ENV_VAR2='hello world' true 'minimal reproducible example'
Removing intermediate container 6ebec9bed99a
---> 5e1901518b51
Successfully built 5e1901518b51
Successfully tagged mre2:latest
行中注释
+ ENV_VAR1='foo bar' ENV_VAR2='hello world' true 'minimal reproducible example'
两个环境变量都按我的预期引用了。
我希望帮助理解为什么基于
golang:1.19.2-bullseye
基础镜像的 Docker 构建中不包含引号。
对于上下文,我希望能够在我的构建中看到引用,以便我可以确保我的所有命令都按照我的预期被 shell 扩展和引用。但如果引用不起作用,我无法轻易验证这一点。
更好的解决方法是不依赖
set -x
来准确显示您想要的格式。尝试类似的事情
RUN printf '%s\n' 'package main' \
'import (' \
' "fmt"' \
' "os"' \
')' \
'func main(){' \
' fmt.Println(os.Getenv("CGO_CFLAGS"))' \
'}' > main.go && \
cat main.go && \
: basically unnecessary && \
CGO_ENABLED="$CGO_ENABLED" && \
CGO_FLAGS="$CGO_FLAGS" && \
printf '%s=\042%s\042\n' \
CGO_ENABLED "$CGO_ENABLED" \
CGO_FLAGS "$CGO_FLAGS" && \
go build main.go && \
ls && \
./main
输出会稍微少一些(如果您真的想查看所有内容,当然可以放回
set -x
;但要了解输出格式将取决于 shell),但这样,您就可以完全控制 printf
以明确的格式显示变量值。
将
ARG
变量分配回自身基本上是不必要的(正如评论中提到的);但我保留它们只是为了演示如何在当前 RUN
指令的剩余时间内设置一次变量。
(您还会注意到我重构了第一个
printf
。我想如果您真的愿意的话,您可以在源文件中获得制表符缩进;但对我来说,这里的易读性胜过完全控制。)