Skip to content

Commit 3913c2f

Browse files
committed
feat: enable multi-arch builds with ARM compatibility
Signed-off-by: Doug Edgar <dedgar@redhat.com>
1 parent 4ceefd3 commit 3913c2f

File tree

6 files changed

+124
-31
lines changed

6 files changed

+124
-31
lines changed

.github/workflows/build-image.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,15 @@ jobs:
2121
with:
2222
go-version-file: go.mod
2323

24+
- name: Set up Docker Buildx
25+
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435
26+
2427
- name: Login to Quay.io
2528
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
2629
with:
2730
registry: quay.io
2831
username: ${{ secrets.APP_QUAY_USERNAME }}
2932
password: ${{ secrets.APP_QUAY_TOKEN }}
3033

31-
- name: Build and push latest image
32-
run: make image-build image-push -e IMG=quay.io/llamastack/llama-stack-k8s-operator:latest
34+
- name: Build and push multi-arch image
35+
run: make image-buildx -e IMG=quay.io/llamastack/llama-stack-k8s-operator:latest

.github/workflows/generate-release.yml

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,13 @@ jobs:
157157
run: |
158158
make test
159159
160+
- name: Set up Docker Buildx
161+
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435
162+
160163
- name: Validate build release image
161164
shell: bash
162165
run: |
163-
make image-build IMG=quay.io/llamastack/llama-stack-k8s-operator:v${{ steps.validate.outputs.operator_version }}
166+
make image-buildx IMG=quay.io/llamastack/llama-stack-k8s-operator:v${{ steps.validate.outputs.operator_version }}
164167
165168
- name: Commit and push changes
166169
shell: bash
@@ -247,10 +250,8 @@ jobs:
247250
with:
248251
go-version-file: go.mod
249252

250-
- name: Build release image
251-
shell: bash
252-
run: |
253-
make image-build IMG=quay.io/llamastack/llama-stack-k8s-operator:v${{ env.operator_version }}
253+
- name: Set up Docker Buildx
254+
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435
254255

255256
- name: Log in to Quay.io
256257
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
@@ -259,11 +260,11 @@ jobs:
259260
username: ${{ secrets.APP_QUAY_USERNAME }}
260261
password: ${{ secrets.APP_QUAY_TOKEN }}
261262

262-
- name: Push release image
263+
- name: Build and push multi-arch release image
263264
shell: bash
264265
run: |
265-
make image-push IMG=quay.io/llamastack/llama-stack-k8s-operator:v${{ env.operator_version }}
266-
echo "✅ Pushed: quay.io/llamastack/llama-stack-k8s-operator:v${{ env.operator_version }}"
266+
make image-buildx IMG=quay.io/llamastack/llama-stack-k8s-operator:v${{ env.operator_version }}
267+
echo "✅ Pushed multi-arch image: quay.io/llamastack/llama-stack-k8s-operator:v${{ env.operator_version }}"
267268
268269
- name: Release complete
269270
shell: bash

.github/workflows/release-image.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,16 @@ jobs:
3030
with:
3131
ref: ${{ github.event.inputs.release_branch }}
3232

33+
- name: Set up Docker Buildx
34+
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435
35+
3336
- name: Login to Quay.io
3437
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
3538
with:
3639
registry: quay.io
3740
username: ${{ secrets.APP_QUAY_USERNAME }}
3841
password: ${{ secrets.APP_QUAY_TOKEN }}
3942

40-
- name: Build and push release image
43+
- name: Build and push multi-arch release image
4144
run: |
42-
make image -e IMG=quay.io/llamastack/llama-stack-k8s-operator:${{ github.event.inputs.version }}
45+
make image-buildx -e IMG=quay.io/llamastack/llama-stack-k8s-operator:${{ github.event.inputs.version }}

Dockerfile

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
# Build the manager binary
22
ARG GOLANG_VERSION=1.24
33

4-
FROM registry.access.redhat.com/ubi9/go-toolset:${GOLANG_VERSION} as builder
4+
# Use BUILDPLATFORM to run the builder natively (avoid QEMU emulation for Go compilation)
5+
# This dramatically improves build reliability and speed for cross-platform builds
6+
FROM --platform=$BUILDPLATFORM registry.access.redhat.com/ubi9/go-toolset:${GOLANG_VERSION} as builder
57
ARG TARGETOS=linux
68
ARG TARGETARCH
7-
ARG CGO_ENABLED=1
8-
ARG GOTAGS=strictfipsruntime,openssl
9+
ARG BUILDPLATFORM
10+
ARG TARGETPLATFORM
11+
12+
# FIPS compliance settings
13+
# For native builds: CGO_ENABLED=1 with full FIPS OpenSSL support
14+
# For cross-builds: CGO_ENABLED=0 with pure Go FIPS (strictfipsruntime)
915
ENV GOEXPERIMENT=strictfipsruntime
1016

1117
WORKDIR /workspace
@@ -24,19 +30,37 @@ COPY pkg/ pkg/
2430
COPY distributions.json distributions.json
2531

2632
# Build the manager binary
33+
# Cross-compilation is handled natively by Go via GOOS and GOARCH
34+
# This runs on the build host's native architecture, not under QEMU emulation
2735
USER root
2836

29-
# GOARCH is intentionally left empty to automatically detect the host architecture
30-
# This ensures the binary matches the platform where image-build is executed
31-
RUN CGO_ENABLED=${CGO_ENABLED} GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -a -tags=${GOTAGS} -o manager main.go
37+
# Determine if we're cross-compiling by comparing BUILDPLATFORM and TARGETPLATFORM
38+
# - Native builds (same platform): CGO_ENABLED=1 with openssl tag for full FIPS OpenSSL support
39+
# - Cross builds (different platform): CGO_ENABLED=0 with pure Go FIPS (no CGO = no cross-compiler needed)
40+
RUN echo "Building for TARGETPLATFORM=${TARGETPLATFORM} on BUILDPLATFORM=${BUILDPLATFORM}" && \
41+
if [ "${BUILDPLATFORM}" = "${TARGETPLATFORM}" ]; then \
42+
echo "Native build detected - using CGO_ENABLED=1 with OpenSSL FIPS"; \
43+
CGO_ENABLED=1 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
44+
go build -a -tags=strictfipsruntime,openssl -o manager main.go; \
45+
else \
46+
echo "Cross-compilation detected - using CGO_ENABLED=0 with pure Go FIPS"; \
47+
CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
48+
go build -a -tags=strictfipsruntime -o manager main.go; \
49+
fi
3250

33-
# Use distroless as minimal base image to package the manager binary
34-
# Refer to https://github.com/GoogleContainerTools/distroless for more details
51+
# Use UBI minimal as the runtime base image
52+
# This stage runs under QEMU for cross-platform builds, but the workload is minimal
3553
FROM registry.access.redhat.com/ubi9/ubi-minimal:latest
54+
ARG TARGETARCH
55+
3656
WORKDIR /
3757
COPY --from=builder /workspace/manager .
3858
COPY --from=builder /workspace/controllers/manifests ./manifests/
39-
RUN microdnf install -y openssl && microdnf clean all
59+
60+
# Install openssl - use minimal options for reliability under QEMU emulation
61+
RUN microdnf install -y --setopt=install_weak_deps=0 --setopt=tsflags=nodocs openssl && \
62+
microdnf clean all
63+
4064
USER 1001
4165

4266
ENTRYPOINT ["/manager"]

Makefile

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -179,21 +179,41 @@ image-push: ## Push image with the manager.
179179
$(CONTAINER_TOOL) push ${IMG}
180180

181181
# PLATFORMS defines the target platforms for the manager image be built to provide support to multiple
182-
# architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to:
182+
# architectures. (i.e. make image-buildx IMG=myregistry/myoperator:0.0.1). To use this option you need to:
183183
# - be able to use docker buildx. More info: https://docs.docker.com/build/buildx/
184184
# - have enabled BuildKit. More info: https://docs.docker.com/develop/develop-images/build_enhancements/
185185
# - be able to push the image to your registry (i.e. if you do not set a valid value via IMG=<myregistry/image:<tag>> then the export will fail)
186186
# To adequately provide solutions that are compatible with multiple platforms, you should consider using this option.
187-
PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le
188-
.PHONY: docker-buildx
189-
docker-buildx: ## Build and push docker image for the manager for cross-platform support
190-
# copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile
191-
sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross
192-
- $(CONTAINER_TOOL) buildx create --name x-builder
187+
# Note: The Dockerfile uses --platform=$BUILDPLATFORM for native cross-compilation, avoiding QEMU emulation
188+
# for Go builds which dramatically improves reliability and speed.
189+
PLATFORMS ?= linux/amd64,linux/arm64
190+
191+
.PHONY: image-buildx
192+
image-buildx: ## Build and push multi-arch image with the manager (uses native cross-compilation)
193+
@echo "Building multi-arch image for platforms: $(PLATFORMS)"
194+
ifeq ($(CONTAINER_TOOL),docker)
195+
- $(CONTAINER_TOOL) buildx create --name x-builder 2>/dev/null || true
193196
$(CONTAINER_TOOL) buildx use x-builder
194-
- $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross .
195-
- $(CONTAINER_TOOL) buildx rm x-builder
196-
rm Dockerfile.cross
197+
$(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} .
198+
else
199+
# Podman: Use manifest-based multi-arch build
200+
$(CONTAINER_TOOL) manifest rm ${IMG} 2>/dev/null || true
201+
$(CONTAINER_TOOL) manifest create ${IMG}
202+
@for platform in $$(echo $(PLATFORMS) | tr ',' ' '); do \
203+
echo "Building for $$platform..."; \
204+
$(CONTAINER_TOOL) build --platform $$platform --manifest ${IMG} . ; \
205+
done
206+
$(CONTAINER_TOOL) manifest push ${IMG}
207+
endif
208+
@echo "Successfully pushed multi-arch image: ${IMG}"
209+
210+
.PHONY: image-build-arm
211+
image-build-arm: ## Build ARM64 image with the manager
212+
$(CONTAINER_TOOL) build --platform linux/arm64 -t ${IMG} .
213+
214+
# Legacy docker-buildx target (deprecated, use image-buildx instead)
215+
.PHONY: docker-buildx
216+
docker-buildx: image-buildx ## Deprecated: use image-buildx instead
197217

198218
# Installer directory is updated to `release` from operator-sdk default `dist` directory.
199219
.PHONY: build-installer

README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,48 @@ Set `enabled: false` (or remove the block) to turn the feature back off; the ope
164164
The default image used is `quay.io/llamastack/llama-stack-k8s-operator:latest` when not supply argument for `make image`
165165
To create a local file `local.mk` with env variables can overwrite the default values set in the `Makefile`.
166166

167+
- Building multi-architecture images (ARM64, AMD64, etc.)
168+
169+
The operator supports building for multiple architectures including ARM64. To build and push multi-arch images:
170+
171+
```commandline
172+
make image-buildx IMG=quay.io/<username>/llama-stack-k8s-operator:<custom-tag>
173+
```
174+
175+
By default, this builds for `linux/amd64,linux/arm64`. You can customize the platforms by setting the `PLATFORMS` variable:
176+
177+
```commandline
178+
# Build for specific platforms
179+
make image-buildx IMG=quay.io/<username>/llama-stack-k8s-operator:<custom-tag> PLATFORMS=linux/amd64,linux/arm64
180+
181+
# Add more architectures (e.g., for future support)
182+
make image-buildx IMG=quay.io/<username>/llama-stack-k8s-operator:<custom-tag> PLATFORMS=linux/amd64,linux/arm64,linux/s390x,linux/ppc64le
183+
```
184+
185+
**Note**:
186+
- The `image-buildx` target works with both Docker and Podman. It will automatically detect which tool is being used.
187+
- **Native cross-compilation**: The Dockerfile uses `--platform=$BUILDPLATFORM` to run Go compilation natively on the build host, avoiding QEMU emulation for the build process. This dramatically improves build speed and reliability. Only the minimal final stage (package installation) runs under QEMU for cross-platform builds.
188+
- **FIPS adherence**: Native builds use `CGO_ENABLED=1` with full OpenSSL FIPS support. Cross-compiled builds use `CGO_ENABLED=0` with pure Go FIPS (via `GOEXPERIMENT=strictfipsruntime`). Both approaches are Designed for FIPS.
189+
- For Docker: Multi-arch builds require Docker Buildx. Ensure Docker Buildx is set up:
190+
191+
```commandline
192+
docker buildx create --name x-builder --use
193+
```
194+
195+
- For Podman: Podman 4.0+ supports `podman buildx` (experimental). If buildx is unavailable, the Makefile will automatically fall back to using podman's native manifest-based multi-arch build approach.
196+
- The resulting images are multi-arch manifest lists, which means Kubernetes will automatically select the correct architecture when pulling the image.
197+
198+
- Building ARM64-only images
199+
200+
To build a single ARM64 image (useful for testing or ARM-native systems):
201+
202+
```commandline
203+
make image-build-arm IMG=quay.io/<username>/llama-stack-k8s-operator:<custom-tag>
204+
make image-push IMG=quay.io/<username>/llama-stack-k8s-operator:<custom-tag>
205+
```
206+
207+
This works with both Docker and Podman.
208+
167209
- Once the image is created, the operator can be deployed directly. For each deployment method a
168210
kubeconfig should be exported
169211

0 commit comments

Comments
 (0)