Когда вы запускаете сборку в системе amd64 образа arm7 с помощью этой команды:
docker build -t registry.gitlab.com/company/edge_to_bc:armhf .
Он будет использовать базовые образы и запускать команды в этом образе для amd64. Таким образом, даже если ваш двоичный файл edge_to_bc
может быть скомпилирован, остальная часть изображения - нет. Далее, сама команда бинарной компиляции выглядит так, как будто она соединяется с библиотеками, которых, скорее всего, нет в вашем конечном образе. Вы можете запустить ldd edge_to_bc
, чтобы просмотреть эти ссылки, и, если они отсутствуют, вы получите сообщение об ошибке «файл не найден».
В моем собственном тесте кросс-компиляции я использую BuildKit, Buildx и некоторые экспериментальные функции на 19.03.0-rc2, так что будут некоторые части, которые не имеют обратной совместимости, но, надеюсь, вы найдете их полезными. Я использую многоэтапную сборку, чтобы избежать компиляции вне докера, а затем вторую сборку. Я также указываю платформу для хоста сборки и использую целевые значения arch и переменные ОС для настройки иди кросс-компиляции. В этом сценарии я использовал полностью статически связанный двоичный файл, поэтому не было никаких библиотек для включения, и только с командами копирования в моем образе окончательного выпуска я избегал проблем при запуске сборки на другой платформе.
# 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=. .
Это выведет содержимое стадии артефакта (один двоичный файл) в локальный каталог.
Выше приведен вариант 3 в списке Docker способов создания многоархивных изображений.
Вариант 1 - настроить qemu с binfmt_misc для создания и запуска образов для разных платформ. Я еще не смог заставить это работать на Linux с Buildx, но Docker сделал это для Mac, и вы, возможно, сможете найти более подробную информацию о том, что они сделали в проекте LinuxKit. Использование qemu для вашей ситуации может быть идеальным, если вам нужно запускать команды и устанавливать другие инструменты как часть вашей сборки, а не просто кросс-компилировать один статически связанный двоичный файл.
Вариант 2 - запустить сборку на целевой платформе, которая, как вы видели, работает хорошо. С Buildx вы можете добавить несколько узлов сборки, по одному на каждую платформу, что позволяет запускать сборку из одного места (сервер CI).