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
...
CodePudding user response:
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.