Skip to content

Commit dbcd8be

Browse files
authored
Add cache fallback to "latest" for cloud-build-docker module (#12)
## Summary: No one likes slow PR checks. With the current cloud-build-docker implementation, caching is based on the branch name. This means the first docker build for a given branch will take longer because it won't have a cache to pull from. This change enhances the cloud-build-docker module to automatically fall back to using "latest" as the cache tag when the specified cache tag doesn't exist in the container registry. This improves build performance by ensuring builds can still benefit from layer caching even when branch-specific caches are not available. Key changes: - Added `check_cache_tag_exists()` function to verify if a cache tag exists - Added `get_effective_cache_tag()` function that determines the effective cache tag with fallback logic - Modified `build_image()` to use the effective cache tag instead of always using the provided tag - Enhanced Cloud Build configuration with better logging and error handling - Updated documentation to reflect the new cache fallback behavior The implementation provides clear logging to indicate when fallback occurs, making it easy to understand the caching behavior during builds. ## Test plan: This was tested [here](Khan/internal-services#355) Author: jwbron Reviewers: csilvers Required Reviewers: Approved By: csilvers Checks: ✅ 1 check was successful Pull Request URL: #12
1 parent 71e9c26 commit dbcd8be

File tree

3 files changed

+61
-8
lines changed

3 files changed

+61
-8
lines changed

terraform/modules/cloud-build-docker/README.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ A reusable Terraform module for building Docker images using Google Cloud Build
66

77
- **Cloud Build Integration**: Uses Google Cloud Build for reliable, scalable Docker image building
88
- **Branch-based Caching**: Optimizes build times by caching layers based on branch names
9+
- **Cache Fallback**: Automatically falls back to "latest" tag if the specified cache tag doesn't exist, ensuring we always have some level of caching
910
- **Digest Tracking**: Returns full image digests for precise versioning in Terraform
1011
- **Flexible Dockerfile Support**: Supports custom Dockerfile names and locations
1112
- **Build Arguments**: Supports custom build arguments and base image digests
@@ -132,13 +133,15 @@ module "secure_app" {
132133

133134
### Build Process
134135
1. **Context Preparation**: The module prepares the build context and handles Dockerfile symlinks if needed
135-
2. **Cloud Build Submission**: Submits the build to Google Cloud Build with appropriate substitutions
136-
3. **Caching**: Uses branch-based caching to optimize build times
137-
4. **Digest Retrieval**: Queries the built image to get its full digest
138-
5. **Output**: Returns the digest for use in other Terraform resources
136+
2. **Cache Tag Resolution**: Determines the effective cache tag by checking if the specified tag exists, falling back to "latest" if not
137+
3. **Cloud Build Submission**: Submits the build to Google Cloud Build with appropriate substitutions
138+
4. **Caching**: Uses the effective cache tag for layer caching to optimize build times
139+
5. **Digest Retrieval**: Queries the built image to get its full digest
140+
6. **Output**: Returns the digest for use in other Terraform resources
139141

140142
### Caching Strategy
141143
- **Branch-based**: Uses the `image_tag_suffix` as a cache tag
144+
- **Fallback to Latest**: If the specified cache tag doesn't exist, falls back to using "latest" as the cache tag, ensuring we always have some level of caching
142145
- **Layer Reuse**: Subsequent builds reuse cached layers when possible
143146
- **Cache Invalidation**: Cache is automatically invalidated when Dockerfile or context changes
144147

@@ -265,3 +268,4 @@ resource "google_cloud_run_v2_service" "app" {
265268
- Clear cache by using a unique `image_tag_suffix`
266269
- Check if base images are accessible
267270
- Verify network connectivity during builds
271+
- **Cache Fallback**: If you see "falling back to 'latest'" in logs, the specified cache tag doesn't exist and the module is using "latest" instead

terraform/modules/cloud-build-docker/build_image.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,48 @@ def get_image_digest(image_uri, tag, project_id):
5454
return None
5555

5656

57+
def check_cache_tag_exists(image_uri, cache_tag, project_id):
58+
"""Check if a cache tag exists for the given image."""
59+
try:
60+
result = run_command(
61+
[
62+
"gcloud",
63+
"container",
64+
"images",
65+
"list-tags",
66+
image_uri,
67+
"--filter",
68+
f"tags:{cache_tag}",
69+
"--limit",
70+
"1",
71+
"--format=get(digest)",
72+
"--project",
73+
project_id,
74+
]
75+
)
76+
return bool(result.stdout.strip())
77+
except subprocess.CalledProcessError:
78+
return False
79+
80+
81+
def get_effective_cache_tag(image_uri, image_tag_suffix, project_id):
82+
"""Determine the effective cache tag.
83+
84+
Ideally, the cache tag we use is the requested tag, `image_tag_suffix`.
85+
But if that doesn't exist, we fall back to 'latest'. In that case, we may
86+
still be able to reuse some or all layers, depending on where changes were
87+
made in the branch running this build.
88+
"""
89+
# First check if the provided cache tag exists
90+
if check_cache_tag_exists(image_uri, image_tag_suffix, project_id):
91+
print(f"Using cache tag: {image_tag_suffix}", file=sys.stderr)
92+
return image_tag_suffix
93+
94+
# Fall back to 'latest' if the provided tag doesn't exist
95+
print(f"Cache tag '{image_tag_suffix}' not found, falling back to 'latest'", file=sys.stderr)
96+
return "latest"
97+
98+
5799
def build_image(
58100
image_name,
59101
context_path,
@@ -70,6 +112,9 @@ def build_image(
70112

71113
print(f"Building image: {image_name} with tag: {image_tag_suffix}", file=sys.stderr)
72114

115+
# Determine the effective cache tag
116+
effective_cache_tag = get_effective_cache_tag(image_uri, image_tag_suffix, project_id)
117+
73118
# Handle Dockerfile symlink if needed
74119
# The symlinking logic allows using Dockerfiles with:
75120
# - Custom names (e.g., listener.Dockerfile, base.Dockerfile)
@@ -117,7 +162,7 @@ def build_image(
117162
"_IMAGE_NAME": image_uri,
118163
"_IMAGE_TAG": image_tag,
119164
"_BASE_DIGEST": base_digest,
120-
"_CACHE_TAG": image_tag_suffix, # Use branch name for caching
165+
"_CACHE_TAG": effective_cache_tag, # Use effective cache tag (with fallback to latest)
121166
}
122167
subs_str = ",".join(f"{k}={v}" for k, v in substitutions.items())
123168

terraform/modules/cloud-build-docker/cloudbuild.yml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
# Cloud Build configuration with branch-based caching.
2-
# Uses branch name as cache tag for optimal layer reuse.
2+
# Enables caching by pulling the previous image with a given "cache tag", if it exists, and reusing its layers.
33
steps:
44
- name: 'gcr.io/cloud-builders/docker'
55
id: Pull previous image
66
entrypoint: bash
7-
args: ['-c', 'docker pull "$_IMAGE_NAME:$_CACHE_TAG" || true']
7+
args:
8+
- -c
9+
- |
10+
echo "Attempting to pull cache image: $_IMAGE_NAME:$_CACHE_TAG"
11+
docker pull "$_IMAGE_NAME:$_CACHE_TAG" || echo "Cache image not found, will build without cache"
812
913
- name: 'gcr.io/cloud-builders/docker'
1014
id: Build image
@@ -16,7 +20,7 @@ steps:
1620
echo "Using cache from $_IMAGE_NAME:$_CACHE_TAG"
1721
docker build --tag="$_IMAGE_TAG" --cache-from="$_IMAGE_NAME:$_CACHE_TAG" --build-arg BASE_IMAGE="$_BASE_DIGEST" .
1822
else
19-
echo "No cache found, building without --cache-from"
23+
echo "No cache found for $_IMAGE_NAME:$_CACHE_TAG, building without --cache-from"
2024
docker build --tag="$_IMAGE_TAG" --build-arg BASE_IMAGE="$_BASE_DIGEST" .
2125
fi
2226

0 commit comments

Comments
 (0)