Building a Multi-Arch Operator Image

This section will guide through all of the steps involved with building, and hosting a multi-arch operator image.

Please note that the following build process is for demonstration purposes only and is NOT supported by Red Hat due to the use of emulation software to enable cross-compilation and building.

You should always build containers and application binaries on the native host platform that you intend to support. Emulation software should only be used to create non-production builds for testing.

It should also be noted that this procedure will NOT work in RHEL because the required emulation software is intentionally unavailable. Please see

Here we showcase how to build an operator controller image targeting other (non-Intel/AMD) CPU architectures for hosting in an external registry. It also covers assembling the various architecture images into a single manifest list and tag. This section assumes that you've already made the required changes to the operator's Dockerfile (labels and licenses) when certifying for Intel/AMD.

Building an operand image for other CPU architectures is not covered in this guide. However, you can use the same --arch flag with podman to force the use of a specific architecture, and the GOARCH variable would also still apply for container applications written in Go.

If you already have a multi-arch operator and operands hosted in Quay or Docker Hub, please proceed with Scanning and Publishing.

Building with Legacy Operator SDK Releases (pre-1.0)

If you don't have an Operator SDK project to work from, you can clone this git repository and follow along: The operator project is located within the ubi-noop-go/ directory of the git repo.

For older releases of the Operator SDK, where image building is handled by the operator-sdk binary, the process is fairly simple. There are no specific requirements to be made to the Dockerfile source for the operator controller. All modifications are made as flags or variables when calling operator-sdk build.

Specifically, you must set the GOARCH variable and --arch flag to match the CPU architecture that you're targeting. According to the CPU architecture table, the correct architecture (arch) for IBM Z would be s390x.

For cross compilation and container builds to work, you must install the qemu-user-static package, which is not available in RHEL (Fedora was used here):

$ sudo dnf install -y qemu-user-static

Let's build an image locally for the s390x architecture. Keep in mind that we're using podman in docker emulation mode as the image build tool:

$ GOARCH=s390x operator-sdk build ubi-noop-go:v0.0.1-s390x --image-build-args="--arch=s390x"

Similarly, to build an operator image for IBM Power:

$ GOARCH=ppc64le operator-sdk build ubi-noop-go:v0.0.1-ppc64le --image-build-args="--arch=ppc64le"

OPTIONAL: If you do not yet have an Intel/AMD operator image (a requirement for multi-arch operator certification) and want to follow along, then build the amd64 image. Regardless, you must set the arch flag to amd64 or podman will default to using the most recent image/architecture (ppc64le) for future commands:

$ operator-sdk build ubi-noop-go:v0.0.1-amd64 --image-build-args="--arch=amd64"

With all of the images built, the next step is to create a manifest list and push it to an external registry.

Build and Push the Manifest List

In this section, we use podman exclusively for creating and pushing the manifest list. The syntax for docker is similar, with the exception of using a single argument when pushing the manifest to a registry.

Create a new manifest list, which will reference each of the underlying architecture-specific images:

$ podman manifest create ubi-noop-go:v0.0.1

You could also populate the index by listing each sub-architecture image as an additional argument when creating the manifest list. However, if anything happens (typo/mispelling), then you're left with a partially populated manifest list.

Add each of the architecture specific images to the manifest list, in no particular order. To make sure that podman pulls the images from local container storage, we use the containers-storage:localhost/ prefix. Let's start with the Intel/AMD image:

$ podman manifest add ubi-noop-go:v0.0.1 containers-storage:localhost/ubi-noop-go:v0.0.1-amd64

Next, add the Power image to the manifest list:

$ podman manifest add ubi-noop-go:v0.0.1 containers-storage:localhost/ubi-noop-go:v0.0.1-ppc64le

Add the Z image to complete the manifest list:

$ podman manifest add ubi-noop-go:v0.0.1 containers-storage:localhost/ubi-noop-go:v0.0.1-s390x

The last step is to push the new manifest list to an external registry for hosting. is suggested here, but you can use any registry which supports manifest lists, such as Docker Hub:

$ podman manifest push ubi-noop-go:v0.0.1

Once you have the manifest list hosted for your operator and its operands (which aren't covered here), you can proceed with Scanning and Publishing.

Last updated