Golang 与 docker 镜像内的 CGO 交叉编译

rav*_*mar 9 gcc go cgo docker buildx

要求:应用程序必须容器化为 docker 镜像,并且需要支持arm64amd64架构。

代码库:它是一个 golang 应用程序,需要使用git2go库,并且必须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"]
Run Code Online (Sandbox Code Playgroud)

构建步骤

docker buildx create --name gitops --use
docker buildx build --platform=linux/amd64,linux/arm64 --pull .
Run Code Online (Sandbox Code Playgroud)

此设置有效,但在构建不同的拱门时构建花费的时间太长。此特定构建步骤之间的时间差: RUN CGO_ENABLED=1 GO111MODULE=on GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -tags static,system_libgit2 -a -o gitoperations main.go在构建不同的架构时始终长 10 倍:

例子:

  1. 在arm64 M1 mac(没有rossetta)上:构建arm64可执行文件需要约30秒,amd64需要约300秒。
  2. 在我们的 amd64 Jenkins CI 系统上:构建 arm64 可执行文件比构建 amd64 可执行文件花费的时间长 10 倍。

通过查看docker buildx build命令输出可以看到此构建时间。
我相信(而且我肯定是错的)它的发生是因为 dockerqemu在构建与主机的 cpu 架构不同的 cpu 架构时使用模拟。所以我想利用golang交叉编译功能来加快构建时间。

我尝试过的:我想builder通过尝试以下语法在这个用于arm和amd arch的dockerfile中有一个阶段:
FROM --platform=$BUILDPLATFORM golang:1.17.6-alpine3.15 as builder。但是在对 dockerfile 进行此更改后使用相同的 docker 构建命令会出现构建错误,这是我在 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'
Run Code Online (Sandbox Code Playgroud)

阅读golang CGO 文档后,我认为发生此错误是因为go没有选择c能够为两种架构构建的正确编译器,并且我需要设置CCenv 变量来指示使用go哪个编译器。c

问题:我是否正确地假设这qemu是导致构建时间差异的原因,并且可以通过使用 golang 的本机交叉编译功能来减少它?
我如何go build使用 docker 桌面从任何主机进行 amd64 和 arm64 编译,因为我没有任何使用C代码的经验,gcc并且我不确定如果我需要支持and ,应该CC在命令中为 flag设置什么值?go buildlinux/amd64linux/arm64

Ber*_* Ay 0

为了能够随时随地编译 C 代码,您需要将CC变量设置为arm交叉编译器。您可以CC通过 来查看您的变量go env。您遇到的错误与您使用的主机系统中的本机编译器有关。你应该apk add gcc-arm-none-eabi在你的 dockerfile 中。下载好必要的交叉编译工具后。您需要将 gcc 命令链接到通过我提到的命令下载的编译器。然后您应该能够为arm64 编译您的应用程序。

您也可以分享您的go env输出吗?您可能还需要编辑GOGCCFLAGS变量。