Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support building multi-platform docker images #256

Merged
merged 1 commit into from
Jan 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 18 additions & 14 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,30 +1,34 @@
ARG DIST=nginx:1.19.6
FROM $DIST
ARG ARCH=amd64

# Set by `docker buildx build`
ARG TARGETPLATFORM

# Delete sym links from nginx image, install logrotate
RUN rm /var/log/nginx/access.log && \
rm /var/log/nginx/error.log

WORKDIR /root

RUN apt-get update && \
apt-get install -y python ruby cron iproute2 apache2-utils logrotate wget && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

# Need this already now, but cannot copy remainder of fs_overlay yet
COPY ./fs_overlay/bin/archname /bin/

ENV S6_OVERLAY_VERSION v1.22.1.0
ENV DOCKER_GEN_VERSION 0.7.4
ENV ACME_TINY_VERSION 4.1.0

ADD https://github.com/just-containers/s6-overlay/releases/download/$S6_OVERLAY_VERSION/s6-overlay-$ARCH.tar.gz /tmp/
ADD https://github.com/jwilder/docker-gen/releases/download/$DOCKER_GEN_VERSION/docker-gen-linux-$ARCH-$DOCKER_GEN_VERSION.tar.gz /tmp/
ADD https://raw.githubusercontent.com/diafygi/acme-tiny/$ACME_TINY_VERSION/acme_tiny.py /bin/acme_tiny

RUN tar xzf /tmp/s6-overlay-$ARCH.tar.gz -C / &&\
tar -C /bin -xzf /tmp/docker-gen-linux-${ARCH}-$DOCKER_GEN_VERSION.tar.gz && \
rm /tmp/docker-gen-linux-$ARCH-$DOCKER_GEN_VERSION.tar.gz && \
rm /tmp/s6-overlay-$ARCH.tar.gz && \
rm /etc/nginx/conf.d/default.conf && \
apt-get update && \
apt-get install -y python ruby cron iproute2 apache2-utils logrotate && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
RUN sh -c "wget -q https://github.com/just-containers/s6-overlay/releases/download/$S6_OVERLAY_VERSION/s6-overlay-`archname s6-overlay`.tar.gz -O -" | \
tar xzC /
RUN sh -c "wget -q https://github.com/jwilder/docker-gen/releases/download/$DOCKER_GEN_VERSION/docker-gen-linux-`archname docker-gen`-$DOCKER_GEN_VERSION.tar.gz -O -" | \
tar xzC /bin
RUN wget -q https://raw.githubusercontent.com/diafygi/acme-tiny/$ACME_TINY_VERSION/acme_tiny.py -O /bin/acme_tiny

RUN rm /etc/nginx/conf.d/default.conf

COPY ./fs_overlay /

Expand Down
56 changes: 56 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Create multi-platform docker image. If you have native systems around, using
# them will be much more efficient at build time. See e.g.
# https://netfuture.ch/2020/05/multi-arch-docker-image-easy/
BUILDXDETECT = ${HOME}/.docker/cli-plugins/docker-buildx

# Just one of the many files created
QEMUDETECT = /proc/sys/fs/binfmt_misc/qemu-m68k

# Override this, if you want to use your own registry
REGISTRY = steveltn
# For version x.y.z, output "-t …:x.yz -t …:x.y -t …:x";
# for anything else, output nothing
BASETAG = ${REGISTRY}/https-portal
VERSIONTAGS = $(shell git describe --tags | \
sed -n -e 's,^\(\(\([0-9]*\).[0-9]*\).[0-9]*\).*,-t ${BASETAG}:\1 -t ${BASETAG}:\2 -t ${BASETAG}:\3,p')

# For platform compatibility/naming matrix, see `./fs_overlay/bin/archname`
#
# Not building for linux/arm/v5, as building the docker image fails with:
# Error while loading /usr/sbin/dpkg-split: No such file or directory
# Error while loading /usr/sbin/dpkg-deb: No such file or directory

PLATFORMS = linux/386,linux/amd64,linux/arm/v7,linux/arm64/v8

docker-multiarch: qemu buildx docker-multiarch-builder
docker login
docker buildx build --builder docker-multiarch --pull --push \
--platform ${PLATFORMS} \
${VERSIONTAGS} -t ${BASETAG}:latest .

qemu: ${QEMUDETECT}
${QEMUDETECT}:
docker pull multiarch/qemu-user-static
docker run --privileged multiarch/qemu-user-static --reset -p yes
docker ps -a | sed -n 's, *multiarch/qemu-user-static.*,,p' \
| (xargs docker rm 2>&1 || \
echo "Cannot remove docker container on ZFS; retry after next reboot") \
| grep -v 'dataset is busy'

buildx: ${BUILDXDETECT}
${BUILDXDETECT}:
@echo
# Output of `uname -m` is too different
@echo '*** `docker buildx` missing. Install binary for this machine architecture'
@echo '*** from `https://github.com/docker/buildx/releases/latest`'
@echo '*** to `~/.docker/cli-plugins/docker-buildx` and `chmod +x` it.'
@echo
@exit 1

docker-multiarch-builder: qemu buildx
if ! docker buildx ls | grep -w docker-multiarch > /dev/null; then \
docker buildx create --name docker-multiarch && \
docker buildx inspect --builder docker-multiarch --bootstrap; \
fi

.PHONY: qemu buildx docker-multiarch docker-multiarch-builder
28 changes: 23 additions & 5 deletions Readme.armhf
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
#to build armhf image from amd64 machine:
#Install qemu, see https://github.com/multiarch/qemu-user-static
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
#Pass relevant args to the docker build cmd
docker build --build-arg DIST=arm32v7/nginx:1.17.3 --build-arg ARCH=armhf -t https-portal:armhf-master .
# Cross-platform/multi-platform docker images
Docker images for different and/or multiple architectures may be built using
`sudo make docker-multiarch`.

# Single-platform image for the current platform (compatibility)
As usual: `docker build .`

# Basic multi-platform build
Publish to your private docker registry, using the default set of platforms:

`sudo make docker-multiarch REGISTRY=docker.example.com`

# Specify your list of platforms
`sudo make docker-multiarch REGISTRY=docker.example.com PLATFORMS=linux/arm/v7`

For details on the supported platforms, see `./fs_overlay/bin/archname`.

# Set-up
In order to support cross-platform building, you need to install
`docker-buildx`. It can be downloaded from
https://github.com/docker/buildx/releases/latest and needs to be installed
as `~/.docker/cli-plugins/docker-buildx` and `chmod +x`ed. See
https://github.com/docker/buildx for more information.
80 changes: 80 additions & 0 deletions fs_overlay/bin/archname
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#/bin/sh
# Usage: archname.sh <PACKAGE>
#
# Outputs the correct architecture string for the named package, translating
# between the different platform strings. The nice things about standards…
# PACKAGE is one of "s6-overlay" or "docker-gen"
#
# Reads the docker architecture from $TARGETPLATFORM (set by `docker buildx
# build`) and outputs the appropriate per-package architecture name. If
# $TARGETPLATFORM is unset (using legacy `docker build`), it is determined from
# the output for `arch`, which is unreliable in case it is run in an emulator.
# If you really plan to build inside an emulator with the legacy build process,
# you are on your own.


# Platform compatibility/naming matrix as of 2020-01-17:
# - Nginx: https://hub.docker.com/_/nginx?tab=tags
# - S6 Overlay: https://github.com/just-containers/s6-overlay/releases
# - Docker-Gen: https://github.com/jwilder/docker-gen/releases
# - ACME-Tiny: No architecture/platform dependencies
# - Stars elsewhere are not part of the output, but indicate special values
#
# Nginx (Docker) | `arch` output | S6 Overlay | Docker-Gen | ACME-Tiny
# ---------------+---------------+------------+------------+----------
# linux/386 | x86_64* | x86 | i386 | *
SteveLTN marked this conversation as resolved.
Show resolved Hide resolved
# linux/amd64 | x86_64 | amd64 | amd64 | *
# linux/arm/v5 | armv7l* | arm | armel | *
# linux/arm/v6* | armv7l* | arm | armel | *
# linux/arm/v7 | armv7l | armhf | armhf | *
# linux/arm64/v8 | aarch64 | aarch64 | armhf* | *
# linux/mips64le | mips64 | --- | --- | *
# linux/ppc64le | ppc6le | ppc64le | --- | *
# linux/s390x | s390x | --- | --- | *
#
# Notes:
# - nginx:1.19.6 does not support linux/arm/v6
# - `arch` output as reported potentially inside the emulater with
# `docker buildx build --builder docker-multiarch --platform ...`
# - Assuming linux/arm/v5 and linux/arm/v6 are user-level compatible (for
# S6 Overlay and Docker-Gen)
# - Assuming Docker-Gen armhf is user-level compatible with linux/arm64/v8

# This is only used for `docker build`, so we should not run inside the
# emulator:
if [ "x$TARGETPLATFORM" = "x" ]; then
case `arch` in
# Extend this list as necessary
x86_64) TARGETPLATFORM=linux/amd64;;
armv7l) TARGETPLATFORM=linux/arm/v7;;
aarch64) TARGETPLATFORM=linux/arm64/v8;;
*) echo "$0: Empty TARGETPLATFORM, unsupported ARCH: `arch`">&2; exit 1;;
esac
fi

case "$1" in
s6-overlay)
case "$TARGETPLATFORM" in
linux/386) echo x86;;
linux/amd64) echo amd64;;
linux/arm/v5) echo arm;;
linux/arm/v6) echo arm;;
linux/arm/v7) echo armhf;;
linux/arm64/v8) echo aarch64;;
*) echo "$0: Unsupported TARGETPLATFORM: $TARGETPLATFORM" >&2; exit 1;;
esac
;;
docker-gen)
case "$TARGETPLATFORM" in
linux/386) echo i386;;
linux/amd64) echo amd64;;
linux/arm/v5) echo armel;;
linux/arm/v6) echo armel;;
linux/arm/v7) echo armhf;;
linux/arm64/v8) echo armhf;;
*) echo "$0: Unsupported TARGETPLATFORM: $TARGETPLATFORM" >&2; exit 1;;
esac
;;
*)
echo "$0: Unsupported package: $1" >&2; exit 1;;
esac