ARM

Modern Docker builds should treat CPU architecture as an explicit build output. A developer may build on an amd64 laptop, deploy to arm64 cloud nodes, and still publish one image name that works for both platforms. See the cross-builds chapter for the full Mac-to-x86, x86-to-ARM64, and multi-platform release workflow.

Use Buildx for multi-platform builds.

1docker buildx ls
2docker buildx create --name multiarch --driver docker-container --use
3docker buildx inspect --bootstrap

If the builder needs CPU emulation, install QEMU/binfmt support on the build host.

1docker run --privileged --rm tonistiigi/binfmt --install all

Build and push one manifest list containing both amd64 and arm64 images.

1docker buildx build \
2    --platform linux/amd64,linux/arm64 \
3    -t registry.example.com/team/app:latest \
4    --push .

For local testing, load a single-platform image into the local Docker image store.

1docker buildx build --platform linux/arm64 --load -t app:arm64 .

Do not assume that every base image supports every platform. Check the manifest before choosing the base image.

1docker buildx imagetools inspect python:3-slim

Basic ARM container

An example Dockerfile to build a basic ARM container for Raspberry Pi-style work is as follows.

 1FROM arm32v7/ubuntu
 2
 3ENV DEBIAN_FRONTEND=noninteractive
 4
 5RUN apt-get update -y && \
 6    apt-get upgrade -y
 7
 8RUN apt-get install -y \
 9    wget \
10    build-essential \
11    tk-dev \
12    libncurses5-dev \
13    libncursesw5-dev \
14    libreadline6-dev \
15    libdb5.3-dev \
16    libgdbm-dev \
17    libsqlite3-dev \
18    libssl-dev \
19    libbz2-dev \
20    libexpat1-dev \
21    liblzma-dev \
22    zlib1g-dev \
23    libffi-dev \
24    gfortran \
25    libopenblas-dev \
26    liblapack-dev \
27    python-dev \
28    default-libmysqlclient-dev \
29    libfreetype6-dev \
30    libxml2-dev \
31    libxslt1-dev
32
33RUN apt-get clean

Notice the following.

  • The base image is arm32v7/ubuntu.

  • The ENV DEBIAN_FRONTEND=noninteractive instruction is to enable non-interactive installs (so we do not get prompted for Yes or No).

  • We install a lot of system libraries since we want to do interesting things later on.

Build.

1docker build --no-cache -t rpi-base:local .

Run.

1docker run -it rpi-base:local

Jupyter Lab

What if we want to have a reproducible, consistent data science work environment on the Raspberry Pi? We can build a container to spin up a Jupyter Lab instance.

 1FROM rpi-base:local
 2
 3ENV CONDA_HOME=/root/miniconda
 4ENV PATH=${CONDA_HOME}/bin:${PATH}
 5ENV JUPYTER_TYPE=lab
 6
 7# setup OS
 8RUN apt-get update -y && \
 9    apt-get upgrade -y && \
10    apt-get install supervisor -y
11
12# setup supervisor
13COPY jupyter.conf /etc/supervisor/conf.d/
14
15# setup miniconda
16RUN wget -q http://repo.continuum.io/miniconda/Miniconda3-latest-Linux-armv7l.sh -O /tmp/miniconda.sh && \
17    /bin/bash /tmp/miniconda.sh -b -p /root/miniconda && \
18    conda update -n root conda -y && \
19    conda update --all -y && \
20    pip install --upgrade pip && \
21    conda config --add channels rpi && \
22    conda install python=3.6 -y && \
23    conda install jupyter jupyterlab nodejs scikit-learn numpy scipy matplotlib -y -c anaconda -c conda-forge
24
25# setup mount point
26RUN mkdir /ipynb
27VOLUME ["/ipynb"]
28
29# setup ports
30EXPOSE 8888
31
32# clean up
33RUN apt-get clean
34
35CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf", "-n"]

Notice the following.

  • We use the previous rpi-base:local image to bootstrap this image.

  • We install miniconda to manage our Python environments and dependencies.

  • We setup a mount volume at /ipynb so that we can mount our local notebooks onto the container at runtime.

  • We expose port 8888, which is what Jupyter Lab is running on.

  • We use supervisor to start up Jupyter Lab at runtime.

  • If you do not like Jupyter Lab and want the classic Jupyter Notebook, override at runtime with -e JUPYTER_TYPE=notebook.

Build.

1docker build --no-cache -t rpi-jupyter:local .

Run.

1docker run \
2    -it \
3    --rm \
4    -p 8888:8888 \
5    -v `pwd`/ipynb:/ipynb \
6    rpi-jupyter:local

You may now access the Jupyter Lab at http://localhost:8888.

References