Search code examples
linuxdockergoarm

Docker scratch image not showing up as arm64


I'm building a Docker image for multiple architectures (amd64 and arm64 - both on Linux). I'm using a multistage build - this means using go-alpine image as a builder (to compile my Go source code into an executable), and then copying the executable to a smaller secondary image.

Initially, I was copying the compiled program into an Alpine image, but I decided to switch to a scratch image (because of the security benefit) - this involved changing my build command so that the resultant executable is statically linked. My Dockerfile is now:

FROM golang:1.16 AS builder
WORKDIR /go/src/
RUN go get -d -v ./...
COPY . .
RUN env ${opts} go build -a -installsuffix cgo -o app .

FROM scratch
WORKDIR /root/
COPY --from=builder /go/src/app .
CMD ["./app"]

When I build for amd64, I run docker build --build-arg opts="CGO_ENABLED=0 GOOS=linux GOARCH=amd64" -t myuser/example:amd64 . - if I inspect this image, it shows "Architecture": "amd64" as expected.

When I build for arm64, I run docker build --build-arg opts="CGO_ENABLED=0 GOOS=linux GOARCH=arm64" -t myuser/example:arm64 . - if I inspect this image, it shows "Architecture": "amd64" - which isn't what I want.

If I compile the Go code locally by running CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build, and then I run file on the compiler output, it shows that the compiled output is arm64. I assume that the issue is therefore a result of Docker pulling an amd64 base image (since my PC is x86), but I can't figure out how to fix the issue.


Solution

  • The snippets you shared are cross-compiling your Go binary, but like you pointed out, are still using an amd64 base image. To do this, you will need to build a docker container for multiple platforms:

    https://www.docker.com/blog/multi-arch-build-and-images-the-simple-way/

    The example is something like:

    docker buildx build \
    --platform linux/arm64/v8,linux/amd64 \ 
    --tag your-username/multiarch-example:buildx-latest .
    

    Using the docker platform route, you shouldn't need to use go-multiarch building since the go tool should infer the platform its building for.

    As an alternative, you could cross-compile your binary on your host, and then have a docker image for each platform that is based off of the scratch image for that architecture and just copies your executable in.