Skip to content

Commit c49f40c

Browse files
authored
feat(ci): Implement CI/CD pipeline for parallel Ubuntu Docker builds (#4952)
This PR establishes a robust CI/CD pipeline for building and publishing Docker images, addressing the requirements of the Ubuntu 24.04 migration. The new system introduces separate, parallel build workflows for Pull Requests and merges to the master branch. Part of: [b/441792502](b/441792502) *** ### Key Changes - **New CI/CD Workflow:** Implemented a two-stage pipeline for Docker image builds: 1. **Pull Request Verification:** A new trigger, `Build-Base-Images-PR`, automatically runs on PRs that modify files in the `docker/` directory. It performs a parallel, **build-only** check for all Ubuntu versions to validate the Dockerfiles. The success of this build is required for merging. 2. **Master Branch Deployment:** A second new trigger, `Build-And-Push-Base-Images-Master`, runs upon merging to `master`. It executes the same parallel build process but also **pushes** the tagged images to the Artifact Registry. - **Parallel Execution:** The `docker/cloudbuild.yaml` configuration was updated to run the builds for `latest`, `ubuntu-20-04`, and `ubuntu-24-04` simultaneously, significantly reducing pipeline execution time. - **Conditional Push Logic:** The `docker/build.sh` script was refactored to accept a `--no-push` flag, allowing the CI pipeline to control whether images are only built (for PRs) or built and pushed (for master). *** ### How to Verify 1. **Pull Request:** Observe that the `Build-Base-Images-PR` check has run and passed on this PR, as it contains changes in the `docker/` directory. 2. **Master Branch:** After this PR is merged, the `Build-And-Push-Base-Images-Master` trigger will be active. To verify its functionality, a subsequent PR that modifies a file in `docker/` can be merged. The Cloud Build dashboard for the `clusterfuzz-images` project should then show this trigger running and successfully pushing the images.
1 parent 87bfaea commit c49f40c

File tree

7 files changed

+314
-186
lines changed

7 files changed

+314
-186
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,7 @@ bazel-*
5555
*.tfstate*
5656
*.lock.hcl
5757
**/.terraform/*
58+
59+
# Ignore temporary build files.
60+
docker/base/Pipfile
61+
docker/base/Pipfile.lock

Pipfile.lock

Lines changed: 194 additions & 160 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docker/README.md

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,61 @@
1-
# Docker Build Instructions
1+
# Docker Images
22

3-
## Local testing
3+
This directory contains the Dockerfiles for various images used by ClusterFuzz.
44

5-
You can build a specific image locally using the `docker build` command from the root of the **clusterfuzz** repository.
5+
## Building Images Locally
66

7-
For example, to build the `base` image, run the following command from the root of the **clusterfuzz** repository:
7+
The `build.sh` script is the primary way to build images locally. It handles the complexities of build contexts and tagging for all images.
8+
9+
### Prerequisites
10+
11+
Ensure you are in the `docker/` directory before running the script:
12+
```bash
13+
cd docker
14+
```
15+
16+
### Usage
17+
18+
The script builds all images defined in its configuration. You must specify the Ubuntu version to build for.
819

920
```bash
10-
docker build -f docker/base/Dockerfile docker/base
21+
# Usage: ./build.sh <ubuntu-version> [git-hash] [--no-push]
1122
```
1223

13-
### Command Explanation
24+
- `<ubuntu-version>`: **(Required)** The Ubuntu version to build (e.g., `20.04`, `24.04`). The script will look for a corresponding `.Dockerfile` for each image.
25+
- `git-hash`: (Optional) A git hash to use for tagging the image. Defaults to the current `HEAD`.
26+
- `--no-push`: (Optional) A flag to prevent the script from pushing the built images to the container registry. This is useful for local testing.
27+
28+
### Examples
29+
30+
**1. Build all `ubuntu-24.04` images for local testing:**
31+
```bash
32+
./build.sh ubuntu-24-04 --no-push
33+
```
1434

15-
* `docker build`: The standard command to build a Docker image.
16-
* `-f docker/base/Dockerfile`: This flag specifies the path to the `Dockerfile` to be used, relative to the repository root.
17-
* `docker/base`: This is the "build context". The Docker daemon will have access to all files and folders within this path. This is necessary for the `COPY` instructions in the `Dockerfile`.
35+
**2. Build and push all `ubuntu-20.04` images:**
36+
```bash
37+
./build.sh ubuntu-20.04
38+
```
1839

19-
You can adapt this command to build other images by changing the path to the `Dockerfile` and the build context accordingly.
40+
## Manual Build (for a single image)
2041

21-
## Production
42+
While using `build.sh` is recommended, you can build a specific image manually. You must run the `docker build` command from the **root of the clusterfuzz repository**.
2243

23-
To build all images on container builder, run:
44+
For example, to build the `base` image for `ubuntu-24.04`:
2445

2546
```bash
26-
./build_on_container_builder.sh
47+
# Note: This command requires manually handling the build context.
48+
# The build.sh script handles this automatically.
49+
docker build -f docker/base/ubuntu-24-04.Dockerfile docker/base
2750
```
2851

29-
Note that your checkout needs to be on the latest deployed commit.
30-
You also need to have access to the `clusterfuzz-images` project.
52+
## Production Builds
53+
54+
Production images are built using Google Cloud Build. The configuration is defined in `cloudbuild.yaml`, which uses the `build.sh` script. The build process is automated with the following triggers:
55+
56+
- **Pull Requests:** A trigger runs on every pull request that modifies files under the `docker/` directory. It builds all images with the `--no-push` flag to validate the changes without deploying them.
57+
- **Push to `master`:** A trigger runs on every push to the `master` branch that modifies files under the `docker/` directory. It builds and pushes all images to the container registry, making them available for production use.
58+
3159
## Docker Image Dependency Tree
3260

3361
```mermaid
@@ -79,4 +107,4 @@ graph TD
79107
H --> O;
80108
81109
O --> P;
82-
```
110+
```

docker/base/ubuntu-24-04.Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ RUN locale-gen en_US.UTF-8
128128
ENV LANG en_US.UTF-8
129129
ENV PYTHONIOENCODING UTF-8
130130

131-
COPY setup_common.sh setup_clusterfuzz.sh start_clusterfuzz.sh setup_mock_metadata.sh Pipfile Pipfile.lock start.sh /data/
131+
COPY Pipfile Pipfile.lock setup_common.sh setup_clusterfuzz.sh start_clusterfuzz.sh setup_mock_metadata.sh start.sh /data/
132132
RUN cd /data && \
133133
# Make pip3.11 the default so that pipenv install --system works.
134134
mv /usr/local/bin/pip3.11 /usr/local/bin/pip && \

docker/build.sh

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,48 @@ IMAGES=(
3131
gcr.io/clusterfuzz-images/fuchsia
3232
)
3333

34-
# The first argument is the version tag, e.g., 'latest', 'ubuntu-20-04'.
35-
VERSION_TAG=${1:-latest}
36-
# The second argument is the git hash.
37-
GIT_HASH_ARG=${2}
34+
# Default values
35+
VERSION_TAG="latest"
36+
GIT_HASH_ARG=""
37+
PUSH="true"
38+
NEEDS_ROOT_PIPFILE=false
39+
40+
# Set up a trap to clean up Pipfiles on exit.
41+
function cleanup() {
42+
if [[ "$NEEDS_ROOT_PIPFILE" == "true" ]]; then
43+
rm -f base/Pipfile base/Pipfile.lock
44+
fi
45+
}
46+
trap cleanup EXIT
47+
48+
# Parse command-line arguments
49+
# The first two arguments are positional for backwards compatibility.
50+
if [ -n "$1" ] && ! [[ "$1" =~ ^-- ]]; then
51+
VERSION_TAG="$1"
52+
shift
53+
fi
54+
if [ -n "$1" ] && ! [[ "$1" =~ ^-- ]]; then
55+
GIT_HASH_ARG="$1"
56+
shift
57+
fi
58+
59+
# Parse optional flags
60+
while [[ "$#" -gt 0 ]]; do
61+
case $1 in
62+
--no-push) PUSH="false";;
63+
"") ;; # Ignore empty arguments, which can be passed by Cloud Build.
64+
*) echo "Unknown parameter passed: $1"; exit 1 ;;
65+
esac
66+
shift
67+
done
3868

3969
function docker_push {
40-
docker push "$image_with_version_tag"
41-
docker push "$image_with_stamp"
70+
if [ "$PUSH" == "true" ]; then
71+
docker push "$image_with_version_tag"
72+
docker push "$image_with_stamp"
73+
else
74+
echo "Skipping push for $image_with_version_tag."
75+
fi
4276
}
4377

4478
if [ -z "$GIT_HASH_ARG" ]; then
@@ -60,7 +94,7 @@ for image_name in "${IMAGES[@]}"; do
6094
if [ "$VERSION_TAG" == "latest" ]; then
6195
dockerfile="$image_dir/Dockerfile"
6296
else
63-
dockerfile="$image_dir/$VERSION_TAG.Dockerfile"
97+
dockerfile="$image_dir/${VERSION_TAG}.Dockerfile"
6498
fi
6599

66100
if [ ! -f "$dockerfile" ]; then
@@ -71,9 +105,27 @@ for image_name in "${IMAGES[@]}"; do
71105
image_with_version_tag="$image_name:$VERSION_TAG"
72106
image_with_stamp="$image_name:$stamp"
73107

108+
# Copy Pipfile to base for the ubuntu-24.04 build, as it's
109+
# needed but not in the build context.
110+
if [[ "$image_dir" == "base" && "$dockerfile" == *"ubuntu-24-04"* ]]; then
111+
NEEDS_ROOT_PIPFILE=true
112+
cp ../Pipfile ../Pipfile.lock base/
113+
fi
114+
74115
docker build -t "$image_with_version_tag" -f "$dockerfile" "$image_dir"
116+
117+
# Clean up the copied files.
118+
if [[ "$NEEDS_ROOT_PIPFILE" == "true" ]]; then
119+
rm base/Pipfile base/Pipfile.lock
120+
NEEDS_ROOT_PIPFILE=false
121+
fi
122+
75123
docker tag "$image_with_version_tag" "$image_with_stamp"
76124
docker_push
77125
done
78126

79-
echo "Built and pushed images successfully for version $VERSION_TAG with stamp $stamp"
127+
if [ "$PUSH" == "true" ]; then
128+
echo "Built and pushed images successfully for version $VERSION_TAG with stamp $stamp"
129+
else
130+
echo "Built images successfully (without push) for version $VERSION_TAG with stamp $stamp"
131+
fi

docker/chromium/base/ubuntu-24-04.Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ FROM gcr.io/clusterfuzz-images/base:ubuntu-24-04
1111
ENV UPDATE_WEB_TESTS True
1212

1313
# Note: snapcraft installation seems to always fail.
14+
RUN ln -s /usr/local/bin/python3.11 /usr/local/bin/python3
1415
RUN echo ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true | debconf-set-selections && curl 'https://chromium.googlesource.com/chromium/src/+/main/build/install-build-deps.py?format=TEXT' | base64 -d > /tmp/install-build-deps.py && sed -i s/snapcraft/doesnotexist/ /tmp/install-build-deps.py && sed -i "s/if requires_pinned_linux_libc():/if False:/" /tmp/install-build-deps.py && chmod u+x /tmp/install-build-deps.py && /tmp/install-build-deps.py --backwards-compatible --no-prompt --no-chromeos-fonts --syms --lib32
1516

1617

docker/cloudbuild.yaml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,38 @@
1515
steps:
1616
- name: 'gcr.io/cloud-builders/docker'
1717
id: 'Build latest'
18+
waitFor: ['-']
1819
entrypoint: bash
20+
dir: 'docker'
1921
args:
2022
- -ex
2123
- build.sh
2224
- latest
2325
- ${_GIT_HASH}
26+
- ${_PUSH_FLAG}
2427
- name: 'gcr.io/cloud-builders/docker'
2528
id: 'Build Ubuntu 20.04'
29+
waitFor: ['-']
2630
entrypoint: bash
31+
dir: 'docker'
2732
args:
2833
- -ex
2934
- build.sh
3035
- ubuntu-20-04
3136
- ${_GIT_HASH}
37+
- ${_PUSH_FLAG}
3238
- name: 'gcr.io/cloud-builders/docker'
3339
id: 'Build Ubuntu 24.04'
40+
waitFor: ['-']
3441
entrypoint: bash
42+
dir: 'docker'
3543
args:
3644
- -ex
3745
- build.sh
3846
- ubuntu-24-04
3947
- ${_GIT_HASH}
48+
- ${_PUSH_FLAG}
4049
timeout: 14400s
4150
options:
42-
machineType: N1_HIGHCPU_32
51+
machineType: E2_HIGHCPU_32
4352
diskSizeGb: 500

0 commit comments

Comments
 (0)