From c10192b6d80c070f474638787b1e84bbf705268f Mon Sep 17 00:00:00 2001 From: humanoid2050 Date: Sat, 18 Mar 2023 23:25:12 -0400 Subject: [PATCH] leverage docker buildkit bake to speed up build times --- tools/docker/build.sh | 73 ++++++++++++------ tools/docker/deploy.sh | 53 +++++++------ tools/docker/docker-bake.hcl | 146 +++++++++++++++++++++++++++++++++++ tools/docker/test.sh | 66 +++++++--------- 4 files changed, 251 insertions(+), 87 deletions(-) create mode 100644 tools/docker/docker-bake.hcl diff --git a/tools/docker/build.sh b/tools/docker/build.sh index 5d0141b85..828ddf7ea 100755 --- a/tools/docker/build.sh +++ b/tools/docker/build.sh @@ -6,46 +6,69 @@ set -euxo pipefail # filesystem # Usage: -# ./build.sh all -# ./build.sh +# ./build.sh all +# ./build.sh +# The argument is used to identify the code version (e.g v2.3.1) or type of build +# (e.g. nightly). This will be used when saving images to the docker image cache. # The argument "all" will build all know architectures. Alternatively, the # user may provide a comma separated list of architectures drawn from the # known architectures. Know architectures include amd64, arm32v6, and arm64v8. source "$(realpath $(dirname ${BASH_SOURCE[0]}))/lib/common" -REQUESTED_ARCH_LIST=$(InterpretArchRequest "$1") -PLATFORM_SPEC=$(archList2platformList "${REQUESTED_ARCH_LIST[@]}") +#used by docker buildx bake, so mark export +export TAG_VER="$1" +if [ -z "$TAG_VER" ]; then + echo "We cannot tag Docker images with an empty string!" >&2 + exit 1 +fi +ARCH_LIST="$2" +if [ -z "$ARCH_LIST" ]; then + echo "Architectures must be specified!" >&2 + exit 1 +fi + +export REGISTRY_SPEC="${DOCKER_HUB_ORG}/" #jump to root, matching popd handed by Cleanup on EXIT via trap pushd "${REPO_ROOT}" # Set trap here, as the popd won't work as expected if invoked prior to pushd trap Cleanup EXIT - +# Create the builder CreateBuilder -# Helper function to build certbot image -BuildCertbot() { - docker buildx build \ - $(StandardCertbotBuildArgs ${PLATFORM_SPEC}) \ - --cache-to=type=local,dest=${DOCKER_CACHE}/certbot \ - . +BuildAll() { + docker buildx bake -f ${WORK_DIR}/docker-bake.hcl \ + --builder certbot_builder \ + --set *.cache-to=type=local,dest=.docker_cache \ + build-all +} +# --progress plain +BuildAndCacheByArch() { + export TAG_ARCH=$1 + docker buildx bake -f ${WORK_DIR}/docker-bake.hcl \ + --builder certbot_builder \ + --set *.platform=$(arch2platform ${TAG_ARCH}) \ + --set *.cache-from=type=local,src=.docker_cache \ + build-all --load } -# Helper function to build plugin image -BuildPlugin() { - PLUGIN=$1 - docker buildx build \ - $(StandardPluginBuildArgs ${PLATFORM_SPEC} ${PLUGIN}) \ - --cache-to=type=local,dest=${DOCKER_CACHE}/${PLUGIN} \ - . -} +# If the request was for all, max out the buildkit parallelization logic +if [ "$ARCH_LIST" = "all" ]; then + BuildAll +fi +# split arch list into an array for per-arch saving of images to the docker image cache +IFS_OLD="$IFS" +IFS="," +read -ra REQUESTED_ARCH_ARRAY <<< $(InterpretArchRequest "$ARCH_LIST") +IFS="$IFS_OLD" +for ARCH in "${REQUESTED_ARCH_ARRAY[@]}"; do + # If the build was already done by BuildAll, then the existing image is pulled + # from the build cache. Otherwise, it gets built on demand here. + # Either way, images get tagged and loaded to the docker image cache + # for use by test and deploy + BuildAndCacheByArch $ARCH +done -# Step 1: Certbot core Docker -BuildCertbot -# Step 2: Certbot DNS plugins Docker images -for PLUGIN in "${CERTBOT_PLUGINS[@]}"; do - BuildPlugin $PLUGIN -done diff --git a/tools/docker/deploy.sh b/tools/docker/deploy.sh index bb0223c38..2e517ebcf 100755 --- a/tools/docker/deploy.sh +++ b/tools/docker/deploy.sh @@ -41,32 +41,39 @@ LatestTag() { fi } -# Helper function to deploy certbot image with version and optional latest tag -DeployCertbot() { - DOCKER_REPO="${DOCKER_HUB_ORG}/certbot" - docker buildx build \ - $(StandardCertbotBuildArgs ${PLATFORM_SPEC}) \ - -t ${DOCKER_REPO}:${TAG_BASE} $(LatestTag ${TAG_BASE}) \ - --push \ - . + +REGISTRY_SPEC="${DOCKER_HUB_ORG}/" + +DeployImage() { + IMAGE_NAME=$1 + TAG_ARCH=$2 + TAG_VER=$3 + docker push "${REGISTRY_SPEC}${IMAGE_NAME}:${TAG_ARCH}-${TAG_VER}" + if [[ "${TAG_BASE}" =~ ^v([2-9]|[1-9][0-9]+)\.[0-9]+\.[0-9]+$ ]]; then + docker tag "${REGISTRY_SPEC}${IMAGE_NAME}:${TAG_ARCH}-${TAG_VER}" "${REGISTRY_SPEC}${IMAGE_NAME}:latest" + fi } -# Helper function to deploy plugin image with version and optional latest tag -DeployPlugin() { - PLUGIN=$1 - DOCKER_REPO="${DOCKER_HUB_ORG}/${PLUGIN}" - docker buildx build \ - $(StandardPluginBuildArgs ${PLATFORM_SPEC} ${PLUGIN}) \ - -t ${DOCKER_REPO}:${TAG_BASE} $(LatestTag ${TAG_BASE}) \ - --push \ - . +DeployManifest() { + IMAGE_NAME=$1 + local IFS="," + read -ra REQUESTED_ARCH_ARRAY <<< $(InterpretArchRequest "$2") + TAG_VER=$3 + + SRC_IMAGES="" + for TAG_ARCH in "${REQUESTED_ARCH_ARRAY[@]}"; do + SRC_IMAGES+="${REGISTRY_SPEC}${IMAGE_NAME}:${TAG_ARCH}-${TAG_VER} " + done + + docker buildx imagetools create -t "${REGISTRY_SPEC}${IMAGE_NAME}:${TAG_VER} $SRC_IMAGES" } -# Step 1: Certbot core Docker -DeployCertbot - -# Step 2: Certbot DNS plugins Docker images -for PLUGIN in "${CERTBOT_PLUGINS[@]}"; do - DeployPlugin $PLUGIN +for TAG_ARCH in "${REQUESTED_ARCH_ARRAY[@]}"; do + DeployImage certbot $TAG_ARCH $TAG_VER + for PLUGIN in "${CERTBOT_PLUGINS[@]}"; do + DeployImage $PLUGIN $TAG_ARCH $TAG_VER + done done + + diff --git a/tools/docker/docker-bake.hcl b/tools/docker/docker-bake.hcl new file mode 100644 index 000000000..911a9656d --- /dev/null +++ b/tools/docker/docker-bake.hcl @@ -0,0 +1,146 @@ +group "build-all" { + targets = ["certbot", + "dns-dnsmadeeasy", + "dns-dnsimple", + "dns-ovh", + "dns-cloudflare", + "dns-digitalocean", + "dns-google", + "dns-luadns", + "dns-nsone", + "dns-rfc2136", + "dns-route53", + "dns-gehirn", + "dns-linode", + "dns-sakuracloud"] + +} + +variable "WORK_DIR" { + default = "tools/docker" +} + +variable "TAG_VER" { + default = "test" +} + +variable "TAG_ARCH" { + default = "auto" +} + +variable "REGISTRY_SPEC" { + // if provided, this should include the trailing slash (e.g. "certbot/) + default = "" +} + +target "certbot" { + dockerfile = "${WORK_DIR}/Dockerfile" + target = "certbot" + tags = ["${REGISTRY_SPEC}certbot:${TAG_ARCH}-${TAG_VER}"] + platforms = ["linux/amd64", "linux/arm64/v8", "linux/arm/v6"] +} + +target "dns-dnsmadeeasy" { + dockerfile = "${WORK_DIR}/Dockerfile" + target = "certbot-plugin" + contexts = {plugin-src = "certbot-dns-dnsmadeeasy"} + tags = ["${REGISTRY_SPEC}dns-dnsmadeeasy:${TAG_ARCH}-${TAG_VER}"] + platforms = ["linux/amd64", "linux/arm64/v8", "linux/arm/v6"] +} + +target "dns-dnsimple" { + dockerfile = "${WORK_DIR}/Dockerfile" + target = "certbot-plugin" + contexts = {plugin-src = "certbot-dns-dnsimple"} + tags = ["${REGISTRY_SPEC}dns-dnsimple:${TAG_ARCH}-${TAG_VER}"] + platforms = ["linux/amd64", "linux/arm64/v8", "linux/arm/v6"] +} + +target "dns-ovh" { + dockerfile = "${WORK_DIR}/Dockerfile" + target = "certbot-plugin" + contexts = {plugin-src = "certbot-dns-ovh"} + tags = ["${REGISTRY_SPEC}dns-ovh:${TAG_ARCH}-${TAG_VER}"] + platforms = ["linux/amd64", "linux/arm64/v8", "linux/arm/v6"] +} + +target "dns-cloudflare" { + dockerfile = "${WORK_DIR}/Dockerfile" + target = "certbot-plugin" + contexts = {plugin-src = "certbot-dns-cloudflare"} + tags = ["${REGISTRY_SPEC}dns-cloudflare:${TAG_ARCH}-${TAG_VER}"] + platforms = ["linux/amd64", "linux/arm64/v8", "linux/arm/v6"] +} + +target "dns-digitalocean" { + dockerfile = "${WORK_DIR}/Dockerfile" + target = "certbot-plugin" + contexts = {plugin-src = "certbot-dns-digitalocean"} + tags = ["${REGISTRY_SPEC}dns-digitalocean:${TAG_ARCH}-${TAG_VER}"] + platforms = ["linux/amd64", "linux/arm64/v8", "linux/arm/v6"] +} + +target "dns-google" { + dockerfile = "${WORK_DIR}/Dockerfile" + target = "certbot-plugin" + contexts = {plugin-src = "certbot-dns-google"} + tags = ["${REGISTRY_SPEC}dns-google:${TAG_ARCH}-${TAG_VER}"] + platforms = ["linux/amd64", "linux/arm64/v8", "linux/arm/v6"] +} + +target "dns-luadns" { + dockerfile = "${WORK_DIR}/Dockerfile" + target = "certbot-plugin" + contexts = {plugin-src = "certbot-dns-luadns"} + tags = ["${REGISTRY_SPEC}dns-luadns:${TAG_ARCH}-${TAG_VER}"] + platforms = ["linux/amd64", "linux/arm64/v8", "linux/arm/v6"] +} + +target "dns-nsone" { + dockerfile = "${WORK_DIR}/Dockerfile" + target = "certbot-plugin" + contexts = {plugin-src = "certbot-dns-nsone"} + tags = ["${REGISTRY_SPEC}dns-nsone:${TAG_ARCH}-${TAG_VER}"] + platforms = ["linux/amd64", "linux/arm64/v8", "linux/arm/v6"] +} + +target "dns-rfc2136" { + dockerfile = "${WORK_DIR}/Dockerfile" + target = "certbot-plugin" + contexts = {plugin-src = "certbot-dns-rfc2136"} + tags = ["${REGISTRY_SPEC}dns-rfc2136:${TAG_ARCH}-${TAG_VER}"] + platforms = ["linux/amd64", "linux/arm64/v8", "linux/arm/v6"] +} + +target "dns-route53" { + dockerfile = "${WORK_DIR}/Dockerfile" + target = "certbot-plugin" + contexts = {plugin-src = "certbot-dns-route53"} + tags = ["${REGISTRY_SPEC}dns-route53:${TAG_ARCH}-${TAG_VER}"] + platforms = ["linux/amd64", "linux/arm64/v8", "linux/arm/v6"] +} + +target "dns-gehirn" { + dockerfile = "${WORK_DIR}/Dockerfile" + target = "certbot-plugin" + contexts = {plugin-src = "certbot-dns-gehirn"} + tags = ["${REGISTRY_SPEC}dns-gehirn:${TAG_ARCH}-${TAG_VER}"] + platforms = ["linux/amd64", "linux/arm64/v8", "linux/arm/v6"] +} + +target "dns-linode" { + dockerfile = "${WORK_DIR}/Dockerfile" + target = "certbot-plugin" + contexts = {plugin-src = "certbot-dns-linode"} + tags = ["${REGISTRY_SPEC}dns-linode:${TAG_ARCH}-${TAG_VER}"] + platforms = ["linux/amd64", "linux/arm64/v8", "linux/arm/v6"] +} + +target "dns-sakuracloud" { + dockerfile = "${WORK_DIR}/Dockerfile" + target = "certbot-plugin" + contexts = {plugin-src = "certbot-dns-sakuracloud"} + tags = ["${REGISTRY_SPEC}dns-sakuracloud:${TAG_ARCH}-${TAG_VER}"] + platforms = ["linux/amd64", "linux/arm64/v8", "linux/arm/v6"] +} + diff --git a/tools/docker/test.sh b/tools/docker/test.sh index 52d5328e4..4f2bc9a07 100755 --- a/tools/docker/test.sh +++ b/tools/docker/test.sh @@ -4,15 +4,30 @@ set -euxo pipefail # This script tests certbot docker and certbot dns plugin images. # Usage: -# ./test.sh all -# ./test.sh +# ./test.sh all +# ./test.sh +# The argument is used to identify the code version (e.g v2.3.1) or type of build +# (e.g. nightly). This will be used when saving images to the docker image cache. # The argument "all" will build all know architectures. Alternatively, the # user may provide a comma separated list of architectures drawn from the # known architectures. Know architectures include amd64, arm32v6, and arm64v8. source "$(realpath $(dirname ${BASH_SOURCE[0]}))/lib/common" -REQUESTED_ARCH_LIST=$(InterpretArchRequest "$1") +TAG_VER="$1" +if [ -z "$TAG_VER" ]; then + echo "We cannot tag Docker images with an empty string!" >&2 + exit 1 +fi +if [ -z "$2" ]; then + echo "Architectures must be specified!" >&2 + exit 1 +fi +IFS_OLD="$IFS" +IFS="," +read -ra REQUESTED_ARCH_ARRAY <<< $(InterpretArchRequest "$2") +IFS="$IFS_OLD" + #jump to root, matching popd handed by Cleanup on EXIT via trap pushd "${REPO_ROOT}" @@ -23,46 +38,19 @@ trap Cleanup EXIT CreateBuilder -IFS_OLD="$IFS" -IFS="," -read -ra REQUESTED_ARCH_ARRAY <<< "$REQUESTED_ARCH_LIST" -IFS="$IFS_OLD" +REGISTRY_SPEC="${DOCKER_HUB_ORG}/" -# Helper function to load and test certbot image -TestCertbot() { - ARCH=$1 - DOCKER_REPO="${DOCKER_HUB_ORG}/certbot" - docker buildx build \ - $(StandardCertbotBuildArgs $(arch2platform ${ARCH})) \ - -t ${DOCKER_REPO}:${ARCH} \ - --load \ - . - docker run --rm "${DOCKER_REPO}:${ARCH}" plugins --prepare - docker rmi ${DOCKER_REPO}:${ARCH} +TestImage() { + IMAGE_NAME=$1 + TAG_ARCH=$2 + TAG_VER=$3 + docker run --rm "${REGISTRY_SPEC}${IMAGE_NAME}:${TAG_ARCH}-${TAG_VER}" plugins --prepare } -# Helper function to load and test plugin image -TestPlugin() { - ARCH=$1 - PLUGIN=$2 - DOCKER_REPO="${DOCKER_HUB_ORG}/${PLUGIN}" - docker buildx build \ - $(StandardPluginBuildArgs $(arch2platform ${ARCH}) ${PLUGIN}) \ - -t ${DOCKER_REPO}:${ARCH} \ - --load \ - . - docker run --rm "${DOCKER_REPO}:${ARCH}" plugins --prepare - docker rmi ${DOCKER_REPO}:${ARCH} -} -# Step 1: Certbot core Docker -for ARCH in "${REQUESTED_ARCH_ARRAY[@]}"; do - TestCertbot $ARCH -done - -# Step 2: Certbot DNS plugins Docker images -for ARCH in "${REQUESTED_ARCH_ARRAY[@]}"; do +for TAG_ARCH in "${REQUESTED_ARCH_ARRAY[@]}"; do + TestImage certbot $TAG_ARCH $TAG_VER for PLUGIN in "${CERTBOT_PLUGINS[@]}"; do - TestPlugin $ARCH $PLUGIN + TestImage $PLUGIN $TAG_ARCH $TAG_VER done done