Background#
Apple released a new MacBooks based on a M1 chip a while ago. Unlike all previous Apple laptops (which were Intel based),
M1 has a different architecture — arm64
instead Intel's amd64
.
New architecture means that the most software products should be repackaged: at least ones written in compiled-languages.
It applies to Jitsu which is written mostly in Go
. However, it's not 100% true: Apple went to great lengths to make
old compiled code compatible with new architecture. The piece of software that makes is possible is called
Roseta 2.
Docker tried to catch up as well: the M1 support came out a few weeks ago. However, things do not work quite as seamless as:
Docker images built for amd64
(Intel) will run on M1 Macs. But the stability isn't guaranteed. You should
ship a second version of your image for arm64
(M1 chip).
Docker relies on qemu to emulate Intel's architecture on M1 chips. Unfortunately, QEMU doesn't work quite well: However, attempts to run Intel-based containers on Apple Silicon machines can crash as QEMU sometimes fails to run the container. Filesystem change notification APIs (e.g. inotify) do not work under QEMU emulation [...]. Therefore, we recommend that you run ARM64 containers on Apple Silicon machines. These containers are also faster and use less memory than Intel-based containers. (See docker documentation for more details)
Why care about M1 macs at all? Aren't all servers still run on Intel anyway? That's a great question! It's true, most of the servers runs on Intel platform. However, at Jitsu we committed to provide an excellent developer's experience. We're sure that stable work at a developer's machine is as much important as server's stability. Besides, 1/2 of our dev team already got a new M1 Macs. So starting from 1.29.0 we decided to ship all releases as dual-architecture images.
We're convinced that all other open-source project should do the same. Therefore, we decided to share our experience with multi-architecture docker builds.
How to build an image for arm64
#
Luckily, Docker has announced the support of cross CPU architecture builds a few weeks ago. buildx - an experimental feature that allows building images for a certain architecture - arm64, amd64, etc.
Step 1. Enable experimental features#
If you’re using Docker Desktop for Mac you should just enable experimental features on the Docker CLI if you haven’t already – just edit ~/.docker/config.json to include the following:
{
...
"experimental": "enabled"
}
Restart your Docker Desktop after that
If you’re using Linux, just run the following command:
docker run --privileged --rm docker/binfmt:a7996909642ee92942dcd6cff44b9b95f08dad64
And restart Docker with service docker restart
Step 2. Create a new builder instance#
Create a new builder instance:
docker buildx create --use
It enables multiple builds in parallel. Make sure that new builder instance is created:
docker buildx ls
Output:
$ docker buildx ls NAME/NODE DRIVER/ENDPOINT STATUS PLATFORMS mystifying_bell * docker-container mystifying_bell0 unix:///var/run/docker.sock inactive default docker default default running linux/amd64, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
Step 3. Build multi-architecture image#
Build your multi-architecture image:
docker buildx build --platform linux/amd64,linux/arm64 -t company/image_name .
platform flag specifies for which platforms Docker image will be built. Docker support 10 platforms,
but probably you shouldn use only linux/amd64
(Intel) and linux/arm64
(M1):

Build multi-architecture images on CircleCI#
At Jitsu we use CircleCI for building and pushing our Docker images to Docker Hub. There is an example of .circleci/config.yaml with building multi-architecture Docker images on every git push to master branch:
install_buildx: &install_buildx
run:
name: install docker buildx
command: |
mkdir -vp ~/.docker/cli-plugins/
curl --silent -L --output ~/.docker/cli-plugins/docker-buildx https://github.com/docker/buildx/releases/download/v0.3.1/buildx-v0.3.1.linux-amd64
chmod a+x ~/.docker/cli-plugins/docker-buildx
docker buildx version
docker context create jitsu
docker buildx create jitsu --use
docker run --privileged --rm tonistiigi/binfmt --install all
build_jitsu: &build_jitsu
run:
name: Build and Push Jitsu Docker image
command: |
docker login -u $DOCKER_LOGIN -p $DOCKER_PWD
DOCKER_BUILDKIT=1 docker buildx build --platform linux/amd64,linux/arm64 --push -t $IMAGE_NAME:$IMAGE_VERSION -f $DOCKER_FILE .
version: 2.1
jobs:
build-latest-jitsu-docker:
environment:
IMAGE_NAME: jitsucom/server
IMAGE_VERSION: latest
DOCKER_FILE: server.Dockerfile
docker:
- image: cimg/go:1.14.15
steps:
- checkout
- setup_remote_docker:
version: 19.03.13
- <<: *install_buildx
- <<: *build_jitsu
workflows:
version: 2.1
build-docker:
jobs:
- build-latest-jitsu-docker:
context: jitsu
filters:
branches:
only: master