wissel.net

Usability - Productivity - Business - The web - Singapore & Twins

Building ARM64 on Github


Getting your CI/CD pipeline right can be a daunting task. Here is one I had to address:

  • Create a Quarkus Java application
  • Compile it to a native executable
  • Build a container for it
  • Make the container available to both Linux and MacOS

The little irony, Docker on macOS or Windowsruns Linux under the hood.

The easy part - Quarkus

As I've written before it is easy to get started with Quarkus. It provides 5 ways to build containers, and detailed instructions to build a native image.

Building a native image looked daunting, with quite some prerequisites like GraalVM, CLI and C compiler. Luckily all this is available in a builder image, and a simple property settin in your pom.xml settles it:

    <properties>
        <quarkus.container-image.group>stwissel</quarkus.container-image.group>
        <quarkus.container-image.name>code-with-quarkus</quarkus.container-image.name>
        <quarkus.container-image.tag>${project.version}</quarkus.container-image.tag>
        <quarkus.native.builder-image>quay.io/quarkus/ubi-quarkus-mandrel-builder-image:jdk-21</quarkus.native.builder-image>
    </properties>

I use Google's gib project to build containers since is doesn't depend on local Docker, is optimized for Java and Quarkus supports it well. All it takes is a dependency:

 <dependencies>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-container-image-jib</artifactId>
        </dependency>
    </dependencies>

Building a native image now is just a Maven command away:

#!/bin/bash
# run a build on github

version=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
if [[ "$BRANCH_NAME" == "main" ]]; then
    tag="latest"
else
    tag="$BRANCH_NAME"
fi

mvn -Pnative \
    --no-transfer-progress \
    --show-version \
    --fail-fast \
    --batch-mode \
    -DskipTests \
    -Dquarkus.container-image.push=true \
    -Dquarkus.container-image.tag=${tag} \
    -Dquarkus.container-image.additional-tags=${version} \
    clean package

The result lands in Github's container registry ghcr.io, but you can target any registry Github can reach.

All fine and dandy? Not so fast! Native means: depending on the hardware architecture. You build on GitHub, you get amd64 code by default, you build on your M1|2|3|4 Mac, you get arm64 code.

While you can use QEMU to bridge the platform gap, you would need to package all steps inside a single run-on-architecture action instead of picking what you fancy from the marketplace.

Using a native runner

Github provides multiple runners for the runs-on entry in your yaml file. Typically you would have something like ubuntu-latest. I tried macos-latest only to learn that it doesn't come with Docker installed and fiercly resisted any installation attempt.

Luckily GitHub allows you to create arm64 runners. You need an organisation for that. Head to
https://github.com/organizations/[insert-org-name-here]/settings/actions/runners and start defining a new runner. When you are on the free plan, you have to host it yourself, on a paid plan GitHub offers:

  • Linux x64
  • Linux ARM64
  • Windows x64
  • Windows ARM64

The wizzard walks you through all steps, picking size, image and concurrency. I build the smallest Linux ARM64 image and named it ArmRunner. Add it to a runner group that allows your repo, in my case called Public. A small update to the workflow and you are set:

jobs:
  arm_build:
    runs-on:
      group: Public
      labels: ArmBuilder
    name: Build arm image
    steps:
    # Define your usual steps here

As usual YMMV


Posted by on 20 November 2024 | Comments (0) | categories: Java Quarkus WebDevelopment

Comments

  1. No comments yet, be the first to comment