Multi-Architecture Docker Image Build in Gradle
[programming
springboot
docker
gradle
]
Usually, when you need build a bootable Spring Boot image, bootBuildImage
is exactly what you’re looking for. The plugin has got many capabilities, including pushes to docker image registries.
Unfortunately, Spring Boot does not have a native support for multi-architecture builds (or even cross-architecture builds for that matter), which was a real blocker for me since most of my hobby development is done on an AMD64 processor but I deploy most of the images on my Raspberry Pi rack which uses ARM64.
I already knew that docker does support multi-architecture builds, so I was a stone’s throw away from a script I could run from gradle.
I created a new task buildMultiArchImage
which exactly does this. Since I don’t use gitlab pipelines for my personal projects (I couldn’t be bothered), I run
DOCKER_HUB_USERNAME=... DOCKER_HUB_PASSWORD=... ./gradlew buildMultiArchImage
directly from my machine.
Here is what buildMultiArchImage
looks like:
val bootBuildImageTaskName = "buildMultiArchImage"
/**
* At the time of writing, there was no easy way of building multiplatform
* Docker, nor there was any reasonable Docker replacement which would have
* this feature.
*
* If possible, please use a more endorsed means of OCI image building!
*/
val bootBuildImage = tasks.register(bootBuildImageTaskName) {
group = "build"
description = "Builds multi-architecture Docker images using 'docker buildx'."
mustRunAfter("build")
doLast {
val usernameEnv = "DOCKER_HUB_USERNAME"
val passEnv = "DOCKER_HUB_PASSWORD"
val platforms = "linux/amd64,linux/arm64"
val user = requireEnvironmentVariable(usernameEnv)
val pass = requireEnvironmentVariable(passEnv)
val dockerImageRepositoryBaseUrl = System.getenv("DOCKER_HUB_URL") ?: "docker.io"
exec {
commandLine("docker", "buildx", "create", "--use")
commandLine("docker", "login", "-u", user, "-p", pass)
}
exec {
buildAndPushMultiArchImage(
platforms = platforms,
dockerImageRepositoryBaseUrl = dockerImageRepositoryBaseUrl,
user = user,
imageName = "beehive-external",
dockerfileName = "main.Dockerfile"
)
}
// you can build and push as many images as you need, just continue with a new `exec` block and buildAndPushMultiArchImage
}
}
private fun ExecSpec.buildAndPushMultiArchImage(
platforms: String,
dockerImageRepositoryBaseUrl: String,
user: String,
imageName: String,
dockerfileName: String
) {
commandLine(
"docker", "buildx", "build",
"--platform", platforms,
"-t", "${dockerImageRepositoryBaseUrl}/${user}/${imageName}:${project.version}",
"--push", rootDir.absolutePath,
"-f", listOf(rootDir.absolutePath, dockerfileName).joinToString(File.separator)
)
}
private fun requireEnvironmentVariable(passEnv: String) = requireNotNull(System.getenv(passEnv)) {
"When using the task '$bootBuildImageTaskName', you must specify a dockerhub username via env property $passEnv ."
}