Software development is a wide-ranging field that impacts almost every aspect of life:
Humanity is becoming increasingly reliant on software and because almost every industry relies on software to function, there is a constant effort to improve the software development process by increasing efficiency and lowering cost. While there are many ways to work toward these goals, Arm and Docker have been working together to deliver a seamless development experience for applications targeting the Arm architecture.
Docker containers continue to gain in popularity because they are flexible, lightweight, and portable. If container concepts are new to you the Docker Get Started is a great place to start.
Docker helps both developers and IT professionals by:
Arm’s solutions span from cloud to edge computing by:
Scaling up software environments quickly to take advantage of market opportunities is a difficult and time-consuming task. Docker has made great strides in developer productivity by abstracting the operating system and hardware details so developers can focus on applications. The recently announced partnership between Docker and Arm extends these benefits to even more software developer use cases.
Arm and Docker are striving to provide a seamless developer experience by enabling multi-architecture builds on Docker Desktop, simplifying application deployment for Arm platforms and enabling Docker Engine for Arm servers and devices.
Multi-platform portability has long been an important goal of enterprise computing. To reach this goal, Docker and Arm have teamed up to build multi-architecture container images with transparent support for the Arm architecture. These multi-architecture containers abstract away underlying hardware details, making it easy to develop consistently reproducible environments for the power-efficient Arm architecture. This functionality is perfect for those doing software development across a variety of platforms such as Windows or Mac laptops, servers in the cloud, and embedded or IoT devices. It also enables developers to try the Arm architecture with almost no change to the development process.
Multi-architecture containers provide native execution on Arm servers in the cloud and on embedded devices. The same containers can be run and validated on the desktop using instruction translation. A common use case is to develop an application with Docker on a desktop machine and move the application to a server, cloud machine, or embedded device.
Let’s look at how multi-architecture containers are used in practice by going through each step of the development process. The flow starts with installing Docker on a local desktop machine (both Windows and Mac are supported), creating an application in a Docker image, pushing the Docker image to a registry such as Docker Hub, installing Docker on a cloud machine such as an AWS A1 instance, and running the application in the cloud. As a final step, the exact same image is run on a Raspberry Pi 3.
Over time, software development has evolved from writing software to run on the same machine on which it was created (think PC applications) to creating software intended to run on mobile, embedded, IoT, and cloud. Even though software deployment happens in the cloud or on a mobile device, IoT device, or other embedded system, developers often prefer to run locally to get started and to fix simple issues. Providing a development environment to start on Windows or Mac and smoothly migrate to the final target is important for developers. One of the benefits of containers is they can be easily migrated to different types of machines. Let's look at how Docker does this and enables applications to run on the Arm architecture.
This article is in collaboration with the Docker Desktop tech preview release announced at DockerCon 2019 and some features are likely to change in the future as they move into the general product, but the tech preview is a great way to see the latest and greatest features of Docker Desktop.
The first step is to install Docker Desktop for Windows or Mac. After downloading the package, install it either by dragging the whale icon into your Applications folder on Mac or clicking though the installer on Windows. Docker Desktop for Windows requires Microsoft Hyper-V to run. The Docker Desktop for Windows installer enables Hyper-V for you, and prompts to restart your machine if needed.
The new features described here are in the Edge release so check the settings to make sure the Edge version is running.
Commands starting with > are for PowerShell on Windows, but are identical on Mac. Commands starting with $ are for bash on Linux. The > or the $ should not be entered.
After installing Docker Desktop, open Windows PowerShell (or Windows Command Prompt) and run hello-world to confirm Docker is setup correctly.
> docker run hello-world Hello from Docker! This message shows that your installation appears to be working correctly. To generate this message, Docker took the following steps: 1. The Docker client contacted the Docker daemon. 2. The Docker daemon pulled the "hello-world" image from the Docker Hub. (amd64) 3. The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading. 4. The Docker daemon streamed that output to the Docker client, which sent it to your terminal. To try something more ambitious, you can run an Ubuntu container with: $ docker run -it ubuntu bash Share images, automate workflows, and more with a free Docker ID: https://hub.docker.com/ For more examples and ideas, visit: https://docs.docker.com/get-started/
Let's explore multi-architecture containers. For example, to run a python container you can run the command:
> docker run python:2 python -c "import platform; print 'Python running on arch: %s' %platform.machine()" Python running on arch: x86_64
Here is 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
All straight-forward so far, the x86_64 architecture is used on a Windows PC.
Let's say an application should also be deployed on an Arm server on AWS or 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, causing delays and cutting into the desired savings, but with Docker Desktop, the Arm images can be created and tested right from the Windows desktop.
The best way to create images for Arm is using the new buildx command which is included in the tech preview release of Docker Desktop. The command usage is shown here:
> 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:
FROM alpine RUN apk --no-cache add curl
The --no-cache argument is a trick to not cache the index and keep containers small.
Here’s how to build images for different platforms using buildx. First, create a new builder instance and use it, then build three images which are stored locally:
> 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 .
Each image can be run, including the Arm images on the local desktop:
> 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
Although it's great to have images on the local machine now for each architecture, 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 such as Docker Hub. Using a repository is an easy way to migrate images to other machines since they can be pulled directly from the repository from any machine.
First, if you don’t have a Docker Hub account already, go to hub.docker.com and click “Sign Up” to create one.
One pushed, buildx can be used to inspect the image and see that it supports three platforms.
> 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
You can 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. The image name from the inspect command can be used to specify the Arm image to run on Docker Desktop:
> 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 also abstracts the underlying hardware architecture. This helps avoid architecture-specific bugs during development. We have shown how to create a single repository with support for three architectures using the buildx command.
This multi-architecture support exists for more than one hundred official images on Docker Hub, and the number of images supporting multiple architectures continues to grow. Explore Docker Hub to find more official images with Arm support.
Now we will set up a simple PHP-based web server and see how to target an Arm 64-bit cloud instance and an Arm 32-bit embedded board. To find an example I searched for a "hello world php example for docker" and easily found one on github and forked it to my account.
Use Git to obtain the example. There are plenty of examples on how to download, install, and get started with Git.
> git clone https://github.com/jasonrandrews/docker-php-hello-world.git Cloning into 'docker-php-hello-world'... remote: Enumerating objects: 4, done. remote: Counting objects: 100% (4/4), done. remote: Compressing objects: 100% (3/3), done. remote: Total 16 (delta 0), reused 2 (delta 0), pack-reused 12 Unpacking objects: 100% (16/16), done.
To start with build the application using Docker. A Dockerfile is provided and the command to build the image is:
> 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:
> 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"
To see the application in action, open a browser and connect to the port 80 on the local machine.
Enter a name in the box and confirm the hello world application is working. It also prints the output of uname -m so we can confirm the platform.
Now, let's prepare and run the same application for multiple architectures, including Arm. Using the new buildx flow we can create all of the images and push them to Docker Hub with a single command. We can run the native image by just specifying the image name and we can test the Arm images by specifying the full name provided by the buildx inspect command.
> 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
If the arm64 image is run the output now shows aarch64 from uname -m.
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 store it in Docker Hub.
With the application now in an accessible repository, the next step is to pull and run the images on other machines such as an Arm-based cloud server or an embedded device.
One way to execute the example application on an Arm server is to use Amazon Web Services. The A1 instance type features 64-bit Arm Neoverse cores powered by custom silicon designed by AWS.
An AWS account is needed to launch an A1 instance in Elastic Compute Cloud (EC2 for short). Create a new account if needed, navigate to the EC2 dashboard, and select Launch Instance. The first step is to select the Amazon Machine Image (AMI) for the cloud instance.
This includes the operating system and hardware architecture. Select the Ubuntu Server 18.04 OS, and click the 64-bit (Arm) button to the right of the image name as seen below:
Hit Select and chose any A1 instance type. For this example, the a1.medium option with 1 CPU and 2Gb RAM is more than sufficient and no additional configuration parameters are needed. When creating the instance make sure the security group is setup to allow port 22 for ssh access and port 80 for the PHP application. With the relevant parameters configured, click Review and Launch and finally Launch to create your instance. You will be prompted to select a private key pair to connect to this instance; select Create a new key pair, type in a key pair name such as ‘A1-PHP-example’ and select Download Key Pair. Keep this A1-PHP-example.pem file safe and accessible as you need this file to connect to this instance remotely. More info about how to use the key pair from different operating systems is provided in the AWS documentation.
Note that this option is not eligible for free-tier AWS usage, but the charges will be minimal just to try it.
Now that the instance is starting, navigate back to the EC2 dashboard > Instances tab to view the created machine instance. Highlighting the instance will display relevant data on the bottom of the screen, such as the instance’s description, status, tags, and more. Under the Description tab there is a Public DNS (IPv4) section representing this instance’s IP address (note you can use the IP address under the IPv4 Public IP as well).
Copy the IP address and use it along with the key pair file to ssh into the machine. There are different ways to connect and the AWS documentation is very good. The username is ubuntu. A simple command line connection would be:
> ssh -i A1-PHP-example.pem ubuntu@A1InstanceIP
Running this command will login to the created instance with the ubuntu username.
With an instance created and connected, the next steps are to install Docker and run the containerized application in the cloud.
Installing Docker on supported Linux systems could hardly be easier. One terminal command is all it takes:
$ sudo apt-get update $ sudo apt-get upgrade $ curl -fsSL get.docker.com -o get-docker.sh && sh get-docker.sh
Add the ubuntu user to the docker group to avoid needing sudo to run the docker command:
$ sudo usermod -aG docker $USER
Make sure to log out and back in again. Now test the install with a quick hello-world run.
$ docker run hello-world Unable to find image 'hello-world:latest' locally latest: Pulling from library/hello-world 3b4173355427: Pull complete Digest: sha256:92695bc579f31df7a63da6922075d0666e565ceccad16b59c3374d2cf4e8e50e Status: Downloaded newer image for hello-world:latest Hello from Docker! This message shows that your installation appears to be working correctly. To generate this message, Docker took the following steps: 1. The Docker client contacted the Docker daemon. 2. The Docker daemon pulled the "hello-world" image from the Docker Hub. (arm64v8) 3. The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading. 4. The Docker daemon streamed that output to the Docker client, which sent it to your terminal. To try something more ambitious, you can run an Ubuntu container with: $ docker run -it ubuntu bash Share images, automate workflows, and more with a free Docker ID: https://hub.docker.com/ For more examples and ideas, visit: https://docs.docker.com/get-started/
With Docker installed, it's time to run the containerized application on the AWS Arm instance. There are several ways to get a Docker image onto this cloud instance, from rebuilding it locally from a Dockerfile to pulling a pre-compiled image from Docker Hub. In previous step, we pushed the PHP example images to Docker Hub so we can run them immediately.
Using the ‘docker run’ command will automatically fetch the image from Docker Hub if not found locally. In the AWS instance, run the following command in the terminal
$ docker run --rm -it -p 80:80 jasonrandrews/php-example
Running this command will pull the application from Docker Hub and run it on this AWS A1 instance.
To view the PHP example application open a web browser and paste the IP address of the A1 instance into the browser and the same PHP example application will appear with the uname -m showing aarch64.
Because the Docker image was developed using multi-architecture containers, the behavior during development and deployment will be the same even though the application was developed on a different hardware architecture than Arm.
Now let's see how to use the PHP example application on an embedded device.
While the above example focused on a cloud-based application, the same flow applies to at-scale embedded use-cases as well. The benefits of abstracting hardware differences during development and using the Arm architecture also apply in the embedded space. Docker makes software deployment for IoT and embedded systems easier because it takes away the pain of flashing individual boards, repeatedly installing necessary dependencies, and building scripts to copy files to boards. Instead, a few simple commands are used to download and run a Docker image. This guarantees environment consistency and simplifies deployment across any number of embedded or IoT devices.
Let's run the same PHP example application on a Raspberry Pi 3. Some boards may require some minor Linux kernel modifications to ensure the Docker engine can run properly, but the Raspberry Pi 3 running Raspbian Strech will run Docker with no configuration changes. Follow installation instructions to install Raspbian and boot the Raspberry Pi 3 from an SD card.
The instructions to run the PHP example application are exactly the same as the AWS A1 instance. The only difference is Raspbian runs only 32-bit images. Enable the ssh server and connect to the board.
Run the same commands as on the A1 instance:
$ sudo apt-get update $ sudo apt-get upgrade $ curl -fsSL get.docker.com -o get-docker.sh && sh get-docker.sh $ sudo usermod -aG docker $USER
Add the pi user to the docker group to avoid needing sudo to run the docker command. Make sure to log out and back in again. Now test the install with a quick hello-world run:
$ docker run hello-world Unable to find image 'hello-world:latest' locally latest: Pulling from library/hello-world c1eda109e4da: Already exists Digest: sha256:92695bc579f31df7a63da6922075d0666e565ceccad16b59c3374d2cf4e8e50e Status: Downloaded newer image for hello-world:latest Hello from Docker! This message shows that your installation appears to be working correctly. To generate this message, Docker took the following steps: 1. The Docker client contacted the Docker daemon. 2. The Docker daemon pulled the "hello-world" image from the Docker Hub. (arm32v7) 3. The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading. 4. The Docker daemon streamed that output to the Docker client, which sent it to your terminal. To try something more ambitious, you can run an Ubuntu container with: $ docker run -it ubuntu bash Share images, automate workflows, and more with a free Docker ID: https://hub.docker.com/ For more examples and ideas, visit: https://docs.docker.com/get-started/
Now run the PHP example application, it's exactly the same as it was with Docker Desktop on Windows (or Mac), on the AWS A1 Arm server, and on the Raspberry Pi 3.
Open a browser on the Raspberry Pi 3 or on another computer on the network and enter in the IP address of the Pi and see the same PHP example application appear, but this time the response shows the uname as armv7l. The exact same command runs the PHP example application on the Windows (or Mac) desktop, the Arm server in AWS, and the Raspberry Pi 3.
The Docker and Arm collaboration makes software development for Arm with Docker Desktop easy, freeing software development teams to focus on what sets them apart. Leveraging multi-architecture containers for development leads to faster time to market and lower deployment costs. Almost any software project in almost any industry can leverage these benefits, from hosting a large-scale web server in the cloud to collecting data via IoT devices across the world. In the modern age, development efficiency and scalability are essential to survive and thrive as a business.
Make sure to download the Docker Desktop tech preview and try out the new features to build containers for multiple architectures and try them out on various Arm hardware systems from Raspberry Pi 3 to Amazon Web Services A1 instance powered by AWS Graviton Processors that feature 64-bit Arm Neoverse cores and custom silicon designed by AWS. If you are at DockerCon please come by the Arm booth to see more demos and talk to us about Docker on Arm.
Here are some more examples:
Cross building Arm images on Docker Desktop
Multiarch docker builds
Building Multi-Arch Images for Arm and x86 with Docker Desktop