Skip to content

Commit eb696cb

Browse files
authored
fix(build): cross-compile Docker images instead of emulating with QEMU (#2339)
The multi-arch release build ran the full Rust release build, the cgo-linked Go build, and go mod download under QEMU for linux/arm64, making the docker publish job take hours on an amd64 runner. Builder stages are now pinned to the build platform and cross-compile to TARGETARCH: diffgen via rustup target aarch64-unknown-linux-gnu and the Go binary via GOARCH/CC with the aarch64 cross toolchain. Only the small final runtime stage runs under emulation. Also fetch cargo dependencies from the manifests alone so the layer survives diffgen source changes, and build both release platforms in the PR workflow so cross-compilation breakage surfaces before release.
1 parent a33bb6c commit eb696cb

2 files changed

Lines changed: 62 additions & 15 deletions

File tree

.github/workflows/build.yml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ permissions:
66
jobs:
77
build:
88
runs-on: ubuntu-latest
9-
timeout-minutes: 20
9+
timeout-minutes: 30
1010
steps:
1111
- name: Harden Runner
1212
uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0
@@ -30,7 +30,12 @@ jobs:
3030
difference=$((final_space - initial_space))
3131
echo "Disk space difference (in KB): $difference"
3232
33+
- name: Set up Docker Buildx
34+
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
35+
36+
# Build both release platforms so PRs catch cross-compilation breakage
37+
# before it reaches the release workflow
3338
- name: Build Container
34-
run: make docker
39+
run: docker buildx build --platform linux/amd64,linux/arm64 -f build/Dockerfile .
3540
env:
3641
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

build/Dockerfile

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,71 @@
1-
FROM rust:bookworm@sha256:29fe4376919e25b7587a1063d7b521d9db735fc137d3cf30ae41eb326d209471 AS rust-builder-base
2-
COPY external/diffgen/ /app/external/diffgen
3-
RUN cd /app/external/diffgen/ && cargo fetch
1+
## Builder stages run on the build platform and cross-compile to TARGETARCH,
2+
## so multi-arch builds don't pay for QEMU emulation of cargo/rustc/go.
3+
FROM --platform=$BUILDPLATFORM rust:bookworm@sha256:29fe4376919e25b7587a1063d7b521d9db735fc137d3cf30ae41eb326d209471 AS rust-builder-base
4+
WORKDIR /app/external/diffgen
5+
COPY external/diffgen/Cargo.toml external/diffgen/Cargo.lock ./
6+
## cargo fetch only needs the manifests; a stub lib.rs keeps this layer
7+
## cached across source-only changes
8+
RUN mkdir -p src && touch src/lib.rs && cargo fetch
49

510
FROM rust-builder-base AS rust-builder
11+
ARG BUILDARCH
12+
ARG TARGETARCH
13+
ENV CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=x86_64-linux-gnu-gcc \
14+
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc
15+
RUN case "$TARGETARCH" in \
16+
amd64) echo x86_64-unknown-linux-gnu > /rust-target ;; \
17+
arm64) echo aarch64-unknown-linux-gnu > /rust-target ;; \
18+
*) echo "unsupported TARGETARCH: $TARGETARCH" >&2; exit 1 ;; \
19+
esac && \
20+
rustup target add "$(cat /rust-target)" && \
21+
if [ "$BUILDARCH" != "$TARGETARCH" ]; then \
22+
apt-get update && \
23+
case "$TARGETARCH" in \
24+
amd64) apt-get install -y --no-install-recommends gcc-x86-64-linux-gnu libc6-dev-amd64-cross ;; \
25+
arm64) apt-get install -y --no-install-recommends gcc-aarch64-linux-gnu libc6-dev-arm64-cross ;; \
26+
esac && \
27+
rm -rf /var/lib/apt/lists/*; \
28+
fi
29+
COPY external/diffgen/src ./src
30+
## cgo links target/release/libdiffgen.a by fixed path, so place the
31+
## cross-compiled staticlib where a native build would put it
32+
RUN cargo build --release --target "$(cat /rust-target)" && \
33+
mkdir -p target/release && \
34+
cp "target/$(cat /rust-target)/release/libdiffgen.a" target/release/libdiffgen.a
35+
36+
FROM --platform=$BUILDPLATFORM golang:1.26-bookworm@sha256:4f4ab2c90005e7e63cb631f0b4427f05422f241622ee3ec4727cc5febbf83e34 AS builder-base
637
WORKDIR /app
7-
COPY Makefile /app
8-
COPY external/diffgen /app/external/diffgen
9-
RUN make rust-diffgen
10-
11-
FROM golang:1.26-bookworm@sha256:4f4ab2c90005e7e63cb631f0b4427f05422f241622ee3ec4727cc5febbf83e34 AS builder-base
12-
WORKDIR /app
13-
14-
ARG VERSION
1538

1639
COPY go.mod /app/go.mod
1740
COPY go.sum /app/go.sum
1841
RUN go mod download
1942

2043
FROM builder-base AS builder
2144

45+
ARG VERSION
46+
ARG BUILDARCH
47+
ARG TARGETARCH
48+
RUN if [ "$BUILDARCH" != "$TARGETARCH" ]; then \
49+
apt-get update && \
50+
case "$TARGETARCH" in \
51+
amd64) apt-get install -y --no-install-recommends gcc-x86-64-linux-gnu libc6-dev-amd64-cross ;; \
52+
arm64) apt-get install -y --no-install-recommends gcc-aarch64-linux-gnu libc6-dev-arm64-cross ;; \
53+
*) echo "unsupported TARGETARCH: $TARGETARCH" >&2; exit 1 ;; \
54+
esac && \
55+
rm -rf /var/lib/apt/lists/*; \
56+
fi
57+
2258
COPY ./ ./
2359

24-
COPY --from=rust-builder /app/external/diffgen/target ./external/diffgen/target
60+
COPY --from=rust-builder /app/external/diffgen/target/release ./external/diffgen/target/release
2561

26-
RUN make build-prod
62+
RUN if [ "$BUILDARCH" != "$TARGETARCH" ]; then \
63+
case "$TARGETARCH" in \
64+
amd64) export CC=x86_64-linux-gnu-gcc ;; \
65+
arm64) export CC=aarch64-linux-gnu-gcc ;; \
66+
esac; \
67+
fi && \
68+
CGO_ENABLED=1 GOOS=linux GOARCH="$TARGETARCH" make build-prod
2769

2870
FROM flanksource/base-image:0.7.1@sha256:5e819e83199855d20ba5b988e8a6f369c6f4332fa6f07c184c99838d06a77cc8
2971

0 commit comments

Comments
 (0)