Build for multiple architectures
JonasAlfredsson opened this issue · 5 comments
In order to do this locally, we can use Docker Buildx.
If you have Docker >= 19.03 this should already be included, otherwise the install instructions are found here. One thing that is easy to miss is the part where they mention that you will need to use this image in order to install all the QEMU dependencies used.
So my setup process went something like this:
1 - Install emulators
docker run --privileged --rm tonistiigi/binfmt --install all
2 - Initialize a builder
docker buildx create --name multiarch
docker buildx use multiarch
docker buildx inspect --bootstrap
3 - Check that it picks up all the emulators
docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS PLATFORMS
multiarch * docker-container
multiarch0 unix:///var/run/docker.sock running linux/amd64, linux/arm64, linux/386, linux/arm/v7, linux/arm/v6, linux/riscv64, linux/ppc64le, linux/s390x
default docker
default default running linux/amd64, linux/386
After the installation is should be as easy as just specifying which platforms you want to build for [1]:
docker buildx build --platform linux/arm64/v8,linux/amd64 --tag jonasal/nginx-certbot-arches .
A problem with this is that DockerHub does not support multiarch builds (at least what I can find), which means that we need to find another solution. After researching this a bit I landed on GitHub Actions, and managed to create something that I think will work.
I found that this guide was good to get started with: https://docs.docker.com/ci-cd/github-actions/
I also used these two guides to get a better grip of how Workflows function:
- https://blog.oddbit.com/post/2020-09-25-building-multi-architecture-im/
- https://www.docker.com/blog/multi-arch-build-and-images-the-simple-way/
After a lot of experimenting I came to the conclusion that I will have two different "action" files in my .github/workflows/
folder. The biggest difference between them is how they are triggered.
In the build_latest.yml
file I will have the following on the top:
on:
push:
branches:
- "main"
- "master"
paths:
- "src/**"
which means that this job will only be triggered when something is changed inside the src/
folder on either the "main
" or "master
" branch, all other changes will be ignored. This should be fine since an edit to the README would not affect the Docker container being built.
In the build_tag.yml
file I will have the following on the top:
on:
push:
branches-ignore:
- "**"
tags:
- "v[1-9].[0-9]+.[0-9]+-nginx[1-9].[0-9]+.[0-9]+"
This will first ignore any pushes to all branches, while then triggering on any tags being pushed. The semi-regex (it is not normal regex, since "." is treated as a normal period here) will make sure that we only trigger on a very specific set of tags, and in this case it has the form of my release tags.
An important thing to understand with the tag filter is that it will trigger on a valid tag on whichever branch you place it. So if you add a valid tag on the dev
branch, the action will checkout that reference and build it. However, the action script being used is the one that is in "main
/master
", oo changes to the wokflows will only take affect when they are pushed there.
One really annoying thing I ran into while building all the architectures is that there exist a really specific problem for some low level libraries when they try to read the filesytem while running inside a 32-bit environment that is emulated by QEMU running on a 64-bit host. More info about this exist in issue #30.
It seems I have been successful in this, closing for now :)