Is it possible to (manually) build multi-arch docker image without docker buildx?
Asked Answered
D

1

5

I have a Golang project which is built and put into scratch image:

# Build binary
FROM golang:1.17-alpine AS build-env
ADD . /app
WORKDIR /app
RUN env CGO_ENABLED=0 go build -ldflags="-s -w" -o myapp ./cmd/myapp/main.go

# Create image
FROM scratch
COPY --from=build-env /app/myapp /
ENTRYPOINT ["/myapp"]

Since Golang allows to cross-compile for multiple architectures (and it's way faster than building in emulated environments), I want to do something like this:

# Build binary
FROM golang:1.17-alpine AS build-env
ADD . /app
WORKDIR /app
RUN env CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags="-s -w" -o myapp_linux_i386 ./cmd/myapp/main.go             # Linux 32bit
RUN env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o myapp_linux_x86_64 ./cmd/myapp/main.go         # Linux 64bit
RUN env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=5 go build -ldflags="-s -w" -o myapp_linux_arm ./cmd/myapp/main.go      # Linux armv5/armel/arm (it also works on armv6)
RUN env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -ldflags="-s -w" -o myapp_linux_armhf ./cmd/myapp/main.go    # Linux armv7/armhf
RUN env CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="-s -w" -o myapp_linux_aarch64 ./cmd/myapp/main.go        # Linux armv8/aarch64
RUN env CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -ldflags="-s -w" -o myapp_freebsd_x86_64 ./cmd/myapp/main.go     # FreeBSD 64bit
RUN env CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags="-s -w" -o myapp_darwin_x86_64 ./cmd/myapp/main.go       # Darwin 64bit
RUN env CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags="-s -w" -o myapp_darwin_aarch64 ./cmd/myapp/main.go      # Darwin armv8/aarch64
RUN env CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags="-s -w" -o myapp_windows_i386.exe ./cmd/myapp/main.go     # Windows 32bit
RUN env CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" -o myapp_windows_x86_64.exe ./cmd/myapp/main.go # Windows 64bit

But then how do I create and push multi-arch docker images?

# Create image
...
Dulcedulcea answered 6/3, 2022 at 18:4 Comment(2)
Is not using buildx a requirement? Buildx supports cross compiling, and it looks like you want to avoid emulation.Stung
This is not a requirement, but it takes significantly longer to build using emulation. Especially for ARMv6 - like 5-8 minutes instead of 5 seconds without emulation.Dulcedulcea
S
8

From the question, it sounds less like you want to avoid buildx, and more that you want to avoid running the compiler under an interpreter, and instead use Go's builtin cross compiling capabilities. That's built into buildx:

# Build binary
FROM --platform=$BUILDPLATFORM golang:1.17-alpine AS build-env
ADD . /app
WORKDIR /app
ARG TARGETOS
ARG TARGETARCH
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
    go build -ldflags="-s -w" -o myapp ./cmd/myapp/main.go

# Create image
FROM scratch
COPY --from=build-env /app/myapp /
ENTRYPOINT ["/myapp"]

The --platform runs the first stage on the platform where you are building. And the TARGETOS and TARGETARCH are builtin args with buildkit. You can then build with:

docker buildx build \
  --platform="linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/ppc64le,linux/s390x" \
  -t registry/image:tag \
  .

Note that there's no MacOS support for docker, it runs in a Linux VM on that platform. And I don't believe windows/386 is valid either, I've only ever seen docker on amd64 and with possible support for arm64.


Beyond that, there are various ways to push multi-platform manifest. There's docker manifest built into docker. Or you can generate the manifest yourself and push it to a registry with API calls (see OCI's image-spec and distribution-spec if you want to get to the low level details). But if you have docker, buildx is the easy button.

Stung answered 6/3, 2022 at 22:7 Comment(2)
This helped me with the dockerfile. I also had to add "--provenance false" to buildx command. docker buildx build \ --provenance false \ --platform linux/arm64/v8,linux/amd64 \Jurdi
What issue required disabling the provenance? I've seen that done when someone didn't build a multiplatform image and wanted qemu to interpret the binaries for other platforms instead.Stung

© 2022 - 2024 — McMap. All rights reserved.