diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 81250357..02a1221f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,6 +37,9 @@ jobs: username: ${{ secrets.DOCKER_USER }} password: ${{ secrets.DOCKER_PASSWORD }} + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Set up GO 1.25.8 uses: actions/setup-go@v5 with: @@ -71,6 +74,7 @@ jobs: VERSION: ${{ github.event.release.tag_name }} IMAGE_TAG_BASE: streamnative/function-mesh CATALOG_BRANCH_TAG: latest + PLATFORMS: linux/amd64,linux/arm64 run: | # convert vx.y.z to x.y.z because a valid semver is needed in creating the bundle VERSION=$(echo $VERSION|cut -c 2-) diff --git a/Dockerfile b/Dockerfile index ee61e169..f37d5427 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,8 @@ # Build the manager binary -FROM golang:1.25.8-trixie AS builder +FROM --platform=$BUILDPLATFORM golang:1.25.8-trixie AS builder + +ARG TARGETOS +ARG TARGETARCH WORKDIR /workspace/api COPY api/ . @@ -20,7 +23,7 @@ COPY controllers/ controllers/ COPY utils/ utils/ # Build -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o manager main.go +RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} GO111MODULE=on go build -a -o manager main.go # Use distroless as minimal base image to package the manager binary # Refer to https://github.com/GoogleContainerTools/distroless for more details diff --git a/Makefile b/Makefile index ce968a20..a56e6915 100644 --- a/Makefile +++ b/Makefile @@ -22,6 +22,12 @@ GOARCH := $(if $(GOARCH),$(GOARCH),amd64) GOENV := CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) GO := $(GOENV) go GO_BUILD := $(GO) build -trimpath +PLATFORMS ?= linux/amd64 +comma := , +PLATFORM_LIST := $(subst $(comma), ,$(PLATFORMS)) +PRIMARY_PLATFORM := $(word 1,$(PLATFORM_LIST)) +MULTI_PLATFORM_BUILD := $(if $(filter 1,$(words $(PLATFORM_LIST))),false,true) +IMAGE_BUILD_PUSH ?= $(MULTI_PLATFORM_BUILD) GO_MAJOR_VERSION := $(shell $(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f1) GO_MINOR_VERSION := $(shell $(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f2) @@ -133,15 +139,27 @@ generate: controller-gen # Build the docker image docker-build: test - docker build --platform linux/amd64 . -t ${IMG} +ifeq ($(MULTI_PLATFORM_BUILD),true) + docker buildx build --platform $(PLATFORMS) --push -t ${IMG} . +else + docker build --platform $(PRIMARY_PLATFORM) -t ${IMG} . +endif # Build image for red hat certification docker-build-redhat: - docker build --platform linux/amd64 -f redhat.Dockerfile . -t ${IMG} --build-arg VERSION=${VERSION} --no-cache +ifeq ($(MULTI_PLATFORM_BUILD),true) + docker buildx build --platform $(PLATFORMS) --push -f redhat.Dockerfile -t ${IMG} --build-arg VERSION=${VERSION} --no-cache . +else + docker build --platform $(PRIMARY_PLATFORM) -f redhat.Dockerfile -t ${IMG} --build-arg VERSION=${VERSION} --no-cache . +endif # Push the docker image image-push: +ifeq ($(IMAGE_BUILD_PUSH),true) + @echo "image already pushed during multi-platform build: ${IMG}" +else docker push ${IMG} +endif # find or download controller-gen # download controller-gen if necessary @@ -171,12 +189,12 @@ bundle: yq kustomize manifests # Build the bundle image. .PHONY: bundle-build bundle-build: - docker build --platform linux/amd64 -f bundle.Dockerfile -t $(BUNDLE_IMG) . + docker build --platform $(PRIMARY_PLATFORM) -f bundle.Dockerfile -t $(BUNDLE_IMG) . .PHONY: bundle-push bundle-push: ## Push the bundle image. echo $(BUNDLE_IMG) - $(MAKE) image-push IMG=$(BUNDLE_IMG) + $(MAKE) image-push IMG=$(BUNDLE_IMG) IMAGE_BUILD_PUSH=false crd: manifests $(KUSTOMIZE) build config/crd > manifests/crd.yaml @@ -186,13 +204,21 @@ rbac: manifests release: manifests kustomize crd rbac manager operator-docker-image helm-crds -operator-docker-image: manager test - docker build --platform linux/amd64 -f operator.Dockerfile -t $(OPERATOR_IMG) . +operator-docker-image: test +ifeq ($(MULTI_PLATFORM_BUILD),true) + docker buildx build --platform $(PLATFORMS) --push -f operator.Dockerfile -t $(OPERATOR_IMG) -t $(OPERATOR_IMG_LATEST) . +else + docker build --platform $(PRIMARY_PLATFORM) -f operator.Dockerfile -t $(OPERATOR_IMG) . docker tag $(OPERATOR_IMG) $(OPERATOR_IMG_LATEST) +endif docker-push: +ifeq ($(MULTI_PLATFORM_BUILD),true) + @echo "operator images already pushed during multi-platform build: $(OPERATOR_IMG), $(OPERATOR_IMG_LATEST)" +else docker push $(OPERATOR_IMG) docker push $(OPERATOR_IMG_LATEST) +endif .PHONY: opm OPM = ./bin/opm @@ -240,9 +266,9 @@ endif # Push the catalog image. .PHONY: catalog-push catalog-push: ## Push a catalog image. - $(MAKE) image-push IMG=$(CATALOG_IMG) + $(MAKE) image-push IMG=$(CATALOG_IMG) IMAGE_BUILD_PUSH=false ifneq ($(origin CATALOG_BRANCH_TAG), undefined) - $(MAKE) image-push IMG=$(CATALOG_BRANCH_IMG) + $(MAKE) image-push IMG=$(CATALOG_BRANCH_IMG) IMAGE_BUILD_PUSH=false endif version: @@ -256,7 +282,11 @@ function-mesh-docker-image-name: # Build the docker image without tests docker-build-skip-test: - docker build --platform linux/amd64 . -t ${IMG} +ifeq ($(MULTI_PLATFORM_BUILD),true) + docker buildx build --platform $(PLATFORMS) --push -t ${IMG} . +else + docker build --platform $(PRIMARY_PLATFORM) -t ${IMG} . +endif e2e: skywalking-e2e yq $(E2E) run -c .ci/tests/integration/e2e.yaml @@ -297,17 +327,21 @@ redhat-certificated-bundle: yq kustomize manifests # Build the bundle image. .PHONY: redhat-certificated-bundle-build redhat-certificated-bundle-build: - docker build --platform linux/amd64 -f bundle.Dockerfile -t $(BUNDLE_IMG) . + docker build --platform $(PRIMARY_PLATFORM) -f bundle.Dockerfile -t $(BUNDLE_IMG) . .PHONY: redhat-certificated-bundle-push redhat-certificated-bundle-push: ## Push the bundle image. echo $(BUNDLE_IMG) - $(MAKE) image-push IMG=$(BUNDLE_IMG) + $(MAKE) image-push IMG=$(BUNDLE_IMG) IMAGE_BUILD_PUSH=false # Build the bundle image. .PHONY: redhat-certificated-image-build redhat-certificated-image-build: - docker build --platform linux/amd64 -f redhat.Dockerfile . -t ${OPERATOR_IMG} --build-arg VERSION=${VERSION} --no-cache +ifeq ($(MULTI_PLATFORM_BUILD),true) + docker buildx build --platform $(PLATFORMS) --push -f redhat.Dockerfile -t ${OPERATOR_IMG} --build-arg VERSION=${VERSION} --no-cache . +else + docker build --platform $(PRIMARY_PLATFORM) -f redhat.Dockerfile -t ${OPERATOR_IMG} --build-arg VERSION=${VERSION} --no-cache . +endif .PHONY: redhat-certificated-image-push redhat-certificated-image-push: ## Push the bundle image. diff --git a/README.md b/README.md index dc11a37e..d7242d9f 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,18 @@ operator-sdk create api --group compute --version v1alpha1 --kind Function --res operator-sdk create webhook --group compute.functionmesh.io --version v1alpha1 --kind Function --defaulting --programmatic-validation ``` +### Multi-platform images + +The image build targets accept `PLATFORMS` as a comma-separated Docker platform list. + +```bash +make docker-build PLATFORMS=linux/amd64,linux/arm64 +make operator-docker-image PLATFORMS=linux/amd64,linux/arm64 +PLATFORMS=linux/amd64,linux/arm64 PUSH=true images/build.sh +``` + +Single-platform builds still default to `linux/amd64`. Multi-platform builds use `docker buildx` and push a manifest list directly, so they require an authenticated registry session. + ## Deployment 1. make sure connected to a kubernetes cluster(gke, mini-kube etc.) diff --git a/docs/release_proocess.md b/docs/release_proocess.md index e3bfe093..a3cd545e 100644 --- a/docs/release_proocess.md +++ b/docs/release_proocess.md @@ -30,3 +30,5 @@ git push origin vX.Y.Z 3. Click the release button Click the release button and draft a new release. When publish the release, the Action CI will automatically trigger the release process, build the corresponding image, and push it to docker_hub. + +The release workflow publishes multi-platform Docker images for `linux/amd64` and `linux/arm64`. diff --git a/images/build.sh b/images/build.sh index 1a5dc650..bd08d79d 100755 --- a/images/build.sh +++ b/images/build.sh @@ -17,7 +17,7 @@ # specific language governing permissions and limitations # under the License. # -set -e +set -euo pipefail PULSAR_IMAGE=${PULSAR_IMAGE:-"streamnative/sn-platform"} PULSAR_IMAGE_TAG=${PULSAR_IMAGE_TAG:-"2.7.1"} @@ -34,30 +34,112 @@ PULSARCTL_PYTHON_RUNNER="pulsar-functions-pulsarctl-python-runner" RUNNER_TAG=${RUNNER_TAG:-$PULSAR_IMAGE_TAG} KIND_PUSH=${KIND_PUSH:-false} CI_TEST=${CI_TEST:-false} +PLATFORMS=${PLATFORMS:-"linux/amd64"} +PRIMARY_PLATFORM=${PLATFORMS%%,*} +RUNNER_BASE_IMAGE="${DOCKER_REPO}/${RUNNER_BASE}:${RUNNER_TAG}" +PULSARCTL_RUNNER_BASE_IMAGE="${DOCKER_REPO}/${PULSARCTL_RUNNER_BASE}:${RUNNER_TAG}" +JAVA_RUNNER_IMAGE="${DOCKER_REPO}/${JAVA_RUNNER}:${RUNNER_TAG}" +PULSARCTL_JAVA_RUNNER_IMAGE="${DOCKER_REPO}/${PULSARCTL_JAVA_RUNNER}:${RUNNER_TAG}" +GO_RUNNER_IMAGE="${DOCKER_REPO}/${GO_RUNNER}:${RUNNER_TAG}" +PULSARCTL_GO_RUNNER_IMAGE="${DOCKER_REPO}/${PULSARCTL_GO_RUNNER}:${RUNNER_TAG}" +PYTHON_RUNNER_IMAGE="${DOCKER_REPO}/${PYTHON_RUNNER}:${RUNNER_TAG}" +PULSARCTL_PYTHON_RUNNER_IMAGE="${DOCKER_REPO}/${PULSARCTL_PYTHON_RUNNER}:${RUNNER_TAG}" + +MULTI_PLATFORM=false +if [[ "${PLATFORMS}" == *,* ]]; then + MULTI_PLATFORM=true +fi + +PUSH_DEFAULT=false +if [[ "${DOCKER_REPO}" == "localhost:5000" || "${MULTI_PLATFORM}" == "true" ]]; then + PUSH_DEFAULT=true +fi +PUSH=${PUSH:-$PUSH_DEFAULT} + +if [[ "${MULTI_PLATFORM}" == "true" && "${PUSH}" != "true" ]]; then + echo "multi-platform builds require PUSH=true so dependent images can resolve their base images" >&2 + exit 1 +fi + +if [[ "${MULTI_PLATFORM}" == "true" && "${KIND_PUSH}" == "true" ]]; then + echo "KIND_PUSH=true is only supported for single-platform builds" >&2 + exit 1 +fi + +build_image() { + local image=$1 + local context=$2 + shift 2 + + if [[ "${MULTI_PLATFORM}" == "true" ]]; then + docker buildx build --platform "${PLATFORMS}" --push -t "${image}" "$@" "${context}" + else + docker build --platform "${PRIMARY_PLATFORM}" -t "${image}" "$@" "${context}" + fi +} + +tag_local_aliases() { + local source_image=$1 + local local_name=$2 + + if [[ "${MULTI_PLATFORM}" != "true" ]]; then + docker tag "${source_image}" "${local_name}:latest" + fi +} echo "build runner base" -docker build --platform linux/amd64 -t ${RUNNER_BASE} images/pulsar-functions-base-runner --build-arg PULSAR_IMAGE="$PULSAR_IMAGE" --build-arg PULSAR_IMAGE_TAG="$PULSAR_IMAGE_TAG" --progress=plain -docker build --platform linux/amd64 -t ${PULSARCTL_RUNNER_BASE} images/pulsar-functions-base-runner -f images/pulsar-functions-base-runner/pulsarctl.Dockerfile --build-arg PULSAR_IMAGE="$PULSAR_IMAGE" --build-arg PULSAR_IMAGE_TAG="$PULSAR_IMAGE_TAG" --progress=plain -docker tag ${RUNNER_BASE} "${DOCKER_REPO}"/${RUNNER_BASE}:"${RUNNER_TAG}" -docker tag ${PULSARCTL_RUNNER_BASE} "${DOCKER_REPO}"/${PULSARCTL_RUNNER_BASE}:"${RUNNER_TAG}" +build_image "${RUNNER_BASE_IMAGE}" images/pulsar-functions-base-runner \ + --build-arg PULSAR_IMAGE="${PULSAR_IMAGE}" \ + --build-arg PULSAR_IMAGE_TAG="${PULSAR_IMAGE_TAG}" \ + --progress=plain +build_image "${PULSARCTL_RUNNER_BASE_IMAGE}" images/pulsar-functions-base-runner \ + -f images/pulsar-functions-base-runner/pulsarctl.Dockerfile \ + --build-arg PULSAR_IMAGE="${PULSAR_IMAGE}" \ + --build-arg PULSAR_IMAGE_TAG="${PULSAR_IMAGE_TAG}" \ + --progress=plain +tag_local_aliases "${RUNNER_BASE_IMAGE}" "${RUNNER_BASE}" +tag_local_aliases "${PULSARCTL_RUNNER_BASE_IMAGE}" "${PULSARCTL_RUNNER_BASE}" echo "build java runner" -docker build --platform linux/amd64 -t ${JAVA_RUNNER} images/pulsar-functions-java-runner --build-arg PULSAR_IMAGE="$PULSAR_IMAGE" --build-arg PULSAR_IMAGE_TAG="$PULSAR_IMAGE_TAG" --progress=plain -docker build --platform linux/amd64 -t ${PULSARCTL_JAVA_RUNNER} images/pulsar-functions-java-runner -f images/pulsar-functions-java-runner/pulsarctl.Dockerfile --build-arg PULSAR_IMAGE="$PULSAR_IMAGE" --build-arg PULSAR_IMAGE_TAG="$PULSAR_IMAGE_TAG" --progress=plain -docker tag ${JAVA_RUNNER} "${DOCKER_REPO}"/${JAVA_RUNNER}:"${RUNNER_TAG}" -docker tag ${PULSARCTL_JAVA_RUNNER} "${DOCKER_REPO}"/${PULSARCTL_JAVA_RUNNER}:"${RUNNER_TAG}" +build_image "${JAVA_RUNNER_IMAGE}" images/pulsar-functions-java-runner \ + --build-arg BASE_IMAGE="${RUNNER_BASE_IMAGE}" \ + --build-arg PULSAR_IMAGE="${PULSAR_IMAGE}" \ + --build-arg PULSAR_IMAGE_TAG="${PULSAR_IMAGE_TAG}" \ + --progress=plain +build_image "${PULSARCTL_JAVA_RUNNER_IMAGE}" images/pulsar-functions-java-runner \ + -f images/pulsar-functions-java-runner/pulsarctl.Dockerfile \ + --build-arg BASE_IMAGE="${PULSARCTL_RUNNER_BASE_IMAGE}" \ + --build-arg PULSAR_IMAGE="${PULSAR_IMAGE}" \ + --build-arg PULSAR_IMAGE_TAG="${PULSAR_IMAGE_TAG}" \ + --progress=plain +tag_local_aliases "${JAVA_RUNNER_IMAGE}" "${JAVA_RUNNER}" +tag_local_aliases "${PULSARCTL_JAVA_RUNNER_IMAGE}" "${PULSARCTL_JAVA_RUNNER}" echo "build python runner" -docker build --platform linux/amd64 -t ${PYTHON_RUNNER} images/pulsar-functions-python-runner --build-arg PULSAR_IMAGE="$PULSAR_IMAGE" --build-arg PULSAR_IMAGE_TAG="$PULSAR_IMAGE_TAG" --progress=plain -docker build --platform linux/amd64 -t ${PULSARCTL_PYTHON_RUNNER} images/pulsar-functions-python-runner -f images/pulsar-functions-python-runner/pulsarctl.Dockerfile --build-arg PULSAR_IMAGE="$PULSAR_IMAGE" --build-arg PULSAR_IMAGE_TAG="$PULSAR_IMAGE_TAG" --build-arg PYTHON_VERSION="$PYTHON_VERSION" --progress=plain -docker tag ${PYTHON_RUNNER} "${DOCKER_REPO}"/${PYTHON_RUNNER}:"${RUNNER_TAG}" -docker tag ${PULSARCTL_PYTHON_RUNNER} "${DOCKER_REPO}"/${PULSARCTL_PYTHON_RUNNER}:"${RUNNER_TAG}" +build_image "${PYTHON_RUNNER_IMAGE}" images/pulsar-functions-python-runner \ + --build-arg BASE_IMAGE="${RUNNER_BASE_IMAGE}" \ + --build-arg PULSAR_IMAGE="${PULSAR_IMAGE}" \ + --build-arg PULSAR_IMAGE_TAG="${PULSAR_IMAGE_TAG}" \ + --progress=plain +build_image "${PULSARCTL_PYTHON_RUNNER_IMAGE}" images/pulsar-functions-python-runner \ + -f images/pulsar-functions-python-runner/pulsarctl.Dockerfile \ + --build-arg PULSAR_IMAGE="${PULSAR_IMAGE}" \ + --build-arg PULSAR_IMAGE_TAG="${PULSAR_IMAGE_TAG}" \ + --build-arg PYTHON_VERSION="${PYTHON_VERSION}" \ + --progress=plain +tag_local_aliases "${PYTHON_RUNNER_IMAGE}" "${PYTHON_RUNNER}" +tag_local_aliases "${PULSARCTL_PYTHON_RUNNER_IMAGE}" "${PULSARCTL_PYTHON_RUNNER}" echo "build go runner" -docker build --platform linux/amd64 -t ${GO_RUNNER} images/pulsar-functions-go-runner --progress=plain # go runner is almost the same as runner base, so we no need to given build args for go runner -docker build --platform linux/amd64 -t ${PULSARCTL_GO_RUNNER} images/pulsar-functions-go-runner -f images/pulsar-functions-go-runner/pulsarctl.Dockerfile --progress=plain # go runner is almost the same as runner base, so we no need to given build args for go runner -docker tag ${GO_RUNNER} "${DOCKER_REPO}"/${GO_RUNNER}:"${RUNNER_TAG}" -docker tag ${PULSARCTL_GO_RUNNER} "${DOCKER_REPO}"/${PULSARCTL_GO_RUNNER}:"${RUNNER_TAG}" +build_image "${GO_RUNNER_IMAGE}" images/pulsar-functions-go-runner \ + --build-arg BASE_IMAGE="${RUNNER_BASE_IMAGE}" \ + --progress=plain +build_image "${PULSARCTL_GO_RUNNER_IMAGE}" images/pulsar-functions-go-runner \ + -f images/pulsar-functions-go-runner/pulsarctl.Dockerfile \ + --build-arg BASE_IMAGE="${PULSARCTL_RUNNER_BASE_IMAGE}" \ + --progress=plain +tag_local_aliases "${GO_RUNNER_IMAGE}" "${GO_RUNNER}" +tag_local_aliases "${PULSARCTL_GO_RUNNER_IMAGE}" "${PULSARCTL_GO_RUNNER}" if [ "$KIND_PUSH" = true ] ; then echo "push images to kind" @@ -65,22 +147,24 @@ if [ "$KIND_PUSH" = true ] ; then echo $clusters for cluster in $clusters do - kind load docker-image "${DOCKER_REPO}"/${JAVA_RUNNER}:"${RUNNER_TAG}" --name $cluster - kind load docker-image "${DOCKER_REPO}"/${PULSARCTL_JAVA_RUNNER}:"${RUNNER_TAG}" --name $cluster - kind load docker-image "${DOCKER_REPO}"/${PYTHON_RUNNER}:"${RUNNER_TAG}" --name $cluster - kind load docker-image "${DOCKER_REPO}"/${PULSARCTL_PYTHON_RUNNER}:"${RUNNER_TAG}" --name $cluster - kind load docker-image "${DOCKER_REPO}"/${GO_RUNNER}:"${RUNNER_TAG}" --name $cluster - kind load docker-image "${DOCKER_REPO}"/${PULSARCTL_GO_RUNNER}:"${RUNNER_TAG}" --name $cluster + kind load docker-image "${JAVA_RUNNER_IMAGE}" --name $cluster + kind load docker-image "${PULSARCTL_JAVA_RUNNER_IMAGE}" --name $cluster + kind load docker-image "${PYTHON_RUNNER_IMAGE}" --name $cluster + kind load docker-image "${PULSARCTL_PYTHON_RUNNER_IMAGE}" --name $cluster + kind load docker-image "${GO_RUNNER_IMAGE}" --name $cluster + kind load docker-image "${PULSARCTL_GO_RUNNER_IMAGE}" --name $cluster done fi -if [ "$DOCKER_REPO" = "localhost:5000" ]; then - docker push "${DOCKER_REPO}"/${JAVA_RUNNER}:"${RUNNER_TAG}" - docker push "${DOCKER_REPO}"/${PULSARCTL_JAVA_RUNNER}:"${RUNNER_TAG}" - docker push "${DOCKER_REPO}"/${PYTHON_RUNNER}:"${RUNNER_TAG}" - docker push "${DOCKER_REPO}"/${PULSARCTL_PYTHON_RUNNER}:"${RUNNER_TAG}" - docker push "${DOCKER_REPO}"/${GO_RUNNER}:"${RUNNER_TAG}" - docker push "${DOCKER_REPO}"/${PULSARCTL_GO_RUNNER}:"${RUNNER_TAG}" +if [[ "${PUSH}" == "true" && "${MULTI_PLATFORM}" != "true" ]]; then + docker push "${RUNNER_BASE_IMAGE}" + docker push "${PULSARCTL_RUNNER_BASE_IMAGE}" + docker push "${JAVA_RUNNER_IMAGE}" + docker push "${PULSARCTL_JAVA_RUNNER_IMAGE}" + docker push "${PYTHON_RUNNER_IMAGE}" + docker push "${PULSARCTL_PYTHON_RUNNER_IMAGE}" + docker push "${GO_RUNNER_IMAGE}" + docker push "${PULSARCTL_GO_RUNNER_IMAGE}" fi # #if [ "$CI_TEST" = true ] ; then @@ -93,4 +177,4 @@ fi # kind load docker-image "${DOCKER_REPO}"/${PYTHON_RUNNER}:"${RUNNER_TAG}" --name $cluster # kind load docker-image "${DOCKER_REPO}"/${GO_RUNNER}:"${RUNNER_TAG}" --name $cluster # done -#fi \ No newline at end of file +#fi diff --git a/images/pulsar-functions-base-runner/pulsarctl.Dockerfile b/images/pulsar-functions-base-runner/pulsarctl.Dockerfile index 08b8911e..2aca4223 100644 --- a/images/pulsar-functions-base-runner/pulsarctl.Dockerfile +++ b/images/pulsar-functions-base-runner/pulsarctl.Dockerfile @@ -1,5 +1,7 @@ FROM alpine:3.21 as functions-runner +ARG TARGETARCH + ENV GID=10001 ENV UID=10000 ENV USER=pulsar @@ -17,10 +19,15 @@ RUN mkdir -p /pulsar/bin/ \ && chown -R $UID:$GID /pulsar \ && chmod -R g=u /pulsar \ && apk update && apk add --no-cache wget bash \ - && wget https://github.com/streamnative/pulsarctl/releases/latest/download/pulsarctl-amd64-linux.tar.gz -P /pulsar/bin/ \ - && tar -xzf /pulsar/bin/pulsarctl-amd64-linux.tar.gz -C /pulsar/bin/ \ - && rm -rf /pulsar/bin/pulsarctl-amd64-linux.tar.gz \ - && chmod +x /pulsar/bin/pulsarctl-amd64-linux/pulsarctl \ - && ln -s /pulsar/bin/pulsarctl-amd64-linux/pulsarctl /usr/local/bin/pulsarctl + && case "${TARGETARCH}" in \ + amd64|arm64) PULSARCTL_ARCH="${TARGETARCH}" ;; \ + *) echo "unsupported TARGETARCH: ${TARGETARCH}" && exit 1 ;; \ + esac \ + && PULSARCTL_DIST="pulsarctl-${PULSARCTL_ARCH}-linux.tar.gz" \ + && wget "https://github.com/streamnative/pulsarctl/releases/latest/download/${PULSARCTL_DIST}" -P /pulsar/bin/ \ + && tar -xzf "/pulsar/bin/${PULSARCTL_DIST}" -C /pulsar/bin/ \ + && rm -rf "/pulsar/bin/${PULSARCTL_DIST}" \ + && chmod +x "/pulsar/bin/pulsarctl-${PULSARCTL_ARCH}-linux/pulsarctl" \ + && ln -s "/pulsar/bin/pulsarctl-${PULSARCTL_ARCH}-linux/pulsarctl" /usr/local/bin/pulsarctl WORKDIR /pulsar diff --git a/images/pulsar-functions-go-runner/pulsarctl.Dockerfile b/images/pulsar-functions-go-runner/pulsarctl.Dockerfile index c48c5248..2488f3f1 100644 --- a/images/pulsar-functions-go-runner/pulsarctl.Dockerfile +++ b/images/pulsar-functions-go-runner/pulsarctl.Dockerfile @@ -1,4 +1,6 @@ -FROM pulsar-functions-pulsarctl-runner-base:latest +ARG BASE_IMAGE=pulsar-functions-pulsarctl-runner-base:latest + +FROM ${BASE_IMAGE} WORKDIR /pulsar diff --git a/images/pulsar-functions-java-runner/pulsarctl.Dockerfile b/images/pulsar-functions-java-runner/pulsarctl.Dockerfile index 748c808d..1d526eec 100644 --- a/images/pulsar-functions-java-runner/pulsarctl.Dockerfile +++ b/images/pulsar-functions-java-runner/pulsarctl.Dockerfile @@ -1,9 +1,10 @@ +ARG BASE_IMAGE=pulsar-functions-pulsarctl-runner-base:latest ARG PULSAR_IMAGE ARG PULSAR_IMAGE_TAG FROM ${PULSAR_IMAGE}:${PULSAR_IMAGE_TAG} as pulsar FROM apachepulsar/pulsar-io-kinesis-sink-kinesis_producer:0.15.12 as pulsar-io-kinesis-sink-kinesis_producer FROM apachepulsar/pulsar-io-kinesis-sink-kinesis_producer:1.0.4 as pulsar-io-kinesis-sink-kinesis_producer-1.0 -FROM pulsar-functions-pulsarctl-runner-base:latest +FROM ${BASE_IMAGE} ARG PULSAR_IMAGE_TAG ENV VERSION_TAG=${PULSAR_IMAGE_TAG} diff --git a/images/pulsar-functions-python-runner/pulsarctl.Dockerfile b/images/pulsar-functions-python-runner/pulsarctl.Dockerfile index cb6a1a2c..990f82b5 100644 --- a/images/pulsar-functions-python-runner/pulsarctl.Dockerfile +++ b/images/pulsar-functions-python-runner/pulsarctl.Dockerfile @@ -5,6 +5,8 @@ FROM ${PULSAR_IMAGE}:${PULSAR_IMAGE_TAG} as pulsar FROM python:${PYTHON_VERSION}-alpine +ARG TARGETARCH + ENV GID=10001 ENV UID=10000 ENV USER=pulsar @@ -22,11 +24,16 @@ RUN mkdir -p /pulsar/bin/ \ && chown -R $UID:$GID /pulsar \ && chmod -R g=u /pulsar \ && apk update && apk add --no-cache wget bash \ - && wget https://github.com/streamnative/pulsarctl/releases/latest/download/pulsarctl-amd64-linux.tar.gz -P /pulsar/bin/ \ - && tar -xzf /pulsar/bin/pulsarctl-amd64-linux.tar.gz -C /pulsar/bin/ \ - && rm -rf /pulsar/bin/pulsarctl-amd64-linux.tar.gz \ - && chmod +x /pulsar/bin/pulsarctl-amd64-linux/pulsarctl \ - && ln -s /pulsar/bin/pulsarctl-amd64-linux/pulsarctl /usr/local/bin/pulsarctl + && case "${TARGETARCH}" in \ + amd64|arm64) PULSARCTL_ARCH="${TARGETARCH}" ;; \ + *) echo "unsupported TARGETARCH: ${TARGETARCH}" && exit 1 ;; \ + esac \ + && PULSARCTL_DIST="pulsarctl-${PULSARCTL_ARCH}-linux.tar.gz" \ + && wget "https://github.com/streamnative/pulsarctl/releases/latest/download/${PULSARCTL_DIST}" -P /pulsar/bin/ \ + && tar -xzf "/pulsar/bin/${PULSARCTL_DIST}" -C /pulsar/bin/ \ + && rm -rf "/pulsar/bin/${PULSARCTL_DIST}" \ + && chmod +x "/pulsar/bin/pulsarctl-${PULSARCTL_ARCH}-linux/pulsarctl" \ + && ln -s "/pulsar/bin/pulsarctl-${PULSARCTL_ARCH}-linux/pulsarctl" /usr/local/bin/pulsarctl WORKDIR /pulsar diff --git a/images/samples/build.sh b/images/samples/build.sh index 9e753b3d..0841fb7e 100755 --- a/images/samples/build.sh +++ b/images/samples/build.sh @@ -17,8 +17,7 @@ # specific language governing permissions and limitations # under the License. # - -set -e +set -euo pipefail PULSAR_IMAGE_TAG=${PULSAR_IMAGE_TAG:-"2.7.1"} PULSAR_IMAGE=${PULSAR_IMAGE:-"streamnative/sn-platform"} @@ -30,22 +29,61 @@ GO_SAMPLE="pulsar-functions-go-sample" CONNECTOR_ES_SAMPLE="pulsar-io-elasticsearch" KIND_PUSH=${KIND_PUSH:-false} CI_TEST=${CI_TEST:-false} +PLATFORMS=${PLATFORMS:-"linux/amd64"} +PRIMARY_PLATFORM=${PLATFORMS%%,*} + +JAVA_SAMPLE_IMAGE="${DOCKER_REPO}/${JAVA_SAMPLE}:${PULSAR_IMAGE_TAG}" +PYTHON_SAMPLE_IMAGE="${DOCKER_REPO}/${PYTHON_SAMPLE}:${PULSAR_IMAGE_TAG}" +GO_SAMPLE_IMAGE="${DOCKER_REPO}/${GO_SAMPLE}:${PULSAR_IMAGE_TAG}" +CONNECTOR_ES_SAMPLE_IMAGE="${DOCKER_REPO}/${CONNECTOR_ES_SAMPLE}:${PULSAR_IMAGE_TAG}" + +MULTI_PLATFORM=false +if [[ "${PLATFORMS}" == *,* ]]; then + MULTI_PLATFORM=true +fi + +PUSH_DEFAULT=false +if [[ "${DOCKER_REPO}" == "localhost:5000" || "${MULTI_PLATFORM}" == "true" ]]; then + PUSH_DEFAULT=true +fi +PUSH=${PUSH:-$PUSH_DEFAULT} + +if [[ "${MULTI_PLATFORM}" == "true" && "${PUSH}" != "true" ]]; then + echo "multi-platform sample builds require PUSH=true" >&2 + exit 1 +fi + +if [[ "${MULTI_PLATFORM}" == "true" && "${KIND_PUSH}" == "true" ]]; then + echo "KIND_PUSH=true is only supported for single-platform sample builds" >&2 + exit 1 +fi + +build_image() { + local image=$1 + local context=$2 + shift 2 + + if [[ "${MULTI_PLATFORM}" == "true" ]]; then + docker buildx build --platform "${PLATFORMS}" --push -t "${image}" "$@" "${context}" + else + docker build --platform "${PRIMARY_PLATFORM}" -t "${image}" "$@" "${context}" + fi +} echo "build java sample" -docker build --platform linux/amd64 -t ${JAVA_SAMPLE} images/samples/java-function-samples --build-arg PULSAR_IMAGE_TAG="$PULSAR_IMAGE_TAG" -docker tag ${JAVA_SAMPLE} "${DOCKER_REPO}"/${JAVA_SAMPLE}:"${PULSAR_IMAGE_TAG}" +build_image "${JAVA_SAMPLE_IMAGE}" images/samples/java-function-samples --build-arg PULSAR_IMAGE_TAG="${PULSAR_IMAGE_TAG}" echo "build python sample" -docker build --platform linux/amd64 -t ${PYTHON_SAMPLE} images/samples/python-function-samples --build-arg PULSAR_IMAGE_TAG="$PULSAR_IMAGE_TAG" -docker tag ${PYTHON_SAMPLE} "${DOCKER_REPO}"/${PYTHON_SAMPLE}:"${PULSAR_IMAGE_TAG}" +build_image "${PYTHON_SAMPLE_IMAGE}" images/samples/python-function-samples --build-arg PULSAR_IMAGE_TAG="${PULSAR_IMAGE_TAG}" echo "build go sample" -docker build --platform linux/amd64 -t ${GO_SAMPLE} images/samples/go-function-samples --build-arg PULSAR_IMAGE_TAG="$PULSAR_IMAGE_TAG" -docker tag ${GO_SAMPLE} "${DOCKER_REPO}"/${GO_SAMPLE}:"${PULSAR_IMAGE_TAG}" +build_image "${GO_SAMPLE_IMAGE}" images/samples/go-function-samples --build-arg PULSAR_IMAGE_TAG="${PULSAR_IMAGE_TAG}" echo "build connector sample" -docker build --platform linux/amd64 -t ${CONNECTOR_ES_SAMPLE} images/samples/pulsar-io-connector/pulsar-io-elasticsearch --build-arg PULSAR_IMAGE_TAG="$PULSAR_IMAGE_TAG" --build-arg PULSAR_IMAGE="$PULSAR_IMAGE" --build-arg RUNNER_IMAGE="$JAVA_RUNNER_IMAGE" -docker tag ${CONNECTOR_ES_SAMPLE} "${DOCKER_REPO}"/${CONNECTOR_ES_SAMPLE}:"${PULSAR_IMAGE_TAG}" +build_image "${CONNECTOR_ES_SAMPLE_IMAGE}" images/samples/pulsar-io-connector/pulsar-io-elasticsearch \ + --build-arg PULSAR_IMAGE_TAG="${PULSAR_IMAGE_TAG}" \ + --build-arg PULSAR_IMAGE="${PULSAR_IMAGE}" \ + --build-arg RUNNER_IMAGE="${JAVA_RUNNER_IMAGE}" if [ "$KIND_PUSH" = true ] ; then echo "push images to kind" @@ -53,8 +91,15 @@ if [ "$KIND_PUSH" = true ] ; then echo $clusters for cluster in $clusters do - kind load docker-image "${DOCKER_REPO}"/${JAVA_SAMPLE}:"${PULSAR_IMAGE_TAG}" --name $cluster - kind load docker-image "${DOCKER_REPO}"/${PYTHON_SAMPLE}:"${PULSAR_IMAGE_TAG}" --name $cluster - kind load docker-image "${DOCKER_REPO}"/${GO_SAMPLE}:"${PULSAR_IMAGE_TAG}" --name $cluster + kind load docker-image "${JAVA_SAMPLE_IMAGE}" --name $cluster + kind load docker-image "${PYTHON_SAMPLE_IMAGE}" --name $cluster + kind load docker-image "${GO_SAMPLE_IMAGE}" --name $cluster done fi + +if [[ "${PUSH}" == "true" && "${MULTI_PLATFORM}" != "true" ]]; then + docker push "${JAVA_SAMPLE_IMAGE}" + docker push "${PYTHON_SAMPLE_IMAGE}" + docker push "${GO_SAMPLE_IMAGE}" + docker push "${CONNECTOR_ES_SAMPLE_IMAGE}" +fi diff --git a/images/samples/go-function-samples/Dockerfile b/images/samples/go-function-samples/Dockerfile index 0a3c0325..8b469ce7 100644 --- a/images/samples/go-function-samples/Dockerfile +++ b/images/samples/go-function-samples/Dockerfile @@ -1,5 +1,8 @@ ARG PULSAR_IMAGE_TAG -FROM golang:1.25.8-trixie as builder +FROM --platform=$BUILDPLATFORM golang:1.25.8-trixie as builder + +ARG TARGETOS +ARG TARGETARCH WORKDIR /workspace # Copy the Go Modules manifests @@ -11,7 +14,7 @@ RUN go mod download COPY func/exclamationFunc.go exclamationFunc.go # Build -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o exclamationFunc exclamationFunc.go +RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} GO111MODULE=on go build -a -o exclamationFunc exclamationFunc.go FROM streamnative/pulsar-functions-go-runner:${PULSAR_IMAGE_TAG} COPY --from=builder --chown=$UID:$GID /workspace/exclamationFunc /pulsar/examples/go-exclamation-func diff --git a/operator.Dockerfile b/operator.Dockerfile index 9c8587fc..5f3864e6 100644 --- a/operator.Dockerfile +++ b/operator.Dockerfile @@ -1,3 +1,24 @@ +FROM --platform=$BUILDPLATFORM golang:1.25.8-trixie AS builder + +ARG TARGETOS +ARG TARGETARCH + +WORKDIR /workspace/api +COPY api/ . +RUN go mod download + +WORKDIR /workspace +COPY go.mod go.mod +COPY go.sum go.sum +RUN go mod download + +COPY main.go main.go +COPY pkg/ pkg/ +COPY controllers/ controllers/ +COPY utils/ utils/ + +RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} GO111MODULE=on go build -a -trimpath -o /workspace/manager main.go + FROM alpine:3.21 ENV GID=10001 @@ -9,6 +30,6 @@ RUN apk upgrade --no-cache \ && addgroup -g $GID pulsar \ && adduser -u $UID -G pulsar -D -g '' $USER -ADD bin/function-mesh-controller-manager /manager +COPY --from=builder /workspace/manager /manager USER $USER diff --git a/redhat.Dockerfile b/redhat.Dockerfile index ab0613ab..9bf8d33a 100644 --- a/redhat.Dockerfile +++ b/redhat.Dockerfile @@ -1,5 +1,8 @@ # Build the manager binary -FROM golang:1.25.8-trixie as builder +FROM --platform=$BUILDPLATFORM golang:1.25.8-trixie as builder + +ARG TARGETOS +ARG TARGETARCH WORKDIR /workspace/api COPY api/ . @@ -20,7 +23,7 @@ COPY controllers/ controllers/ COPY utils/ utils/ # Build -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o manager main.go +RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} GO111MODULE=on go build -a -o manager main.go # Use ubi image as the base image which is required by the red hat certification. # Base on the image size, the order is ubi > ubi-minimal > ubi-micro.