Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions terraform/modules/cloud-build-docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ A reusable Terraform module for building Docker images using Google Cloud Build
## Features

- **Cloud Build Integration**: Uses Google Cloud Build for reliable, scalable Docker image building
- **Multi-Platform Builds**: Supports building for multiple architectures (amd64, arm64) via docker buildx
- **Branch-based Caching**: Optimizes build times by caching layers based on branch names
- **Cache Fallback**: Automatically falls back to "latest" tag if the specified cache tag doesn't exist, ensuring we always have some level of caching
- **Digest Tracking**: Returns full image digests for precise versioning in Terraform
Expand Down Expand Up @@ -109,6 +110,24 @@ module "secure_app" {
}
```

### Multi-Platform Build (amd64 + arm64)

```hcl
module "cross_platform_app" {
source = "git::https://github.com/Khan/terraform-cloud-build-docker-module.git?ref=v1.0.0"

image_name = "cross-platform-app"
context_path = "./app"
project_id = var.project_id
image_tag_suffix = "latest"

# Build for both amd64 and arm64 platforms
platforms = ["linux/amd64", "linux/arm64"]
}
```

> **Note**: Multi-platform builds use docker buildx. For Go services with multi-stage Dockerfiles, cross-compilation is used (Go compiler runs on amd64 but produces binaries for each target platform via `TARGETARCH`/`TARGETOS` build args), which is much faster than QEMU emulation.

## Requirements & Inputs

### Required
Expand All @@ -122,9 +141,8 @@ module "secure_app" {

- `dockerfile_path` - Path to the Dockerfile relative to context_path ("Dockerfile")
- `base_digest` - Base image digest for build args ("latest")
- `cloud_build_config` - Custom Cloud Build config file (null)
- `build_args` - Additional build arguments ({})
- `cache_enabled` - Enable branch-based caching (true)
- `region` - GCP region for Cloud Build ("us-central1")
- `platforms` - Target platforms for multi-arch builds (["linux/amd64"]). Use ["linux/amd64", "linux/arm64"] for cross-platform builds.

## Outputs

Expand Down
21 changes: 18 additions & 3 deletions terraform/modules/cloud-build-docker/build_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,13 @@ def build_image(
image_tag_suffix,
base_digest="latest",
region="us-central1",
platforms="linux/amd64",
):
"""Build a Docker image via Cloud Build and return its digest."""
"""Build a Docker image via Cloud Build and return its digest.

For multi-platform builds (when platforms contains multiple architectures),
docker buildx is used for efficient cross-compilation.
"""

# Construct image URIs
image_uri = f"gcr.io/{project_id}/{image_name}"
Expand Down Expand Up @@ -168,13 +173,21 @@ def build_image(
"_IMAGE_TAG": image_tag,
"_BASE_DIGEST": base_digest,
"_CACHE_TAG": effective_cache_tag, # Use effective cache tag (with fallback to latest)
"_PLATFORMS": platforms,
}
subs_str = ",".join(f"{k}={v}" for k, v in substitutions.items())

# Submit to Cloud Build
# Get the directory where this script is located to find cloudbuild.yml
# Get the directory where this script is located to find cloudbuild config
script_dir = os.path.dirname(os.path.abspath(__file__))
cloudbuild_config = os.path.join(script_dir, "cloudbuild.yml")

# Use buildx config for multi-platform builds
is_multiplatform = "," in platforms
if is_multiplatform:
cloudbuild_config = os.path.join(script_dir, "cloudbuild-buildx.yml")
print(f"Using buildx for multi-platform build: {platforms}", file=sys.stderr)
else:
cloudbuild_config = os.path.join(script_dir, "cloudbuild.yml")

# TODO(jwbron): Consider adding automatic GCS bucket creation with import support for existing buckets in terraform
# Use --polling-interval to reduce API calls and avoid rate limits when running many parallel builds
Expand Down Expand Up @@ -226,6 +239,7 @@ def main():
image_tag_suffix = input_data["image_tag_suffix"]
base_digest = input_data.get("base_digest", "latest")
region = input_data.get("region", "us-central1")
platforms = input_data.get("platforms", "linux/amd64")

# Validate that image_tag_suffix is not empty
if not image_tag_suffix or image_tag_suffix.strip() == "":
Expand All @@ -240,6 +254,7 @@ def main():
image_tag_suffix=image_tag_suffix,
base_digest=base_digest,
region=region,
platforms=platforms,
)

# Return JSON output for Terraform
Expand Down
34 changes: 34 additions & 0 deletions terraform/modules/cloud-build-docker/cloudbuild-buildx.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Cloud Build configuration for multi-platform builds using docker buildx.
# This config is used when multiple platforms are specified (e.g., linux/amd64,linux/arm64).
# Uses efficient Go cross-compilation via TARGETARCH/TARGETOS build args.
steps:
- name: 'gcr.io/cloud-builders/docker'
id: Setup buildx
entrypoint: bash
args:
- -c
- |
echo "Setting up docker buildx..."
docker buildx create --name multiarch-builder --use
docker buildx inspect --bootstrap

- name: 'gcr.io/cloud-builders/docker'
id: Build and push multi-arch image
entrypoint: bash
args:
- -c
- |
echo "Building multi-platform image for: $_PLATFORMS"

# buildx builds must push directly (can't load multi-arch images locally)
# --cache-from and --cache-to use registry-based caching
docker buildx build \
--platform="$_PLATFORMS" \
--tag="$_IMAGE_TAG" \
--tag="$_IMAGE_NAME:$_CACHE_TAG" \
--build-arg BASE_IMAGE="$_BASE_DIGEST" \
--push \
.
waitFor: ['Setup buildx']

# No 'images' section needed - buildx pushes directly during build
2 changes: 2 additions & 0 deletions terraform/modules/cloud-build-docker/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ data "external" "image_build" {
image_tag_suffix = var.image_tag_suffix
base_digest = var.base_digest
region = var.region
# Pass platforms as comma-separated string for the external script
platforms = join(",", var.platforms)
}

# Trigger rebuild when any of these change
Expand Down
11 changes: 11 additions & 0 deletions terraform/modules/cloud-build-docker/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,14 @@ variable "region" {
type = string
default = "us-central1"
}

variable "platforms" {
description = "Target platforms for multi-arch builds (e.g., ['linux/amd64', 'linux/arm64']). When multiple platforms are specified, docker buildx is used for cross-platform builds."
type = list(string)
default = ["linux/amd64"]

validation {
condition = length(var.platforms) > 0
error_message = "At least one platform must be specified."
}
}