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:
- 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 .
- 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 singlebuildx
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. - 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
- 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 thebuildx
command.
To create multi-architecture support for more than one hundred official images follow these steps:
- 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
- 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"
- Open a browser and connect to the port 80 on the local machine. You will see what is displayed in the following screenshot:
- 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:
- Follow the preceding steps 1-5.
- Use the new
buildx
flow to create all the images and push them to Docker Hub with a single command. - Run the native image by specifying the image name.
- 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.