How to set Architecture for docker build to arm64?
Asked Answered
H

7

36

I have a Dockerfile that I run on amd64 but want to run on arm64. Since go build tool takes GOARCH=arm64 as argument I don't need any other cross compilation tool to make the binary.

# Run the build
FROM mojlighetsministeriet/go-polymer-faster-build
ENV WORKDIR /go/src/github.com/mojlighetsministeriet/email
COPY . $WORKDIR
WORKDIR $WORKDIR
RUN go get -t -v ./...
RUN CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build

# Create the final docker image
FROM scratch
COPY --from=0 /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY --from=0 /go/src/github.com/mojlighetsministeriet/email/email /
ENTRYPOINT ["/email"]

The problem is that the resulting image gets marked with the wrong Architecture amd64 instead of arm64. How can I pass an argument to docker build so that it sets Architecture to arm64?

$ docker image inspect mojlighetsministeriet/email-arm64                            
[
    {
        "Id": "sha256:33bcd7da8631c7a0829d61cc53479a26ab7f31fab1cb039105de415ddc6178f3",
        "RepoTags": [
            "mojlighetsministeriet/email-arm64:latest"
        ],
        "RepoDigests": [
            "mojlighetsministeriet/email-arm64@sha256:ab3f05d5597c3a304953b5c14f795179aa75bdfd458af3dc3cfb8b8d8eb87bc3"
        ],
        "Parent": "sha256:e5888262d93ea0946b5fd8146cf1c19ec3a7bac4d36eb51848ef0aefa75cf8e7",
        "Comment": "",
        "Created": "2017-12-05T18:36:36.273648787Z",
        "Container": "7a226edb3b52aaeeefec9e0fb4dd1da50d84992fb6cc374aeda9d82eec1bb2c8",
        "ContainerConfig": {
            "Hostname": "7a226edb3b52",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],
            "Cmd": [
                "/bin/sh",
                "-c",
                "#(nop) ",
                "ENTRYPOINT [\"/email\"]"
            ],
            "Image": "sha256:e5888262d93ea0946b5fd8146cf1c19ec3a7bac4d36eb51848ef0aefa75cf8e7",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": [
                "/email"
            ],
            "OnBuild": null,
            "Labels": {}
        },
        "DockerVersion": "17.10.0-ce",
        "Author": "",
        "Config": {
            "Hostname": "",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],
            "Cmd": null,
            "Image": "sha256:e5888262d93ea0946b5fd8146cf1c19ec3a7bac4d36eb51848ef0aefa75cf8e7",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": [
                "/email"
            ],
            "OnBuild": null,
            "Labels": null
        },
        "Architecture": "amd64",
        "Os": "linux",
        "Size": 7861466,
        "VirtualSize": 7861466,
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/03cb0162bf922636e4e0ec90123b81565a447c6cd511741103551d2f0e7f09f9/diff",
                "MergedDir": "/var/lib/docker/overlay2/091f74815a0caf457df7e57ade43b41c4dd8551388beca44815a7038501742ee/merged",
                "UpperDir": "/var/lib/docker/overlay2/091f74815a0caf457df7e57ade43b41c4dd8551388beca44815a7038501742ee/diff",
                "WorkDir": "/var/lib/docker/overlay2/091f74815a0caf457df7e57ade43b41c4dd8551388beca44815a7038501742ee/work"
            },
            "Name": "overlay2"
        },
        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:66f615d03920919b0fa8bc9fed45515bb95636be1837fdd10a82b2c183e2ad5b",
                "sha256:bd6a01b885eb6e3eec38a3fe3a2899646509633730b210cf6987456fd40b8a1c"
            ]
        },
        "Metadata": {
            "LastTagTime": "2017-12-14T10:24:10.796813522+01:00"
        }
    }
]
Hypoderm answered 14/12, 2017 at 9:27 Comment(3)
This can help you: github.com/paritytech/parity-snappy/wiki/…Clodhopping
I'm not sure how this could help me, I did see a setup for creating a rust arm64/aarch64 binary, that part I have no problems doing with above Dockerfile. The binary is correct, it starts on my arm64 machine, but the image gets tagged with amd64 instead of arm64 which won't let my schedule the image in my docker swarm environment. I need a way of changing the Architecture property on the image metadata but I can't seem to find a way to do that.Hypoderm
Have you found an answer? I have a similar problem, I build code in dotnet, which is platform agnostic by design, so I don't need any cross build setup to get working binaries for arm, But docker limits me to the architecture of the build machine. So now I'm forced to build arm images on arm machine, which is way slowMoureaux
S
53

For building single docker images: Set your environment variable using the command line or modifying your .bashrc or .zshenv file. (introduced in v19.03.0 in 03/2019)

export DOCKER_DEFAULT_PLATFORM=linux/arm64

Alternatively, in the Dockerfile, include the following flag in the FROM command (for a multi-stage Dockerfile build, the flag is only needed for the first stage):

FROM --platform=linux/arm64 python:3.7-alpine

For building images as part of a docker-compose build, include the platform: linux/arm64 for each service. For example:

services:  
  frontend:  
    platform: linux/arm64
    build: frontend  
    ports:
      - 80:80  
    depends_on:
      - backend  
  backend:  
    platform: linux/arm64
    build: backend  

This also works the other way around, for instance if you have an Apple M1 Chip and want to deploy images to a Linux or Windows based AMD64 environment. Simply swap 'linux/arm64' for 'linux/amd64'

Samella answered 8/7, 2022 at 17:35 Comment(0)
L
32

https://docs.docker.com/desktop/multi-arch/

# Shows builders installed
docker buildx ls

# Use builder that supports platform
docker buildx use default

docker buildx build --platform linux/arm64 -t <image_name>:<image_tag> --push .
Lurlinelusa answered 8/7, 2021 at 21:14 Comment(0)
O
4

I have deployed docker image on azure container apps which only allows amd64 . Following solution worked for me.

docker build -t user/image . --platform="linux/amd64"

In the same way you can change platform for you.

Organogenesis answered 23/3 at 19:6 Comment(0)
M
3

I was able to solve the problem, not exactly as I wanted, but close enough.

  1. Have an amd64 Linux machine with docker

  2. Setup qemu user static for arm support https://hub.docker.com/r/multiarch/qemu-user-static/

  3. In your docker file use base image with support for arm. E.g. ubuntu

  4. Build your image with command similar to the following:

    docker build --platform arm --pull -t your_tag .

This command will force docker to pull arm version of the base image and will also set arm architecture to your result image. But be careful, if you use tags with multiple architectures, the command above will set the tag to arm version. So to run the native amd64 version you will need to pull the tag again without --platform arg.

Moureaux answered 21/10, 2018 at 19:40 Comment(0)
K
0

I build and run arm64 images on amd64 host with the help of qemu-user. But docker inspect these arm64 images always show "Architecture": "amd64". I hope it can show "Architecture": "arm64"

I tried DOCKER_DEFAULT_PLATFORM and --platform=linux/arm64, both not work. I'm using docker 19.03.13.

Finally, i found the solution —— save image first, change its metadata, then load it back:

set -e

IMAGE_ID=$(docker image inspect --format '{{.ID}}' $1)
IMAGE_ID=${IMAGE_ID#sha256:}

if [ ${#IMAGE_ID} -ne 64 ]; then
  echo "Invalid image sha256 id"
  exit 1
fi

rm -rf ${IMAGE_ID}
mkdir -p ${IMAGE_ID}
echo "IMAGE ${IMAGE_ID}"

echo "> save..."
docker save ${IMAGE_ID} | tar -xC ${IMAGE_ID}

echo "> change arch..."
sed -i 's/"architecture":"amd64"/"architecture":"arm64"/' ${IMAGE_ID}/${IMAGE_ID}.json

echo "> reload..."
tar -cC ${IMAGE_ID} . | docker load

echo "> clean..."
rm -rf ${IMAGE_ID}

echo "Note: you need to tag it manually"

Save it to changeArch.sh, then run sh changeArch.sh <sha_id> or sh changeArch.sh <image:tag>.

Kevakevan answered 21/4, 2023 at 9:51 Comment(0)
R
0

None of the previous answers worked for me. I was building a static Go binary per architecture and copying it into a completely empty container built FROM scratch. I needed to take the following approach, using the docker manifest annotate command to explicitly set the architecture of the built container:

docker build -f myproject.Dockerfile --pull -t registry.gitlab.com/myuser/myproject/myproject:manifest-arm64 --build-arg ARCH=arm64 .
docker build -f myproject.Dockerfile --pull -t registry.gitlab.com/myuser/myproject/myproject:manifest-amd64 --build-arg ARCH=amd64 .
docker push registry.gitlab.com/myuser/myproject/myproject:manifest-arm64
docker push registry.gitlab.com/myuser/myproject/myproject:manifest-amd64
docker manifest create registry.gitlab.com/myuser/myproject/myproject:manifest-latest \
        --amend registry.gitlab.com/myuser/myproject/myproject:manifest-arm64 \
        --amend registry.gitlab.com/myuser/myproject/myproject:manifest-amd64
docker manifest annotate --arch arm64 registry.gitlab.com/myuser/myproject/myproject:manifest-latest registry.gitlab.com/myuser/myproject/myproject:manifest-arm64
docker manifest push registry.gitlab.com/myuser/myproject/myproject:manifest-latest
Reichstag answered 25/4, 2023 at 18:55 Comment(0)
B
0

Buildkit has the tooling included to make this process very easy for Go images. Your Dockerfile can be modified to look like:

# The --platform uses the built in variable to pick the image matching your build host
# You only want to specify this on non-final stages of a multi-stage build, otherwise you would end up with a filesystem for one platform being marked with the wrong platform
FROM --platform=$BUILDPLATFORM mojlighetsministeriet/go-polymer-faster-build as build
ENV WORKDIR /go/src/github.com/mojlighetsministeriet/email
COPY . $WORKDIR
WORKDIR $WORKDIR
RUN go get -t -v ./...

# to leverage buildkit with Go's cross compiler use the TARGETOS and TARGETARCH args
# they are built in from buildkit and match GOOS and GOARCH
ARG TARGETOS
ARG TARGETARCH
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build

# without specifying a platform here, the scratch image will have the target platform architecture
FROM scratch
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY --from=build /go/src/github.com/mojlighetsministeriet/email/email /
ENTRYPOINT ["/email"]

Then your build command would look like:

docker buildx build --platform linux/arm64 -t mojlighetsministeriet/email-arm64 .

Or create a multi-platform image with:

docker buildx build --platform linux/amd64,linux/arm64 -t mojlighetsministeriet/email .

The --platform flag in those commands set the target for both the embedded build args and more importantly the image config.

Bearing answered 27/5 at 13:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.