Run an ARM32 Docker daemon on ARM64 servers Credits: Yannick Pipke on Unsplash

Run an ARM32 Docker daemon on ARM64 servers

In the last days I worked on a suitable setup for a Drone CI server to support multi-arch builds. While the setup for common x86 Drone runners is easy, working with setups for ARM, especially ARM32, is a bit tricky. The easiest way would be to have native servers of the respective architecture available. However, it’s difficult to find hosting offers for ARM at all - for ARM32 this seems almost impossible. I decided to use Amazon EC2 ARM64 servers, they are relatively cheap and can also be used as a private customer.

But how do you turn an ARM64 server into an ARM32 server? Basic requirement is an ARMv8 CPU. This type supports (in most cases) both AArch32 and AArch64. The first step is to enable multi-arch support on your operating system. I use Ubuntu 18.10 for this setup:

dpkg --add-architecture armhf

Then add the Docker CE repository for ARM32 and install the packages:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# add GPG key
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

# add repos
add-apt-repository \
   "deb [arch=armhf] https://download.docker.com/linux/ubuntu \
   $RELEASE \
   stable

# install
apt-get update
apt-get install docker-ce:armhf

To use the right architecture within Docker containers you still have to force the Docker daemon into ARM32 mode. This can be accomplished by two systemd overwrites:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# overwrite docker.service
# /etc/systemd/system/docker.service.d/override.conf
[Service]
ExecStart=
ExecStart=/usr/bin/setarch linux32 -B /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

# overwrite containerd.service
# /etc/systemd/system/containerd.service.d/override.conf
[Service]
ExecStart=
ExecStart=/usr/bin/setarch linux32 -B /usr/bin/containerd

That’s it. Don’t forget to reload and restart the systemd daemon:

systemctl, daemon reload
systemctl, restart, docker

What you get is a Docker daemon that runs in ARM32 mode. This can be used for example to start a Golang container and build apps without cross-compile.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
root@ip-10-0-225-151:~# docker version
Client: Docker Engine - Community
 Version:           19.03.13
 API version:       1.40
 Go version:        go1.13.15
 Git commit:        4484c46
 Built:             Wed Sep 16 17:07:23 2020
 OS/Arch:           linux/arm
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.13
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.13.15
  Git commit:       4484c46
  Built:            Wed Sep 16 17:01:08 2020
  OS/Arch:          linux/arm
  Experimental:     false
 containerd:
  Version:          1.3.7
  GitCommit:        8fba4e9a7d01810a393d5d25a3621dc101981175
 runc:
  Version:          1.0.0-rc10
  GitCommit:        dc9208a3303feef5b3839f4323d9beb36df0a9dd
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683
root@ip-10-0-225-151:~# docker run alpine uname -a
Linux dbad5ddeb5ea 5.3.0-1035-aws #37-Ubuntu SMP Sun Sep 6 01:17:41 UTC 2020 armv8l Linux

Just a word of warning. I’m not a hardware specialist and there might be some situations where this setup does not work. Furthermore it seems that some operating systems have problems to recognize armv8l as ARM32 architecture. For OpenSuse as an example I had to force zypper into armv7hl mode to get it working. After this step building ARM32 OpenSuse Docker images also works for me.

sed -i 's/# arch = s390/arch = armv7hl/g' /etc/zypp/zypp.conf

If you want to give it a try, I’ve prepared a minimal Cloud-Init configuration:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#cloud-config

apt_reboot_if_required: false
package_update: true
package_upgrade: true

bootcmd:
  - [ dpkg, --add-architecture, armhf  ]

apt:
  sources:
    docker.list:
      source: deb [arch=armhf] https://download.docker.com/linux/ubuntu $RELEASE stable
      keyid: 0EBFCD88

packages:
  - 'docker-ce:armhf'

write_files:
  - path: /etc/systemd/system/docker.service.d/override.conf
    content: |
      [Service]
      ExecStart=
      ExecStart=/usr/bin/setarch linux32 -B /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock      

  - path: /etc/systemd/system/containerd.service.d/override.conf
    content: |
      [Service]
      ExecStart=
      ExecStart=/usr/bin/setarch linux32 -B /usr/bin/containerd      

runcmd:
  - [ systemctl, daemon-reload ]
  - [ systemctl, restart, docker ]