为了账号安全,请及时绑定邮箱和手机立即绑定

从 Ubuntu amd64 到 arm7l 的交叉编译:exec 用户进程导致

从 Ubuntu amd64 到 arm7l 的交叉编译:exec 用户进程导致

Go
catspeake 2023-06-26 16:25:13
我对从 amd64 到 arm7l 的交叉编译感到头疼我终于可以使用 Gitlab CI 来完成此操作,所以现在,我在 docker 映像中编译二进制文件,这是 dockerfile:FROM golangWORKDIR /go/src/gitlab.com/company/edge_to_bcCOPY . .RUN dpkg --add-architecture armhf && apt update && apt-get install -y gcc-arm-linux-gnueabihf libltdl-dev:armhf我将其构建为然后我将构建新容器“交叉编译”并准备好名称ubuntu:cross-compil现在,我可以使用以下命令编译我的二进制文件:docker run -it -v ${EDGE_TO_BC_PATH}/release:/go/src/gitlab.com/company/edge_to_bc/release ubuntu:cross-compil  bash -c 'CC=arm-linux-gnueabihf-gcc CXX=arm-linux-gnueabihf-g++ CGO_ENABLED=1 GOOS=linux GOARCH=arm GOARM=7 go build -v -o ./release/edge_to_bc '我可以看到生成的可执行文件./release/edge_to_bc然后我构建我的 docker 镜像:docker build -t registry.gitlab.com/company/edge_to_bc:armhf .我推动它。在 Dockerfile 中,我只是从主机复制可执行文件:FROM alpine:3.7RUN apk --no-cache add ca-certificates libtoolWORKDIR /sunclient/COPY ./release/edge_to_bc ./EXPOSE 5555CMD [ "./edge_to_bc" ]但是当我在我的臂板上运行它时:docker run --rm registry.gitlab.com/company/edge_to_bc:armhf我得到:standard_init_linux.go:207: exec user process caused "no such file or directory"调试时,如果我想获取文件列表docker run --rm registry.gitlab.com/company/edge_to_bc:armhf我得到:standard_init_linux.go:207: exec user process caused "exec format error"这表明二进制仍然没有正确的格式......我错过了什么 ?我在这个话题上花了很多时间,并没有更多的想法。
查看完整描述

1 回答

?
眼眸繁星

TA贡献1873条经验 获得超9个赞

当您使用以下命令在arm7映像的amd64系统上运行构建时:


docker build -t registry.gitlab.com/company/edge_to_bc:armhf .

它将使用基础映像并在该映像中运行 amd64 的命令。因此,即使您的单个edge_to_bc二进制文件可以交叉编译,图像的其余部分也不会。接下来,二进制编译命令本身看起来像是链接到库,而这些库很可能不在您的最终映像中。您可以运行ldd edge_to_bc查看这些链接,如果它们丢失,您将收到文件未找到错误。


在我自己的交叉编译测试中,我在 19.03.0-rc2 上使用 BuildKit、Buildx 和一些实验性功能,因此其中的某些部分不向后兼容,但希望对您有所帮助。我使用多阶段构建来避免在 docker 之外进行编译,然后进行第二次构建。我还指定了构建主机的平台,并使用目标架构和操作系统变量来配置交叉编译。在这种情况下,我使用了完全静态链接的二进制文件,因此没有要包含的库,并且在最终发布映像中仅使用复制命令,我避免了在不同平台上运行构建的问题。


# syntax=docker/dockerfile:experimental

# ^ above line must be at the beginning of the Dockerfile for BuildKit


# --platform pulls an image for the build host rather than target OS/Arch

FROM --platform=$BUILDPLATFORM golang:1.12-alpine as dev

RUN apk add --no-cache git ca-certificates

RUN adduser -D appuser

WORKDIR /src

COPY . /src/

CMD CGO_ENABLED=0 go build -o app . && ./app


# my dev stage is separate from build to allow mounting source and rebuilding

# on developer machines    

FROM --platform=$BUILDPLATFORM dev as build

ARG TARGETPLATFORM

ARG TARGETOS

ARG TARGETARCH

# --mount is an experimental BuildKit feature

RUN --mount=type=cache,id=gomod,target=/go/pkg/mod/cache \

    --mount=type=cache,id=goroot,target=/root/.cache/go-build \

    CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \

    go build -ldflags '-w -extldflags -static' -o app .

USER appuser

CMD [ "./app" ]


# this stage will have the target OS/Arch and cannot have any RUN commands

FROM scratch as release

COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/

COPY --from=build /etc/passwd /etc/group /etc/

COPY --from=build /src/app /app

USER appuser

CMD [ "/app" ]


FROM scratch as artifact

COPY --from=build /src/app /app


FROM release

为了构建这个,我可以使用 BuildKit 运行一次性构建命令:


DOCKER_BUILDKIT=1 docker build --platform=linux/amd64 -f Dockerfile -t golang-app:amd64 .

DOCKER_BUILDKIT=1 docker build --platform=linux/arm64 -f Dockerfile -t golang-app:arm64 .

但更好的是 Buildx 版本,它创建了一个可以在多个平台上运行的多架构映像。这确实需要您推送到注册表服务器:


docker buildx build -f Dockerfile --platform linux/amd64,linux/arm64 \

  -t ${docker_hub_id}/golang-app:multi-arch --output type=registry .

对于您的场景,您可以将 arm64 的引用替换为您自己的架构。该--platform选项在我运行的许多命令中被列为实验性选项,因此您可能需要在 /etc/docker/daemon.json 文件中配置以下内容才能启用:


{ "experimental": true }

我相信这需要完全重新启动 docker 守护进程 ( systemctl restart docker),而不仅仅是重新加载才能生效。


为了从构建中提取工件(特定架构的编译二进制文件),我使用:


docker build -f Dockerfile --target artifact -o type=local,dest=. .

这会将工件阶段(单个二进制文件)的内容输出到本地目录。


以上是 Docker 构建多架构镜像的方法列表中的选项 3。


选项 1 是使用 binfmt_misc 配置 qemu 来为不同平台构建和运行映像。我还无法通过 Buildx 在 Linux 上实现这一点,但 Docker 已经在 Mac 上实现了这一点,您也许可以找到有关他们在 LinuxKit 项目中所做的更多详细信息。如果您需要运行命令并安装其他工具作为构建的一部分,而不仅仅是交叉编译单个静态链接的二进制文件,那么根据您的情况使用 qemu 可能是理想的选择。


选项 2 是在目标平台上运行构建,正如您所看到的,效果很好。使用Buildx,您可以添加多个构建节点,每个平台最多一个,从而允许您从单个位置(CI 服务器)运行构建。


查看完整回答
反对 回复 2023-06-26
  • 1 回答
  • 0 关注
  • 221 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信