Multi-architecture images

In this section of the guide, we explain multi-architecture containers. Multi-architecture containers support execution as an Arm image or as an x86 image.

For example, to run a Python container with the x86_64 architecture on a Windows PC, you can execute the command that is shown in the following code block: 

> docker run python:2 python -c "import platform; print 'Python running on arch: %s' %platform.machine()"
Python running on arch: x86_64

The following code shows another example using Ubuntu Linux and printing the architecture using uname:

> docker run ubuntu uname -a
Linux 436761beed66 4.9.125-linuxkit #1 SMP Fri Sep 7 08:20:28 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

You might want to deploy your application on an Arm server on AWS, or on an Arm embedded board. Developing an application using Docker on an x86 machine may introduce incompatibilities when running the same software on an Arm machine. Docker Desktop allows you to create and test Arm images from your Windows desktop.

The best way to create images for Arm is to use the new buildx command which is included in Docker Desktop. The command usage is shown in this code:

> docker buildx

Usage:  docker buildx COMMAND

Build with BuildKit

Management Commands:
  imagetools  Commands to work on images in registry

Commands:
  bake        Build from a file
  build       Start a build
  create      Create a new builder instance
  inspect     Inspect current builder instance
  ls          List builder instances
  rm          Remove a builder instance
  stop        Stop builder instance
  use         Set the current builder instance

Run 'docker buildx COMMAND --help' for more information on a command.

Let's use a simple Dockerfile to see how the same Dockerfile supports multiple architectures with no modifications. Use a text editor to create a two-line Dockerfile with the contents that are shown in the following code:

FROM alpine
RUN apk --no-cache add curl

The --no-cache argument is a useful trick to not cache the index and keep containers small.

Now we look at how to build images for different platforms using buildx. Follow these steps:

  1. Create a new builder instance and use it, then build three images which are stored locally, this can be seen in the following code:
    > docker buildx create --name mybuilder
    > docker buildx use mybuilder
    > docker buildx build --platform linux/amd64 -t alpine-amd64 --load .
    > docker buildx build --platform linux/arm64 -t alpine-arm64 --load .
    > docker buildx build --platform linux/arm/v7 -t alpine-arm32 --load .
    
  2. Run each image, including the Arm images, on the local desktop, as you can see in the following code:
    > docker run alpine-amd64 uname -a
    Linux 4bc3bd4b8ff0 4.9.125-linuxkit #1 SMP Fri Sep 7 08:20:28 UTC 2018 x86_64 Linux
    
    > docker run alpine-arm64 uname -a
    Linux 404631ac3379 4.9.125-linuxkit #1 SMP Fri Sep 7 08:20:28 UTC 2018 aarch64 Linux
    
    > docker run alpine-arm32 uname -a
    Linux 5a869d794098 4.9.125-linuxkit #1 SMP Fri Sep 7 08:20:28 UTC 2018 armv7l Linux
    Now the images for each architecture are on this local machine. However, the goal is to save a single image that works on all platforms. This can be done with a single buildx command which pushes directly to a repository like Docker Hub. Using a repository is an easy way to migrate images to other machines, because the images can be pulled directly from the repository using any machine.
  3. Create a Docker Hub account if you do not have one. You can go to hub.docker.com and click Sign Up.
    Once pushed, buildx can be used to inspect the image and see that it supports three platforms, using the following code:
    > docker buildx build  --platform linux/amd64,linux/arm64,linux/arm/v7 -t jasonrandrews/alpine-test --push .
    
    > docker buildx imagetools inspect jasonrandrews/alpine-test
    Name:      docker.io/jasonrandrews/alpine-test:latest
    MediaType: application/vnd.docker.distribution.manifest.list.v2+json
    Digest:    sha256:6f36d248c3b139dc998cd7129a768cf068dd6371ecd6f027ce43c49b6bf80aa4
    
    Manifests:
      Name:      docker.io/jasonrandrews/alpine-test:latest@sha256:aaf426c683e2b1369fdba62e6c420980402fc3706c59ec59eacf0d1ab419e719
      MediaType: application/vnd.docker.distribution.manifest.v2+json
      Platform:  linux/amd64
    
      Name:      docker.io/jasonrandrews/alpine-test:latest@sha256:5966f7b12b7c7ba3146102cf3079e57cccaf1de7228651661276dd1606d84108
      MediaType: application/vnd.docker.distribution.manifest.v2+json
      Platform:  linux/arm64
    
      Name:      docker.io/jasonrandrews/alpine-test:latest@sha256:12e131c1e16083f5d8a8dcaf8b52b10e78612cea439e98b1cd32fe9a60a3cefa
      MediaType: application/vnd.docker.distribution.manifest.v2+json
      Platform:  linux/arm/v7
    
  4. Check your repositories by going to hub.docker.com and signing in with the same account you pushed to.
    If the new image is run with just the name, it will automatically run the native version. You can use the image name from the inspect command to specify the Arm image to run on Docker Desktop, as you can see in this code:
    > docker run jasonrandrews/alpine-test uname -m
    x86_64
    
    > docker run jasonrandrews/alpine-test:latest@sha256:5966f7b12b7c7ba3146102cf3079e57cccaf1de7228651661276dd1606d84108 uname -m
    aarch64
    
    > docker run jasonrandrews/alpine-test:latest@sha256:12e131c1e16083f5d8a8dcaf8b52b10e78612cea439e98b1cd32fe9a60a3cefa uname -m
    armv7l
    
    Docker abstracts the underlying operating system and the underlying hardware architecture. This helps to avoid architecture-specific bugs during development. We have shown how to create a single repository with support for three architectures using the buildx command.

To create multi-architecture support for more than one hundred official images follow these steps:

  1. Build the application using Docker. A Dockerfile is provided and you can see the command to build the image in the following code:
    > docker build -t php-example docker-php-hello-world
    Sending build context to Docker daemon   76.8kB
    Step 1/2 : FROM php:5.6-apache
    5.6-apache: Pulling from library/php
    5e6ec7f28fb7: Pull complete
    cf165947b5b7: Pull complete
    7bd37682846d: Pull complete
    99daf8e838e1: Pull complete
    ae320713efba: Pull complete
    ebcb99c48d8c: Pull complete
    9867e71b4ab6: Pull complete
    936eb418164a: Pull complete
    bc298e7adaf7: Pull complete
    ccd61b587bcd: Pull complete
    b2d4b347f67c: Pull complete
    56e9dde34152: Pull complete
    9ad99b17eb78: Pull complete
    Digest: sha256:0a40fd273961b99d8afe69a61a68c73c04bc0caa9de384d3b2dd9e7986eec86d
    Status: Downloaded newer image for php:5.6-apache
     ---> 24c791995c1e
    Step 2/2 : COPY public/ /var/www/html/
     ---> 00a704f44d8a
    Successfully built 00a704f44d8a
    Successfully tagged php-example:latest
    
  2. Run the image with Docker using the following code:
    > docker run -it --rm -p 80:80 php-example
    AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message
    AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message
    [Mon Apr 22 14:34:24.853585 2019] [mpm_prefork:notice] [pid 1] AH00163: Apache/2.4.25 (Debian) PHP/5.6.40 configured -- resuming normal operations
    [Mon Apr 22 14:34:24.853661 2019] [core:notice] [pid 1] AH00094: Command line: 'apache2 -D FOREGROUND'
    172.17.0.1 - - [22/Apr/2019:14:34:34 +0000] "GET / HTTP/1.1" 200 520 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36"
    
  3. Open a browser and connect to the port 80 on the local machine. You will see what is displayed in the following screenshot:
  4. Enter a name in the box and confirm that the hello world application is working. The application also prints the output of uname -m so that we can confirm the platform. You will see something like the following screenshot:

Now, let's prepare and run the same application for multiple architectures, including Arm:

  1. Follow the preceding steps 1-5.
  2. Use the new buildx flow to create all the images and push them to Docker Hub with a single command.
  3. Run the native image by specifying the image name.
  4. Test the Arm images by specifying the full name that is provided by the buildx inspect command, this should look like the following code:
> docker buildx build  --platform linux/amd64,linux/arm64,linux/arm/v7 -t jasonrandrews/php-example --push docker-php-hello-world

> docker run -it --rm -p 80:80 jasonrandrews/php-example

> docker buildx imagetools inspect jasonrandrews/php-example 
docker buildx imagetools inspect jasonrandrews/php-example
Name:      docker.io/jasonrandrews/php-example:latest
MediaType: application/vnd.docker.distribution.manifest.list.v2+json
Digest:    sha256:82242b9bdacb51c6201c3f4943f2139ba77c1b1233d0e18862c12c1649d1c1be

Manifests:
  Name:      docker.io/jasonrandrews/php-example:latest@sha256:6e4f5a17b2fd4803a43d4586e2f928c04174c42d16d2b07a7444344d77c7458f
  MediaType: application/vnd.docker.distribution.manifest.v2+json
  Platform:  linux/amd64

  Name:      docker.io/jasonrandrews/php-example:latest@sha256:4b47c9e79d744f039e893324fcadcabdcd7f33134740abf908115e7a9226de95
  MediaType: application/vnd.docker.distribution.manifest.v2+json
  Platform:  linux/arm64

  Name:      docker.io/jasonrandrews/php-example:latest@sha256:27cf14ff1a5f9079e9b58262c763d1b75c8760e4934d488006bd480163843344
  MediaType: application/vnd.docker.distribution.manifest.v2+json
  Platform:  linux/arm/v7
  
> docker run -it --rm -p 80:80 docker.io/jasonrandrews/php-example:latest@sha256:4b47c9e79d744f039e893324fcadcabdcd7f33134740abf908115e7a9226de95

Images have been created for three platforms. They are amd64, arm64, and amr32. If the arm64 image is run the output below now shows AArch64 from uname -m. This is an image created for the AArch64 architecture which is running on a Windows or MacOS machine but thinks it is an Arm machine:

We now have a single command to create the Docker image with multi-architecture support for the hello world PHP application for amd64, arm64, and arm32, and to store the image in Docker Hub.

With the application now in an accessible repository, the next step is to pull and run the images on other machines, like an Arm-based cloud server or an embedded device. We will look at this in Setting up an Arm server.

Previous Next