Skip to content

Commit

Permalink
Enable multi-platform support for operator image builds
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
ashokpariya0 committed Nov 19, 2024
1 parent fca381a commit 4aa7e93
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 10 deletions.
3 changes: 0 additions & 3 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@
_kubevirtci

# No need for source code is already compiled
pkg
cmd
tools
vendor
docs

Expand Down
32 changes: 28 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ OPERATOR_IMAGE ?= cluster-network-addons-operator
REGISTRY_IMAGE ?= cluster-network-addons-registry
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)
TLS_SETTING := $(if $(filter $(OCI_BIN),podman),--tls-verify=false,)
PLATFORMS ?= linux/amd64
# Set the platforms for building a multi-platform supported image.
# Example:
# PLATFORMS ?= linux/amd64,linux/arm64,linux/s390x
# Alternatively, you can export the PLATFORMS variable like this:
# export PLATFORMS=linux/arm64,linux/s390x,linux/amd64
ARCH := $(shell uname -m | sed 's/x86_64/amd64/')
DOCKER_BUILDER ?= cnao-docker-builder
OPERATOR_IMAGE_TAGGED := $(IMAGE_REGISTRY)/$(OPERATOR_IMAGE):$(IMAGE_TAG)

TARGETS = \
gen-k8s \
Expand All @@ -26,7 +35,6 @@ TARGETS = \
export GOFLAGS=-mod=vendor
export GO111MODULE=on
GO_VERSION = $(shell hack/go-version.sh)

WHAT ?= ./pkg/... ./cmd/... ./tools/...

export E2E_TEST_TIMEOUT ?= 3h
Expand Down Expand Up @@ -112,16 +120,24 @@ manifest-templator: $(GO)

docker-build: docker-build-operator docker-build-registry

docker-build-operator: manager manifest-templator
$(OCI_BIN) build -f build/operator/Dockerfile -t $(IMAGE_REGISTRY)/$(OPERATOR_IMAGE):$(IMAGE_TAG) .
docker-build-operator:
ifeq ($(OCI_BIN),podman)
$(MAKE) build-multiarch-operator-podman
else ifeq ($(OCI_BIN),docker)
$(MAKE) build-multiarch-operator-docker
else
$(error Unsupported OCI_BIN value: $(OCI_BIN))
endif

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

docker-push: docker-push-operator docker-push-registry

docker-push-operator:
$(OCI_BIN) push ${TLS_SETTING} $(IMAGE_REGISTRY)/$(OPERATOR_IMAGE):$(IMAGE_TAG)
ifeq ($(OCI_BIN),podman)
podman manifest push ${TLS_SETTING} ${OPERATOR_IMAGE_TAGGED} ${OPERATOR_IMAGE_TAGGED}
endif

docker-push-registry:
$(OCI_BIN) push $(IMAGE_REGISTRY)/$(REGISTRY_IMAGE):$(IMAGE_TAG)
Expand Down Expand Up @@ -227,9 +243,17 @@ lint-monitoring:
clean:
rm -rf $(OUTPUT_DIR)

build-multiarch-operator-docker:
ARCH=$(ARCH) PLATFORMS=$(PLATFORMS) OPERATOR_IMAGE_TAGGED=$(OPERATOR_IMAGE_TAGGED) DOCKER_BUILDER=$(DOCKER_BUILDER) ./hack/build-operator-docker.sh

build-multiarch-operator-podman:
ARCH=$(ARCH) PLATFORMS=$(PLATFORMS) OPERATOR_IMAGE_TAGGED=$(OPERATOR_IMAGE_TAGGED) ./hack/build-operator-podman.sh

.PHONY: \
$(E2E_SUITES) \
all \
build-multiarch-operator-docker \
build-multiarch-operator-podman \
check \
cluster-clean \
cluster-down \
Expand Down
29 changes: 26 additions & 3 deletions build/operator/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,24 @@
FROM quay.io/centos/centos:stream9
ARG BUILD_ARCH=amd64
FROM --platform=linux/${BUILD_ARCH} quay.io/centos/centos:stream9 AS builder

RUN dnf install -y tar gzip jq && dnf clean all
RUN ARCH=$(uname -m | sed 's/x86_64/amd64/') && \
GO_VERSION=$(curl -L -s "https://go.dev/dl/?mode=json" | jq -r '.[0].version') && \
curl -L "https://go.dev/dl/${GO_VERSION}.linux-${ARCH}.tar.gz" -o go.tar.gz && \
tar -C /usr/local -xzf go.tar.gz && \
rm go.tar.gz
ENV PATH=$PATH:/usr/local/go/bin
WORKDIR /go/src/cluster-network-addons-operator
COPY . .

ARG TARGETOS
ARG TARGETARCH

RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o build/_output/bin/manager ./cmd/... && \
CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o build/_output/bin/manifest-templator ./tools/manifest-templator/...

FROM --platform=linux/${TARGETARCH} quay.io/centos/centos:stream9 AS final

ENV ENTRYPOINT=/entrypoint \
OPERATOR=/cluster-network-addons-operator \
MANIFEST_TEMPLATOR=/manifest-templator \
Expand All @@ -8,13 +28,16 @@ ENV ENTRYPOINT=/entrypoint \
RUN \
yum -y update && \
yum clean all

COPY build/operator/bin/user_setup /user_setup
COPY build/operator/bin/csv-generator /usr/bin/csv-generator
COPY templates/cluster-network-addons/VERSION/cluster-network-addons-operator.VERSION.clusterserviceversion.yaml.in cluster-network-addons-operator.VERSION.clusterserviceversion.yaml.in
RUN /user_setup
COPY data /data
COPY build/_output/bin/manager $OPERATOR
COPY build/_output/bin/manifest-templator $MANIFEST_TEMPLATOR

COPY --from=builder /go/src/cluster-network-addons-operator/build/_output/bin/manager $OPERATOR
COPY --from=builder /go/src/cluster-network-addons-operator/build/_output/bin/manifest-templator $MANIFEST_TEMPLATOR
COPY build/operator/bin/entrypoint $ENTRYPOINT

ENTRYPOINT $ENTRYPOINT
USER $USER_UID
20 changes: 20 additions & 0 deletions hack/build-operator-docker.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/bin/bash

if [ -z "$ARCH" ] || [ -z "$PLATFORMS" ] || [ -z "$OPERATOR_IMAGE_TAGGED" ]; then
echo "Error: ARCH, PLATFORMS and OPERATOR_IMAGE_TAGGED must be set."
exit 1
fi

# Split the comma-separated platforms into an array
IFS=',' read -r -a PLATFORM_LIST <<< "$PLATFORMS"

BUILD_ARGS="--build-arg BUILD_ARCH=$ARCH -f build/operator/Dockerfile -t $OPERATOR_IMAGE_TAGGED --push ."

if [ ${#PLATFORM_LIST[@]} -eq 1 ]; then
# Single platform build
docker build --platform "$PLATFORMS" $BUILD_ARGS
else
# Multi-platform build
./hack/init-buildx.sh "$DOCKER_BUILDER"
docker buildx build --platform "$PLATFORMS" $BUILD_ARGS
fi
27 changes: 27 additions & 0 deletions hack/build-operator-podman.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/bin/bash

if [ -z "$ARCH" ] || [ -z "$PLATFORMS" ] || [ -z "$OPERATOR_IMAGE_TAGGED" ]; then
echo "Error: ARCH, PLATFORMS, and OPERATOR_IMAGE_TAGGED must be set."
exit 1
fi

# Split the comma-separated platforms into an array
IFS=',' read -r -a PLATFORM_LIST <<< "$PLATFORMS"

# Remove any existing manifest and image
podman manifest rm "${OPERATOR_IMAGE_TAGGED}" || true
podman rmi "${OPERATOR_IMAGE_TAGGED}" || true

# Create a manifest list
podman manifest create "${OPERATOR_IMAGE_TAGGED}"

#for platform in $PLATFORM_LIST; do
for platform in "${PLATFORM_LIST[@]}"; do
podman build \
--no-cache \
--build-arg BUILD_ARCH="$ARCH" \
--platform "$platform" \
--manifest "${OPERATOR_IMAGE_TAGGED}" \
-f build/operator/Dockerfile \
.
done
62 changes: 62 additions & 0 deletions hack/init-buildx.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#!/bin/bash

check_buildx() {
export DOCKER_CLI_EXPERIMENTAL=enabled

# If there is no buildx let's install it
if ! docker buildx > /dev/null 2>&1; then
mkdir -p ~/.docker/cli-plugins
BUILDX_VERSION=$(curl -s https://api.github.com/repos/docker/buildx/releases/latest | jq -r .tag_name)
curl -L https://github.com/docker/buildx/releases/download/"${BUILDX_VERSION}"/buildx-"${BUILDX_VERSION}".linux-amd64 --output ~/.docker/cli-plugins/docker-buildx
chmod a+x ~/.docker/cli-plugins/docker-buildx
fi
}

create_or_use_buildx_builder() {
local builder_name=$1

if [ -z "$builder_name" ]; then
echo "Error: Builder name is required."
exit 1
fi

check_buildx

current_builder="$(docker buildx inspect "${builder_name}")"

# Check if the current builder has multi-architecture support and is not using the "docker" driver
if ! grep -q "^Driver: docker$" <<<"${current_builder}" && \
grep -q "linux/amd64" <<<"${current_builder}" && \
grep -q "linux/arm64" <<<"${current_builder}" && \
grep -q "linux/s390x" <<<"${current_builder}"; then
echo "The current builder already has multi-architecture support (amd64, arm64, s390x)."
echo "Skipping setup as the builder is already configured correctly."
exit 0
fi

# Check if the builder already exists by parsing the output of `docker buildx ls`
# We check if the builder_name appears in the list of active builders
existing_builder=$(docker buildx ls | grep -w "$builder_name" | awk '{print $1}')

if [ -n "$existing_builder" ]; then
echo "Builder '$builder_name' already exists."
echo "Using existing builder '$builder_name'."
docker buildx use "$builder_name"
else
# If the builder does not exist, create a new one
echo "Creating a new Docker Buildx builder: $builder_name"
docker buildx create --driver-opt network=host --use --name "$builder_name"

# Verify the new builder is set as active
echo "The new builder '$builder_name' has been created and set as active."
fi

}

if [ $# -eq 1 ]; then
create_or_use_buildx_builder "$1"
else
echo "Usage: $0 <builder_name>"
echo "Example: $0 mybuilder"
exit 1
fi

0 comments on commit 4aa7e93

Please sign in to comment.