Skip to content

Build ARM64 on AMD64

Christoph Diehl edited this page Oct 28, 2019 · 10 revisions

Install Docker on Ubuntu

sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
sudo apt-get update
sudo apt-get install -y docker-ce
sudo usermod -aG docker $USER

Add experimental features:

echo '{\n    "experimental": true\n}' | sudo tee /etc/docker/daemon.json
mkdir -p ~/.docker && echo '{\n    "experimental": enabled\n}' | tee ~/.docker/config.json

Restart the Docker daemon

sudo systemctl daemon-reload
sudo systemctl restart docker

Login to your Docker Hub account

sudo docker login

Build for ARM64 on AMD64

Objective

-----------------------------
|    Linux    |   Windows   |
-----------------------------
| ARM | AMD64 | ARM | AMD64 |
-----------------------------
|  Dockerfile |  Dockerfile |
-----------------------------
|            Host           |
-----------------------------

The following allows us to run and build ARM64 on AMD64 however, it is restricted to work only with Docker images from the multiarch repository.

docker run --rm --privileged multiarch/qemu-user-static:register --reset
docker run --rm -it multiarch/alpine:arm64-edge /bin/sh

Create the Dockerfiles for AMD64 and ARM64

This is similar of running Linux containers on OSX with default xhyve virtualization.

Requirements

sudo apt-get install -y qemu-user-static qemu-user binfmt-support
ls -ltr /proc/sys/fs/binfmt_misc
# I.e copy /usr/bin/qemu-aarch64-static to Docker project folder.

Or

for target_arch in aarch64; do # Extend
  wget -N https://github.com/multiarch/qemu-user-static/releases/download/v3.1.0-3/x86_64_qemu-${target_arch}-static.tar.gz
  tar -xvf x86_64_qemu-${target_arch}-static.tar.gz
done
rm *.tar.gz

It is now already possible to run ARM64 images by mounting the qemu binary into the container.

docker run --rm -it  -v /usr/bin/qemu-aarch64-static:/usr/bin/qemu-aarch64-static …

But there is no way for the Docker CLI to pull a specific arch, hence we can only specify to pull "ubuntu:latest" which would result in pulling the AMD64 image instead of an ARM64 image. In any way for building an ARM64 image we would need to move the binary to the Docker image during the build process and the "build" flag does not support the volume mount parameter.

Retrieve the SHA256 digest of each architecture

docker manifest inspect ubuntu:latest

Use the digest to pull the corresponding arch image.

# Dockerfile.arm64
FROM ubuntu@sha256:92d9e5deed4c994a64f04cb023370e79ce273fabde51f8cfc7cf66b594147eb2
COPY qemu-aarch64-static /usr/bin/
# Dockerfile.amd64
FROM ubuntu@sha256:f2557f94cac1cc4509d0483cb6e302da841ecd6f82eb2e91dc7ba6cfd0c580ab

Build images for all architectures

#!/usr/bin/env bash

USER=posidron
NAME=core

for arch in amd64 arm64; do
  docker build --no-cache -f Dockerfile.${arch} -t $USER/$NAME:${arch}-latest .
  docker push $USER/$NAME:${arch}-latest
done

Inspect which architecture the image uses

$ docker image inspect posidron/core:arm64-latest --format '{{.Os}} {{.Architecture}}'
linux arm64
$ docker image inspect posidron/core:amd64-latest --format '{{.Os}} {{.Architecture}}'
linux amd64

Create a Manifest

#!/usr/bin/env bash

USER=posidron
NAME=core

docker manifest create \
  $USER/$NAME:latest \
  $USER/$NAME:amd64-latest \
  $USER/$NAME:arm64-latest
docker manifest annotate $USER/$NAME:latest $USER/$NAME:arm64-latest --os linux --arch arm64 --variant v8
docker manifest push $USER/$NAME:latest

Verify the Manifest

docker manifest inspect posidron/core
{
   "schemaVersion": 2,
   "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
   "manifests": [
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 1150,
         "digest": "sha256:f2557f94cac1cc4509d0483cb6e302da841ecd6f82eb2e91dc7ba6cfd0c580ab",
         "platform": {
            "architecture": "amd64",
            "os": "linux"
         }
      },
      {
         "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
         "size": 1361,
         "digest": "sha256:7f7c38c7cc5f4d4ba657ec1d0041ff6a5a633f1d9864ddc3e5ba2b0b49a49ace",
         "platform": {
            "architecture": "arm64",
            "os": "linux",
            "variant": "armv8"
         }
      }
   ]
}
docker run mplatform/mquery posidron/core
Image: posidron/core
 * Manifest List: Yes
 * Supported platforms:
   - linux/amd64
   - linux/arm64

Use

This would resemble FuzzOS multi-arch.

# Dockerfile
ARG IMAGE=posidron/core
FROM $IMAGE
# Build for both Architectures
docker build --no-cache --build-arg IMAGE=posidron/core:arm64-latest -t fuzzos .
docker build --no-cache --build-arg IMAGE=posidron/core:amd64-latest -t fuzzos .

Proceed with creating a manifest for the new child image similar to posidron/core but for posidron/fuzzos in this case.

References

https://github.com/linuxkit/linuxkit/tree/master/pkg/binfmt/etc/binfmt.d