草庐IT

docker - 从 Ubuntu amd64 到 arm7l : exec user process caused "exec format error" 进行交叉编译

coder 2024-07-06 原文

从 amd64 到 arm7l 的交叉编译让我头疼

我终于可以用 Gitlab CI 做到这一点,所以现在,我在 docker 镜像中编译我的二进制文件,这是 dockerfile:

FROM golang

WORKDIR /go/src/gitlab.com/company/edge_to_bc

COPY . .
RUN dpkg --add-architecture armhf && apt update && apt-get install -y gcc-arm-linux-gnueabihf libltdl-dev:armhf

我将其构建为 然后我将使用名称 ubuntu:cross-compil

构建新容器“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.7

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

WORKDIR /sunclient/

COPY ./release/edge_to_bc ./
EXPOSE 5555

CMD [ "./edge_to_bc" ]

但是当我在我的 ARM 板上运行它时:

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"

这表明二进制仍然没有正确的格式...

我错过了什么?我在这个主题上花了很多时间,没有更多的想法。

当我检查二进制的架构时,这是我得到的:

 edge_to_bc git:(master) ✗ readelf -h ./release/edge_to_bc
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           ARM
  Version:                           0x1
  Entry point address:               0x19209
  Start of program headers:          52 (bytes into file)
  Start of section headers:          23993360 (bytes into file)
  Flags:                             0x5000400, Version5 EABI, hard-float ABI
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         10
  Size of section headers:           40 (bytes)
  Number of section headers:         49
  Section header string table index: 48

在目标操作系统上,这是我得到的:

[root@gw-sol1 ~]# uname -a
Linux gw-sol-1 4.4.113-UNRELEASED #1 SMP PREEMPT Thu Mar 7 16:46:40 CET 2019 armv7l armv7l armv7l GNU/Linux

编辑:

当我直接在 ARM 设备上构建应用程序时,它将运行:

go build -o ./release/edge_to_bc -v -ldflags '-w -s -extldflags "-static"' ./...

小 Sprite :

ELF Header:
  Magic:   7f 45 4c 46 01 01 01 03 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - GNU
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           ARM
  Version:                           0x1
  Entry point address:               0x125f1
  Start of program headers:          52 (bytes into file)
  Start of section headers:          16594072 (bytes into file)
  Flags:                             0x5000400, Version5 EABI, hard-float ABI
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         7
  Size of section headers:           40 (bytes)
  Number of section headers:         38
  Section header string table index: 37

它看起来与另一个非常相似,至少在架构上是这样。

然后构建docker镜像:

docker build . -t image/peer-test:armhf

最佳答案

当您使用以下命令在 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 外部进行编译然后进行第二次构建。我还指定了构建主机的平台,并使用目标架构和操作系统变量来配置 go 以进行交叉编译。在这种情况下,我使用了一个完全静态链接的二进制文件,因此没有要包含的库,并且在我的最终发布镜像中只有复制命令,我避免了在不同平台上运行构建的问题。

# 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 服务器)运行构建。

关于docker - 从 Ubuntu amd64 到 arm7l : exec user process caused "exec format error" 进行交叉编译,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56634558/

有关docker - 从 Ubuntu amd64 到 arm7l : exec user process caused "exec format error" 进行交叉编译的更多相关文章

  1. ruby-on-rails - rails : "missing partial" when calling 'render' in RSpec test - 2

    我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou

  2. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

    我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

  3. ruby - 在 64 位 Snow Leopard 上使用 rvm、postgres 9.0、ruby 1.9.2-p136 安装 pg gem 时出现问题 - 2

    我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po

  4. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  5. ruby - 检查 "command"的输出应该包含 NilClass 的意外崩溃 - 2

    为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar

  6. ruby-on-rails - 迷你测试错误 : "NameError: uninitialized constant" - 2

    我遵循MichaelHartl的“RubyonRails教程:学习Web开发”,并创建了检查用户名和电子邮件长度有效性的测试(名称最多50个字符,电子邮件最多255个字符)。test/helpers/application_helper_test.rb的内容是:require'test_helper'classApplicationHelperTest在运行bundleexecraketest时,所有测试都通过了,但我看到以下消息在最后被标记为错误:ERROR["test_full_title_helper",ApplicationHelperTest,1.820016791]test

  7. ruby-on-rails - 相关表上的范围为 "WHERE ... LIKE" - 2

    我正在尝试从Postgresql表(table1)中获取数据,该表由另一个相关表(property)的字段(table2)过滤。在纯SQL中,我会这样编写查询:SELECT*FROMtable1JOINtable2USING(table2_id)WHEREtable2.propertyLIKE'query%'这工作正常:scope:my_scope,->(query){includes(:table2).where("table2.property":query)}但我真正需要的是使用LIKE运算符进行过滤,而不是严格相等。然而,这是行不通的:scope:my_scope,->(que

  8. 使用 ACL 调用 upload_file 时出现 Ruby S3 "Access Denied"错误 - 2

    我正在尝试编写一个将文件上传到AWS并公开该文件的Ruby脚本。我做了以下事情:s3=Aws::S3::Resource.new(credentials:Aws::Credentials.new(KEY,SECRET),region:'us-west-2')obj=s3.bucket('stg-db').object('key')obj.upload_file(filename)这似乎工作正常,除了该文件不是公开可用的,而且我无法获得它的公共(public)URL。但是当我登录到S3时,我可以正常查看我的文件。为了使其公开可用,我将最后一行更改为obj.upload_file(file

  9. ruby - 安装 Ruby 时遇到问题(无法下载资源 "readline--patch") - 2

    当我尝试安装Ruby时遇到此错误。我试过查看this和this但无济于事➜~brewinstallrubyWarning:YouareusingOSX10.12.Wedonotprovidesupportforthispre-releaseversion.Youmayencounterbuildfailuresorotherbreakages.Pleasecreatepull-requestsinsteadoffilingissues.==>Installingdependenciesforruby:readline,libyaml,makedepend==>Installingrub

  10. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

随机推荐