Following on from my article on faster multi-arch container builds using rust, today I’ll be applying the same pattern to a go service. For the service I’ve used, my GitHub multi-arch build times dropped down from roughly 6 minutes to 1:30!
If you’re not familiar with how buildx handles multi-arch builds, have a quick skim over the rust version of this blog so this makes sense. With go, cross-compilation is even easier than rust. If you are targeting Linux you don’t need to depend on glibc or musl as go takes advantage of the Linux system call API being stable and uses it directly rather than calling through a libc-style library 1. For a simple service without native C dependencies, it’s super easy to whittle things down to a single, focused binary, and we don’t need to do wild things with LLVM now either.
In comparison to the rust version, there’s no system packages to install at all, and we can convert the docker buildx style TARGETPLATFORM easily with a bash substitution inline. And, we get a nice, tiny ~25 MiB image out at the end too. Once again, there’s a complete worked Dockerfile example on github and associated GitHub workflow.
Footnotes
-
libc-style libraries are used on Linux to provide user-space access to the kernel. They wrap up the kernel API itself in a friendly C-API. Glibc is the most well-known, whilst musl is a smaller, typically-statically-linked variant popular for optimising docker image size. Going a little deeper, the kernel API is invoked by putting magic numbers into particular registers and firing off an IRQ to jump back into kernel space. This is naturally not the most ergonomic thing to do, which is why libc exists. ↩