Skip to content

Commit 2fdfd41

Browse files
committed
Enable multi-platform support for operator image builds
These changes enable building and pushing container images for multiple platforms (amd64, s390x, arm64) from a single Dockerfile. Enhanced multi-platform support in the build process by adding a PLATFORMS argument in the Makefile for amd64, s390x, and arm64 architectures. Updated the docker-build-operator target to support builds with docker buildx and podman, and added new targets for creating Docker Buildx builders and pushing multi-platform images. Signed-off-by: Ashok Pariya <[email protected]>
1 parent fca381a commit 2fdfd41

File tree

4 files changed

+151
-10
lines changed

4 files changed

+151
-10
lines changed

.dockerignore

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@
1212
_kubevirtci
1313

1414
# No need for source code is already compiled
15-
pkg
16-
cmd
17-
tools
1815
vendor
1916
docs
2017

Makefile

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,19 @@ OPERATOR_IMAGE ?= cluster-network-addons-operator
1313
REGISTRY_IMAGE ?= cluster-network-addons-registry
1414
export OCI_BIN ?= $(shell if podman ps >/dev/null 2>&1; then echo podman; elif docker ps >/dev/null 2>&1; then echo docker; fi)
1515
TLS_SETTING := $(if $(filter $(OCI_BIN),podman),--tls-verify=false,)
16+
PLATFORMS ?= linux/amd64
17+
# Set the platforms for building a multi-platform supported image.
18+
# Example:
19+
# PLATFORMS ?= linux/amd64,linux/arm64,linux/s390x
20+
# Alternatively, you can export the PLATFORMS variable like this:
21+
# export PLATFORMS=linux/arm64,linux/s390x,linux/amd64
22+
ARCH := $(shell uname -m | sed 's/x86_64/amd64/')
23+
DOCKER_BUILDER ?= docker-builder
24+
OPERATOR_IMAGE_TAGGED := $(IMAGE_REGISTRY)/$(OPERATOR_IMAGE):$(IMAGE_TAG)
25+
NULL :=
26+
SPACE := $(NULL) #
27+
COMMA := ,
28+
PLATFORM_LIST := $(subst $(COMMA),$(SPACE),$(strip $(PLATFORMS)))
1629

1730
TARGETS = \
1831
gen-k8s \
@@ -26,7 +39,6 @@ TARGETS = \
2639
export GOFLAGS=-mod=vendor
2740
export GO111MODULE=on
2841
GO_VERSION = $(shell hack/go-version.sh)
29-
3042
WHAT ?= ./pkg/... ./cmd/... ./tools/...
3143

3244
export E2E_TEST_TIMEOUT ?= 3h
@@ -112,16 +124,30 @@ manifest-templator: $(GO)
112124

113125
docker-build: docker-build-operator docker-build-registry
114126

115-
docker-build-operator: manager manifest-templator
116-
$(OCI_BIN) build -f build/operator/Dockerfile -t $(IMAGE_REGISTRY)/$(OPERATOR_IMAGE):$(IMAGE_TAG) .
127+
docker-build-operator:
128+
ifeq ($(OCI_BIN),podman)
129+
$(MAKE) build-multiarch-operator-podman
130+
else ifeq ($(OCI_BIN),docker)
131+
$(MAKE) build-multiarch-operator-docker
132+
else
133+
$(error Unsupported OCI_BIN value: $(OCI_BIN))
134+
endif
117135

118136
docker-build-registry:
119137
$(OCI_BIN) build -f build/registry/Dockerfile -t $(IMAGE_REGISTRY)/$(REGISTRY_IMAGE):$(IMAGE_TAG) .
120138

121139
docker-push: docker-push-operator docker-push-registry
122140

123141
docker-push-operator:
124-
$(OCI_BIN) push ${TLS_SETTING} $(IMAGE_REGISTRY)/$(OPERATOR_IMAGE):$(IMAGE_TAG)
142+
ifeq ($(OCI_BIN),podman)
143+
podman manifest push ${TLS_SETTING} ${OPERATOR_IMAGE_TAGGED}
144+
else ifeq ($(OCI_BIN),docker)
145+
ifeq ($(words $(PLATFORM_LIST)), 1)
146+
docker push ${TLS_SETTING} ${OPERATOR_IMAGE_TAGGED}
147+
endif
148+
else
149+
$(error Unsupported OCI_BIN value: $(OCI_BIN))
150+
endif
125151

126152
docker-push-registry:
127153
$(OCI_BIN) push $(IMAGE_REGISTRY)/$(REGISTRY_IMAGE):$(IMAGE_TAG)
@@ -227,9 +253,45 @@ lint-monitoring:
227253
clean:
228254
rm -rf $(OUTPUT_DIR)
229255

256+
build-multiarch-operator-docker:
257+
ifeq ($(words $(PLATFORM_LIST)), 1)
258+
docker build \
259+
--build-arg BUILD_ARCH=$(ARCH) \
260+
--platform $(PLATFORMS) \
261+
-f build/operator/Dockerfile \
262+
-t ${OPERATOR_IMAGE_TAGGED} .
263+
else
264+
./hack/build-multiarch-operator.sh ${DOCKER_BUILDER}
265+
docker buildx build \
266+
--build-arg BUILD_ARCH=$(ARCH) \
267+
--platform $(PLATFORMS) \
268+
-f build/operator/Dockerfile \
269+
-t ${OPERATOR_IMAGE_TAGGED} \
270+
--push .
271+
endif
272+
273+
build-multiarch-operator-podman:
274+
# Remove any existing manifest and image
275+
@podman manifest rm ${OPERATOR_IMAGE_TAGGED} || true
276+
@podman rmi ${OPERATOR_IMAGE_TAGGED} || true
277+
# Create a manifest list
278+
@podman manifest create ${OPERATOR_IMAGE_TAGGED}
279+
# Loop through each platform and build the image
280+
$(foreach platform,$(PLATFORM_LIST), \
281+
podman build \
282+
--no-cache \
283+
--build-arg BUILD_ARCH=$(ARCH) \
284+
--platform $(platform) \
285+
--manifest ${OPERATOR_IMAGE_TAGGED} \
286+
-f build/operator/Dockerfile \
287+
.; \
288+
)
289+
230290
.PHONY: \
231291
$(E2E_SUITES) \
232292
all \
293+
build-multiarch-operator-docker \
294+
build-multiarch-operator-podman \
233295
check \
234296
cluster-clean \
235297
cluster-down \

build/operator/Dockerfile

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,25 @@
1-
FROM quay.io/centos/centos:stream9
1+
ARG BUILD_ARCH=amd64
2+
FROM --platform=linux/${BUILD_ARCH} quay.io/centos/centos:stream9 AS builder
3+
4+
RUN dnf install -y tar gzip jq && dnf clean all
5+
RUN ARCH=$(uname -m | sed 's/x86_64/amd64/') && \
6+
GO_VERSION=$(curl -L -s "https://go.dev/dl/?mode=json" | jq -r '.[0].version') && \
7+
curl -L "https://go.dev/dl/${GO_VERSION}.linux-${ARCH}.tar.gz" -o go.tar.gz && \
8+
tar -C /usr/local -xzf go.tar.gz && \
9+
rm go.tar.gz
10+
ENV PATH=$PATH:/usr/local/go/bin
11+
WORKDIR /go/src/cluster-network-addons-operator
12+
COPY . .
13+
14+
ARG TARGETOS
15+
ARG TARGETARCH
16+
ARG TARGETPLATFORM
17+
18+
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o build/_output/bin/manager ./cmd/... && \
19+
CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o build/_output/bin/manifest-templator ./tools/manifest-templator/...
20+
21+
FROM --platform=linux/${TARGETARCH} quay.io/centos/centos:stream9 AS final
22+
223
ENV ENTRYPOINT=/entrypoint \
324
OPERATOR=/cluster-network-addons-operator \
425
MANIFEST_TEMPLATOR=/manifest-templator \
@@ -8,13 +29,16 @@ ENV ENTRYPOINT=/entrypoint \
829
RUN \
930
yum -y update && \
1031
yum clean all
32+
1133
COPY build/operator/bin/user_setup /user_setup
1234
COPY build/operator/bin/csv-generator /usr/bin/csv-generator
1335
COPY templates/cluster-network-addons/VERSION/cluster-network-addons-operator.VERSION.clusterserviceversion.yaml.in cluster-network-addons-operator.VERSION.clusterserviceversion.yaml.in
1436
RUN /user_setup
1537
COPY data /data
16-
COPY build/_output/bin/manager $OPERATOR
17-
COPY build/_output/bin/manifest-templator $MANIFEST_TEMPLATOR
38+
39+
COPY --from=builder /go/src/cluster-network-addons-operator/build/_output/bin/manager $OPERATOR
40+
COPY --from=builder /go/src/cluster-network-addons-operator/build/_output/bin/manifest-templator $MANIFEST_TEMPLATOR
1841
COPY build/operator/bin/entrypoint $ENTRYPOINT
42+
1943
ENTRYPOINT $ENTRYPOINT
2044
USER $USER_UID

hack/build-multiarch-operator.sh

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#!/bin/bash
2+
3+
check_buildx() {
4+
if ! docker buildx version > /dev/null 2>&1; then
5+
echo "Docker Buildx is not installed or not available."
6+
echo "Please ensure Docker 19.03+ and Buildx is available."
7+
exit 1
8+
fi
9+
}
10+
11+
create_or_use_buildx_builder() {
12+
local builder_name=$1
13+
14+
if [ -z "$builder_name" ]; then
15+
echo "Error: Builder name is required."
16+
exit 1
17+
fi
18+
19+
check_buildx
20+
21+
current_builder="$(docker buildx inspect ${builder_name})"
22+
23+
# Check if the current builder has multi-architecture support and is not using the "docker" driver
24+
if ! grep -q "^Driver: docker$" <<<"${current_builder}" && \
25+
grep -q "linux/amd64" <<<"${current_builder}" && \
26+
grep -q "linux/arm64" <<<"${current_builder}" && \
27+
grep -q "linux/s390x" <<<"${current_builder}"; then
28+
echo "The current builder already has multi-architecture support (amd64, arm64, s390x)."
29+
echo "Skipping setup as the builder is already configured correctly."
30+
exit 0
31+
fi
32+
33+
# Check if the builder already exists by parsing the output of `docker buildx ls`
34+
# We check if the builder_name appears in the list of active builders
35+
existing_builder=$(docker buildx ls | grep -w "$builder_name" | awk '{print $1}')
36+
37+
if [ -n "$existing_builder" ]; then
38+
echo "Builder '$builder_name' already exists."
39+
echo "Using existing builder '$builder_name'."
40+
docker buildx use "$builder_name"
41+
else
42+
# If the builder does not exist, create a new one
43+
echo "Creating a new Docker Buildx builder: $builder_name"
44+
docker buildx create --driver-opt network=host --use --name "$builder_name"
45+
46+
# Verify the new builder is set as active
47+
echo "The new builder '$builder_name' has been created and set as active."
48+
fi
49+
50+
}
51+
52+
if [ $# -eq 1 ]; then
53+
create_or_use_buildx_builder "$1"
54+
else
55+
echo "Usage: $0 <builder_name>"
56+
echo "Example: $0 mybuilder"
57+
exit 1
58+
fi

0 commit comments

Comments
 (0)