From 0ff368f5020082550922d0fb911cf9628206e42e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E6=8C=AF=E5=AE=87?= <> Date: Mon, 10 Feb 2025 04:00:49 +0800 Subject: [PATCH] fix(pipeline): update buildx create command to specify platforms for multi-arch images MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 孙振宇 <> --- .../com/freeleaps/devops/ImageBuilder.groovy | 114 ++++++++++-------- 1 file changed, 62 insertions(+), 52 deletions(-) diff --git a/first-class-pipeline/src/com/freeleaps/devops/ImageBuilder.groovy b/first-class-pipeline/src/com/freeleaps/devops/ImageBuilder.groovy index d9e66d2b..3450a994 100644 --- a/first-class-pipeline/src/com/freeleaps/devops/ImageBuilder.groovy +++ b/first-class-pipeline/src/com/freeleaps/devops/ImageBuilder.groovy @@ -58,14 +58,9 @@ class ImageBuilder { if (builderType == ImageBuilderTypes.DOCKER_IN_DOCKER && architectures.size() > 1) { steps.log.warn("ImageBuilder", "If you want to build multi-arch images and using Docker in Docker (DIND) as builder, system will using buildx to replace build command.") steps.log.info("ImageBuilder", "Creating buildx builder with name: multiarch-builder-${name}") - steps.sh "docker buildx create --use --name multiarch-builder-${name}" + steps.sh "docker buildx create --use --name multiarch-builder-${name} --platform ${architectures.join(",")}" steps.log.info("ImageBuilder", "Inspecting buildx builder with name: multiarch-builder-${name}") steps.sh "docker buildx inspect --bootstrap" - steps.log.info("ImageBuilder", "Register clean up hook for buildx builder deletion for builder named: multiarch-builder-${name}") - def context = steps.$build() - context.cleanup { - steps.sh "docker buildx rm multiarch-builder-${name} || true" - } this.buildxBuilderName = "multiarch-builder-${name}" } } @@ -78,63 +73,78 @@ class ImageBuilder { } def build() { - steps.log.info("ImageBuilder", "Building image with ${builderType.builder}") - steps.log.info("ImageBuilder", "Workspace sets to: ${workspace}") - steps.log.info("ImageBuilder", "Using dockerfile at: ${dockerfile}, context root sets to: ${contextRoot}") + try { + steps.log.info("ImageBuilder", "Building image with ${builderType.builder}") + steps.log.info("ImageBuilder", "Workspace sets to: ${workspace}") + steps.log.info("ImageBuilder", "Using dockerfile at: ${dockerfile}, context root sets to: ${contextRoot}") - if (architectures == null || architectures.isEmpty()) { - steps.log.warn("ImageBuilder", "No architectures specified, using default amd64") - architectures = ['linux/amd64'] - } + if (architectures == null || architectures.isEmpty()) { + steps.log.warn("ImageBuilder", "No architectures specified, using default amd64") + architectures = ['linux/amd64'] + } + + steps.withCredentials([steps.usernamePassword(credentialsId: registryCredentialsId, passwordVariable: 'DOCKER_PASSWORD', usernameVariable: 'DOCKER_USERNAME')]) { + steps.log.info("ImageBuilder", "Authentication to ${registry}") + switch(builderType) { + case ImageBuilderTypes.DOCKER_IN_DOCKER: + steps.sh "docker login -u ${steps.env.DOCKER_USERNAME} -p ${steps.env.DOCKER_PASSWORD} ${registry}" + break + case ImageBuilderTypes.KANIKO: + def auth = "${steps.env.DOCKER_USERNAME}:${steps.env.DOCKER_PASSWORD}".bytes.encodeBase64().toString() + steps.writeFile file: '/kaniko/.docker/config.json', text: """{ + "auths": { + "${registry}": { + "auth": "${auth}" + } + } + }""" + break + default: + steps.error("Unsupported builder type: ${builderType.builder}") + } + } - steps.withCredentials([steps.usernamePassword(credentialsId: registryCredentialsId, passwordVariable: 'DOCKER_PASSWORD', usernameVariable: 'DOCKER_USERNAME')]) { - steps.log.info("ImageBuilder", "Authentication to ${registry}") switch(builderType) { case ImageBuilderTypes.DOCKER_IN_DOCKER: - steps.sh "docker login -u ${steps.env.DOCKER_USERNAME} -p ${steps.env.DOCKER_PASSWORD} ${registry}" - break - case ImageBuilderTypes.KANIKO: - def auth = "${steps.env.DOCKER_USERNAME}:${steps.env.DOCKER_PASSWORD}".bytes.encodeBase64().toString() - steps.writeFile file: '/kaniko/.docker/config.json', text: """{ - "auths": { - "${registry}": { - "auth": "${auth}" + steps.dir(workspace) { + if (buildxBuilderName != null && !buildxBuilderName.isEmpty() && architectures.size() > 1) { + steps.log.info("ImageBuilder", "Building image ${registry}/${repository}/${name} with architectures: ${architectures} using buildx builder: ${buildxBuilderName}, tag sets to ${version}") + steps.sh "docker buildx build --builder ${buildxBuilderName} --platform ${architectures.join(",")} -t ${registry}/${repository}/${name}:${version} -f ${dockerfile} --push ${contextRoot}" + } else { + architectures.each { architecture -> + def archTag = architecture.split("/")[1] + steps.log.info("ImageBuilder", "Building image ${registry}/${repository}/${name} with architectures: ${architectures}, tag sets to ${version}") + steps.sh "docker build -t ${registry}/${repository}/${name}:${version}-${archTag} --platform ${architecture} -f ${dockerfile} ${contextRoot}" + steps.sh "docker push ${registry}/${repository}/${name}:${version}-${archTag}" } } - }""" + } + break + case ImageBuilderTypes.KANIKO: + steps.dir(workspace) { + architectures.each { architecture -> + def archTag = architecture.split("/")[1] + steps.log.info("ImageBuilder", "Building image ${registry}/${repository}/${name} with architectures: ${architectures}, tag sets to ${version}-${archTag}") + steps.sh "/kaniko/executor --log-format text --context ${contextRoot} --dockerfile ${dockerfile} --destination ${registry}/${repository}/${name}:${version}-${archTag} --custom-platform ${architecture}" + } + } break default: steps.error("Unsupported builder type: ${builderType.builder}") } - } - - switch(builderType) { - case ImageBuilderTypes.DOCKER_IN_DOCKER: - steps.dir(workspace) { - if (buildxBuilderName != null && !buildxBuilderName.isEmpty() && architectures.size() > 1) { - steps.log.info("ImageBuilder", "Building image ${registry}/${repository}/${name} with architectures: ${architectures} using buildx builder: ${buildxBuilderName}, tag sets to ${version}") - steps.sh "docker buildx build --builder ${buildxBuilderName} --platform ${architectures.join(",")} -t ${registry}/${repository}/${name}:${version} -f ${dockerfile} --push ${contextRoot}" - } else { - architectures.each { architecture -> - def archTag = architecture.split("/")[1] - steps.log.info("ImageBuilder", "Building image ${registry}/${repository}/${name} with architectures: ${architectures}, tag sets to ${version}") - steps.sh "docker build -t ${registry}/${repository}/${name}:${version}-${archTag} --platform ${architecture} -f ${dockerfile} ${contextRoot}" - steps.sh "docker push ${registry}/${repository}/${name}:${version}-${archTag}" - } - } + } catch (Exception e) { + steps.log.error("ImageBuilder", "Failed to build image: ${e.message}") + throw e + } finally { + if (buildxBuilderName != null && !buildxBuilderName.isEmpty() && architectures.size() > 1) { + try { + steps.log.info("ImageBuilder", "Cleaning up buildx builder: ${buildxBuilderName}") + steps.sh "docker buildx rm ${buildxBuilderName} || true" + } catch (Exception e) { + steps.log.warn("ImageBuilder", "Failed to cleanup buildx builder: ${e.message}") } - break - case ImageBuilderTypes.KANIKO: - steps.dir(workspace) { - architectures.each { architecture -> - def archTag = architecture.split("/")[1] - steps.log.info("ImageBuilder", "Building image ${registry}/${repository}/${name} with architectures: ${architectures}, tag sets to ${version}-${archTag}") - steps.sh "/kaniko/executor --log-format text --context ${contextRoot} --dockerfile ${dockerfile} --destination ${registry}/${repository}/${name}:${version}-${archTag} --custom-platform ${architecture}" - } - } - break - default: - steps.error("Unsupported builder type: ${builderType.builder}") + } } } } +