Multi-arch registries

I’m trying to find out about the multi-arch support of the Docker registry, and so far I’ve found things like resin/rpi-raspbian and so forth.

I’m a little uncertain about what Docker does and does not support by multi-architecture; rather than probe around, I’ve decided the most productive way to approach the topic is to assume the support is terrible or non-existent, and discuss how to implement an ideal registry to find out how close Docker is to this.

The ideal

Ideally, Docker should “just work”. A docker pull redis:3-alpine should provide the expected result on x86-64 or ARM, for example. By that logic, whatever redis:3-alpine builds FROM should also just work, and should build from the same Dockerfile.

Speculative Proposal

The simplest way to achieve this, on the registry side, is to have Docker try to docker pull from the correct architecture on the registry. If there’s an x86-64/debian:jessie, it works; if you’re on a Raspberry Pi and it tries to pull arm6/debian:jessie, it doesn’t.

On the build end, it’s a little more complex.

Obviously, the above implies that builds just work, so long as you have the right containers available: if your Dockerfile builds FROM java:jre9, and that builds FROM debian:jessie, and there is a debian:jessie image on x86-64 and arm6, then your Dockerfile probably builds just fine. That’s the easy part: Docker pulls the appropriate architecture, so the correct base image comes down in all cases.

Conditional builds

The first portion of multi-arch building would be conditional builds and architecture tagging.

Imagine a Dockerfile as such:

# debian:jessie
FROM scratch

# Unpack Debian x86-64 architecture base
IFARCH x86-64
ADD rootfs-x64.tar.xz
ENDIFARCH

# Unpack the Raspberry Pi base if it's any of these four archs
IFARCH arm7 arm8 arm8.1 rpi
ADD rootfs-rpi.tar.xz
# This image is compatible with any of these architectures,
# and does not get built or stored separately for them all
TAGARCH arm7 arm8 arm8.1 rpi
ENDIFARCH

RUN \
     export DEBCONF_FRONTEND=noninteractive \
  && apt-get update \
  && apt-get upgrade \
  && apt-get clean \
  && env -i \
  && find /var/lib/apt/lists -type f -delete

CMD ["/bin/bash"]

A Dockerfile as above would simply select the correct rootfs-$ARCH.tar.xz to unpack. For multiple compatible architectures, it will also tag itself as being for those several architectures.

This has a major backwards-compatibility advantage: any existing Dockerfile built on a given architecture will just run as-is and tag itself with the current architecture. It will either build or fail; and if it fails, it won’t produce an image. That means nothing breaks.

At the same time, architecture-specific actions such as installing a precompiled binary can occur based on the architecture. This requires modification to the Dockerfile; and that’s a trivial modification, likely by encircling single-actions in a conditional block.

Further, to avoid clutter, we could tag things to run on multiple architectures, such that forward-compatible builds are allowed. This might be better handled by the hub preferring a more-specific build (armv8.1) over a compatible one (armv8); I’m not sure.

Conclusion

Engineering for multi-architecture should be relatively straight-forward. I haven’t found documentation yet explaining how this works currently, at least not in any way I’ve been able to understand. What’s clear so far is it doesn’t “just work”, as we don’t have a repository built for multiple architectures–notably the Raspberry Pi–and there are entries in the Hub specifically for Pi, rather than just “This is Debian” and it gives you the correct image if you ask for debian:jessie.