Docker pulling in wrong architecture

I’ve managed to build a multi-arch image, but when I do Docker pull on my M1 Mac, it still pulls in the amd64 version instead of the arm64 one.

I didn’t build with buildx because I have architecture-specific binaries to include in my image. So I have two Docker files, create two images which I push. Then I create a manifest that I annotate so that it includes both images:

{
   "schemaVersion": 2,
   "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
   "manifests": [
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 1579,
         "digest": "sha256:2c...",
         "platform": {
            "architecture": "amd64",
            "os": "linux"
         }
      },
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 1579,
         "digest": "sha256:7c...",
         "platform": {
            "architecture": "arm64",
            "os": "linux"
         }
      }
   ]
}

But when I do docker pull of this image/manifest on my M1, it still pulls in the amd64 version. I can check this by running docker inspect on the pulled image.

I thought Docker would automatically download the image that fits my architecture? Or is something still wrong with the manifest?

Ok, on further investigation, it seems the contents of the image are correct (it includes the statements to download the correct native binary). But the docker inspect command still says it’s made for amd64. So I’m thinking it’s something with the way the image was created or the way it was pushed to the registry.

It seems I have to build the images with buildx instead of just plain docker build.

How did you push the images? Using different tags for both images and a third tag for the multi-arch image?
I guess you folloed these instructions:

As you suspected, docker pull should pull the image with the matching architecture unless you override with the --platform option. If it didn’t work for you, then you probably did not have that architecture which can be because of using the same tag for both images. Althought I am not sure how you could have create the manifest that way, so there is a good chance that I am wrong.

We basically did what you explain. But our thinking now it that we need to build with buildx and have some emulator installed. We’re still not there yet, but once we find out how to do this, I’ll post the solution here.

Docker Desktop has a built-in emulator. You don’t need buildx for that, but to be honest, when I wanted to create my first multi-arch image, I also started with docker manifest and ended up using buildx because it was easier for me and worked perfectly.

Except, I don’t have a single Dockerfile. Because we have architecture-specific binaries that must be included in the container, we have separate Dockerfiles. So I can’t just use buildx and tell it to build for two different architectures based on a single file.

Architecture-specific binaries can be handled in one Dockerfile if that helps:

The platform variables are automatically set so you can use those to copy or download different binaries into each image.

Thanks for the link! We’ve currently solved it by running the following commands in our Jenkins build:

# install emulator
docker run --privileged --rm tonistiigi/binfmt --install linux/arm64,linux/amd64
# create builder
docker buildx create --name multiarch --driver docker-container --platform linux/amd64,linux/arm64 --use
docker buildx inspect --bootstrap

# create images
docker buildx build -f Dockerfile.amd64 -t our-repo.com/image-amd64 --platform=linux/amd64 . --push
docker buildx build -f Dockerfile.arm64 -t our-repo.com/image-arm64 --platform=linux/arm64 . --push

# pull locally, because the --push flag previously ensured the image doesn't exist locally
docker pull our-repo.com/image-amd64
docker pull our-repo.com/image-arm64

# create, annotate and push manifest
docker manifest create our-repo.com/image:latest --amend our-repo.com/image-amd64:latest --amend our-repo.com/image-arm64:latest
docker manifest annotate rour-repo.com/image:latest our-repo.com/image-amd64:latest --os linux --arch amd64
docker manifest annotate our-repo.com/image:latest our-repo.com/image-arm64:latest --os linux --arch arm64
docker manifest push our-repo.com/image:latest

This now works for us, it seems.

For those coming here for help, I must say we didn’t really get it working. The built images never ran correctly. So what we did was base our image off a multiplatform base image (Java) that already contained the correct binary. Sorry.

Also, thanks @rimelek , the post you mentioned did help. We managed to use multi-stage builds and/or the TARGETOS/TARGETARCH to differentiate.

2 Likes

You’re one of those rare people on the internet who actually posted their experience as you went along. Ive read so many posts where it ended with: “figured it out, bye” that this was a breath of fresh air. Thank you!

1 Like