Back in 2018, I wrote about Multi-Architecture Docker Builds. My main aim then was to run the occasional container image on a Raspberry Pi. Apple’s transition to M1 based machines has increased demand for multi-architecture container images. In this post, I document an improved approach to building multi-architecture images.
I’ll use a Go application to show the build process. It prints the current runtime OS and CPU architecture to the terminal and then exits.
package main
import (
"fmt"
"runtime"
)
func main() {
fmt.Println("Hello, 世界!")
fmt.Println("GOOS:", runtime.GOOS)
fmt.Println("GOARCH", runtime.GOARCH)
}
In the past I needed to use a separate Dockerfile for each architecture. It is now possible to use a single Dockerfile to build multi-architecture images. This is a minimal example of a multi-stage build.
FROM golang:1.18-alpine AS build
LABEL maintainer="Bill Glover"
RUN apk --no-cache add ca-certificates
COPY . /app/
WORKDIR /app
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-extldflags "-static"' -o /app/app
FROM scratch
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=build /app/app /app
ENTRYPOINT ["/app"]
The command to build multi-architecture images is:
docker buildx build . --platform linux/arm64,linux/amd64 --tag bglovervmw/m1go:latest --push
Running this for the fist time on a default Docker installation will produce an error.
error: multiple platforms feature is currently not supported for docker driver. Please switch to a different driver (eg. "docker buildx create --use")
To fix this, create a new driver. You can name this anything, but I’ve called mine multiarch
to remind me why I created it.
docker buildx create --name multiarch --use
Now go ahead and build your multi-architecture image.
docker buildx build . --platform linux/arm64,linux/amd64 --tag bglovervmw/m1go:latest --push
This will build a single manifest for both a linux/arm64
and a linux/amd64
image. Note: This build command will push the image to the final image repository. It is not currently possible to sepate the build
from the push
.
Running this container on my Apple Silicon machine:
docker run --rm bglovervmw/m1go
Hello, 世界!
GOOS: linux
GOARCH arm64
Running the same image on my Intel machine:
docker run --rm bglovervmw/m1go
Hello, 世界!
GOOS: linux
GOARCH amd64
I’ll look at building multi-architecture images as part of CI/CD pipelines in a future post.