需求: 一个应用程序必须作为Docker镜像进行容器化,并且需要支持arm64
和amd64
架构。
代码库: 这是一个使用git2go库的golang应用程序,必须具有CGO_ENABLED=1
才能构建项目。最小可重现示例可以在这里的github链接中找到。
主机机器: 我正在使用arm64 M1 mac和docker桌面版构建应用程序,但在我们的amd64 Jenkins CI构建系统上结果相似。
Dockerfile:
FROM golang:1.17.6-alpine3.15 as builder
WORKDIR /workspace
COPY go.mod go.mod
COPY go.sum go.sum
RUN apk add --no-cache libgit2 libgit2-dev git gcc g++ pkgconfig
RUN go mod download
COPY main.go main.go
ARG TARGETARCH TARGETOS
RUN CGO_ENABLED=1 GO111MODULE=on GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -tags static,system_libgit2 -a -o gitoperations main.go
FROM alpine:3.15 as runner
WORKDIR /
COPY --from=builder /workspace/gitoperations .
ENTRYPOINT ["/gitoperations"]
构建步骤:
docker buildx create --name gitops --use
docker buildx build --platform=linux/amd64,linux/arm64 --pull .
这个设置是有效的,但是在构建不同架构时构建时间太长了。这个特定的构建步骤:
RUN CGO_ENABLED=1 GO111MODULE=on GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -tags static,system_libgit2 -a -o gitoperations main.go
在构建不同架构时的时间差总是比较大:
例如:
- 在 arm64 M1 mac(没有rossetta)上:构建 arm64 可执行文件需要约 30 秒,amd64 需要约 300 秒。
- 在我们的 amd64 Jenkins CI 系统上:构建 arm64 可执行文件所需的时间比构建 amd64 可执行文件所需的时间长 10 倍。
可以通过查看 docker buildx build
命令输出来查看这些构建时间。
我认为(当然我可能错了),这是因为 docker 在构建与主机机器 CPU 架构不同的 CPU 架构时使用了 qemu
模拟。因此,我想利用 golang 的交叉编译能力来加速构建时间。
builder
阶段来处理 arm 和 amd 架构,尝试使用以下语法:FROM --platform=$BUILDPLATFORM golang:1.17.6-alpine3.15 as builder
。
但是,在对 Dockerfile 进行此更改后使用相同的 docker build 命令会导致构建错误。在 arm64 M1 mac 上运行时,我得到了以下错误信息: > [linux/arm64->amd64 builder 9/9] RUN CGO_ENABLED=1 GO111MODULE=on GOOS=linux GOARCH=amd64 go build -tags static,system_libgit2 -a -o gitoperations main.go:
#0 1.219 # runtime/cgo
#0 1.219 gcc: error: unrecognized command-line option '-m64'
阅读了golang CGO文档后,我认为这个错误发生的原因是go
没有选择能够为两种架构构建的正确c
编译器,所以我需要设置CC
环境变量来指示go
使用哪个c
编译器。
问题:我是否正确地假设qemu
导致了构建时间差异,并且可以通过使用golang的本地交叉编译功能来减少?
如何使go build
从任何主机机器上编译出amd64和arm64,使用docker桌面,因为我没有使用C
代码和gcc
的经验,也不确定我应该为CC
标志设置什么值,如果我需要支持linux/amd64
和linux/arm64
?