Skip to content

Instantly share code, notes, and snippets.

@sh0rez
Last active July 15, 2019 20:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sh0rez/75ca03f6e129400481b955b4a9fb3675 to your computer and use it in GitHub Desktop.
Save sh0rez/75ca03f6e129400481b955b4a9fb3675 to your computer and use it in GitHub Desktop.
Cross-platform golang docker in 2019

Overview

This gist demonstrates how to build cross-architecture docker images in 2019. While go has been awesome at cross compiling since ever, docker support really lacked behind.

This changed with the advent of buildkit: Just by specifying --platform, we can build one and the same Dockerfile for every supported architecture, as long as the buildkit executor is capable of this arch. However, qemu is a great help here. By running sudo apt install binfmt-support and sudo docker run --privileged linuxkit/binfmt:v0.6, your amd64 PC is now capable of executing nearly every available linux binary like magic.

We can now use this to build this image using img or any other buildkit wrapper:

$ img build --platform=linux/arm/v7 --platform=linux/arm64 --platform=linux/amd64 -t shorez/multi .
$ img push shorez/multi

This leaves us with a perfect fat manifest v2 image, ready to be pulled using docker pull shorez/multi. The correct version for the arch will be selected automatically.

Notes on the Dockerfile

This Dockerfile is fairly different from traditional ones, because the new --platform support from buildkit or docker 19.03+ is used. An short explanation:

Imagine this build is called using --platform=linux/arm64. This means, buildkit is now going to pull golang:1.12-alpine, but soon discovers it needs the arm version, so it pulls this one. It then saves GOARCH and GOARM to a file for later use. This is required, because the format of these two diverts from what docker uses to distinguish arch.

The next stage is always going to run as amd64. Here happens the actual go build. It is amd64, because it is not possible to compile go on qemu. No problem, go is capable of cross compiling itself.

The final stage is going to run as arm64 again. In this case this does not matter, as there is no RUN. But there could certainly be one.

FROM golang:1.12-alpine as goenv
RUN go env GOARCH > /goarch && \
go env GOARM > /goarm
FROM --platform=linux/amd64 golang:1.12-alpine as build
COPY --from=goenv /goarch /goarm /
COPY . /app
WORKDIR /app
RUN GOARCH=$(cat /goarch) GOARM=$(cat /goarm) go build .
FROM alpine
COPY --from=build /app/app /app
ENTRYPOINT ["/app"]
package main
import(
"fmt"
"runtime"
)
func main() {
fmt.Println(runtime.GOARCH)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment