# Cloud Build and CI/CD on GCP

> Chapter 9 of the Courseiva GOOGLE-ACE curriculum — https://courseiva.com/learn/google-ace/gcp-cloud-build

**Official objective:** 3.2 — Deploy Implement

## Introduction

The 'Deploy and Implement' domain (Objective 3.2) includes Cloud Build, Google Cloud's fully managed CI/CD platform, and its role in automating software delivery on GCP. For the ACE exam, this topic appears in roughly 5-8% of questions, primarily in the 'Deploy and Implement' domain (Objective 3.2). You will be tested on Cloud Build triggers, build configurations, integration with source repositories, and deployment to GCP services like GKE, Cloud Run, and Compute Engine. Mastery of Cloud Build is essential for automating pipelines and ensuring repeatable, auditable deployments.

## Cloud Build as an Automated Assembly Line

Ten assembly stations, one plant manager, and a blueprint guide every car build at the plant. The plant manager (Cloud Build) receives an order (a code commit) to build a specific car model (your application). The order includes a blueprint (cloudbuild.yaml) that lists every step: weld the chassis (compile code), paint the body (run tests), install the engine (package artifacts), and test the brakes (deploy to staging). Each station on the assembly line is a build step, and the entire line is a build pipeline. The manager can trigger the line automatically whenever a new order arrives (GitHub push trigger), or manually for a special prototype (manual invocation). If a station fails (test failure), the line stops and the manager sends an alert (Cloud Build notification). The plant has a warehouse (Container Registry) where finished cars (Docker images) are stored. The manager can also run multiple lines in parallel for different models (build parallelism). Just as a car plant uses standardized tools and robots, Cloud Build uses pre-built or custom builder images to execute steps in a consistent environment. The manager tracks each car's build history (logs and artifacts) and can rerun a specific build from a previous step (build retry).

## Core explanation

### What is Cloud Build?

Cloud Build is a fully managed continuous integration and continuous delivery (CI/CD) platform on Google Cloud. It executes build steps in a series of isolated, containerized environments. Each build step runs inside a Docker container, and you can use pre-built images (e.g., `gcr.io/cloud-builders/gcloud`, `gcr.io/cloud-builders/docker`) or custom images from any registry. Cloud Build can import source code from Cloud Storage, Cloud Source Repositories, GitHub, Bitbucket, or GitLab. It can produce artifacts such as Docker images, Java JARs, or arbitrary files, and store them in Container Registry, Artifact Registry, or Cloud Storage.

### How Cloud Build Works Internally

When you submit a build, Cloud Build performs the following steps:
1. **Source Fetching**: Cloud Build downloads the source code from the specified repository or storage location. For GitHub or Bitbucket, you can configure a trigger that automatically starts a build when code is pushed or a pull request is created.
2. **Build Execution**: Cloud Build reads the build configuration file (by default `cloudbuild.yaml` or `cloudbuild.json`) and executes each step sequentially in a dedicated, ephemeral Docker container. Each step can specify:
   - `name`: The container image to use (e.g., `gcr.io/cloud-builders/gcloud`).
   - `entrypoint`: Override the container's default entrypoint.
   - `args`: Arguments to pass to the entrypoint.
   - `env`: Environment variables.
   - `dir`: Working directory relative to `/workspace`.
   - `id`: A unique identifier for the step.
   - `waitFor`: A list of step IDs that must complete before this step runs (default: previous step). Use `['-']` to run in parallel.
3. **Artifact Storage**: After the build steps complete, Cloud Build can store artifacts (e.g., built binaries, Docker images) in Container Registry, Artifact Registry, or Cloud Storage. You can also push Docker images automatically using `docker push` or the `gcr.io/cloud-builders/docker` builder.
4. **Logging and Notifications**: Cloud Build streams logs to Cloud Logging and can send notifications via Pub/Sub, Cloud Functions, or Slack webhooks. Build status can be viewed in the Console or via `gcloud builds describe`.

### Build Configuration File Structure

The `cloudbuild.yaml` file has the following key sections:
- `steps`: Required. An array of build step objects.
- `images`: Optional. A list of Docker image names to build and push (e.g., `['gcr.io/$PROJECT_ID/my-image']`). Cloud Build will execute `docker build` and `docker push` automatically if this is provided, but you can also do it manually in a step.
- `artifacts`: Optional. Specify objects to store in Cloud Storage after the build.
- `options`: Optional. Configure logging, machine type, substitution variables, etc.
- `timeout`: Optional. Maximum build duration (default 10 minutes, max 24 hours).
- `tags`: Optional. Labels for the build.
- `substitutions`: Optional. Define custom variable substitutions.

### Key Features for the ACE Exam

- **Triggers**: You can create build triggers that automatically start a build when code is pushed to a branch, a tag is created, or a pull request is opened. Triggers are configured per repository and can filter by branch/tag regex. They support GitHub, Bitbucket, GitLab, and Cloud Source Repositories.
- **Substitution Variables**: Use built-in variables like `$PROJECT_ID`, `$BUILD_ID`, `$REPO_NAME`, `$BRANCH_NAME`, `$TAG_NAME`, `$COMMIT_SHA`, `$SHORT_SHA`. You can define custom variables in the trigger or in the build config.
- **Parallel Steps**: Use `waitFor: ['-']` to run steps in parallel. By default, steps run sequentially, each waiting for the previous step to complete.
- **Custom Builders**: You can create custom builder images to run tools not available in the default set. Publish them to Container Registry and reference them in steps.
- **Private Pools**: For builds that need to access VPC resources, use private pools to run builds in a specific VPC network. This allows Cloud Build to reach private IP addresses (e.g., on-premises resources, private GKE clusters). Private pools are provisioned with a specific machine type (e.g., e2-standard-2) and scale to zero when idle.
- **IAM Roles**: Key roles include `cloudbuild.builds.builder` (allows submitting builds), `cloudbuild.builds.viewer` (view builds), and `cloudbuild.builds.approver` (approve builds requiring approval). For triggers, use `cloudbuild.triggers.create`, `cloudbuild.triggers.update`, etc.
- **Build Approvals**: You can require manual approval before a build can proceed. This is configured in the trigger settings. A user with `cloudbuild.builds.approver` role must approve the build via Console or `gcloud builds approve`.

### Common Commands

- `gcloud builds submit`: Submit a build from the current directory or a specified source. Example:
  ```bash
  gcloud builds submit --tag gcr.io/$PROJECT_ID/my-image
  ```
  This command uploads the source, executes the build steps defined in `cloudbuild.yaml` (or uses a default Docker build if no config is found), and pushes the image.
- `gcloud builds list`: List recent builds.
- `gcloud builds describe BUILD_ID`: Get details of a specific build.
- `gcloud builds log BUILD_ID`: Stream logs for a build.
- `gcloud builds triggers create ...`: Create a trigger (e.g., for GitHub, Bitbucket, Cloud Source Repositories).
- `gcloud builds triggers list`: List triggers.
- `gcloud builds triggers run TRIGGER_ID --branch=BRANCH`: Manually run a trigger.

### Integration with Other GCP Services

- **Cloud Run**: Deploy a container image built by Cloud Build directly to Cloud Run using the `gcr.io/cloud-builders/gcloud` builder:
  ```yaml
  steps:
  - name: 'gcr.io/cloud-builders/gcloud'
    args: ['run', 'deploy', 'my-service', '--image=gcr.io/$PROJECT_ID/my-image', '--region=us-central1', '--platform=managed']
  ```
- **GKE**: Deploy to a GKE cluster using `kubectl` or the `gcr.io/cloud-builders/kubectl` builder. You must ensure the Cloud Build service account has the necessary IAM permissions to access the cluster.
- **Compute Engine**: Deploy by copying artifacts to a VM via SSH or using `gcloud compute ssh`.
- **Firebase**: Use the `gcr.io/cloud-builders/firebase` builder to deploy to Firebase Hosting or Cloud Functions.
- **Cloud Functions**: Use `gcloud functions deploy` in a build step.

### Default Values and Timers

- **Default timeout**: 10 minutes. Max 24 hours.
- **Default machine type**: `e2-standard-2` for standard builds, `n1-highcpu-8` for larger builds. Private pools can use different machine types.
- **Logs bucket**: Build logs are stored in Cloud Storage at `gs://PROJECT_ID.cloudbuild-logs.googleusercontent.com/`.
- **Source size limit**: 50 GB per build.
- **Concurrent builds**: By default, up to 10 builds per region per project (can be increased via quota request).

## Real-world context

### Enterprise Scenario 1: Multi-Environment Microservices Deployment

A large e-commerce company uses Cloud Build to deploy over 50 microservices to Google Kubernetes Engine (GKE) across dev, staging, and production environments. Each service has its own `cloudbuild.yaml` that builds a Docker image, runs unit and integration tests, and then deploys to the appropriate GKE cluster using the `gcr.io/cloud-builders/kubectl` builder. They use Cloud Build triggers connected to a GitHub repository; pushes to `main` trigger a build that deploys to staging, while tags like `v*` trigger production deployments. To manage secrets (e.g., database passwords), they use Cloud KMS encryption and decrypt them in build steps using the `gcr.io/cloud-builders/gcloud` builder with the `kms decrypt` command. A common pitfall is forgetting to grant the Cloud Build service account the `container.deployer` role on the GKE cluster, causing deployment failures. They also set up Pub/Sub notifications to send build status to a Slack channel for real-time visibility.

### Enterprise Scenario 2: Cross-Project CI/CD with Artifact Registry

A financial services firm uses Cloud Build to build and deploy applications across multiple GCP projects (one per environment). They use Artifact Registry for storing Docker images centrally in a shared project. The build pipeline compiles Java code, runs SonarQube analysis, builds a Docker image, pushes it to Artifact Registry, and then deploys to Cloud Run in the target project. They use Cloud Build's `--substitutions` flag to pass environment-specific variables (e.g., `_ENV=prod`). A key challenge is managing IAM permissions: the Cloud Build service account in the source project must have `artifactregistry.writer` on the registry, and the deployment project must allow the service account to invoke Cloud Run services. They also use Cloud Build's private pools to access a VPC-hosted SonarQube server, avoiding public internet exposure. A common misconfiguration is setting the private pool network incorrectly, causing build steps to fail with network timeouts.

### Scenario 3: Serverless CI/CD for Cloud Functions

A startup uses Cloud Build to deploy Python Cloud Functions triggered by Cloud Storage events. Their `cloudbuild.yaml` runs linting, unit tests, and then deploys using `gcloud functions deploy`. They use a trigger on a Cloud Source Repository; any commit to the `functions` branch triggers a build. They discovered that the default Cloud Build service account lacks `cloudfunctions.functions.create` permission, so they had to grant it explicitly. They also use Cloud Build's `--timeout` flag to set a 30-minute timeout because some deployments involve large dependencies. A common mistake is not using a `.gcloudignore` file, causing large node_modules or __pycache__ directories to be uploaded, slowing the build and exceeding the 50 GB source limit.

## Exam focus

### What the ACE Exam Tests on Cloud Build

The ACE exam (Objective 3.2) focuses on your ability to set up and manage CI/CD pipelines using Cloud Build. Key areas include:
- **Build Configuration**: Understanding the structure of `cloudbuild.yaml`, including `steps`, `images`, `artifacts`, `timeout`, and `options`. You may be asked to identify the correct syntax or spot errors.
- **Triggers**: Creating triggers for GitHub, Bitbucket, and Cloud Source Repositories. Know how to filter by branch or tag using regex.
- **IAM Roles**: The difference between `cloudbuild.builds.builder`, `cloudbuild.builds.viewer`, and `cloudbuild.builds.approver`. The exam often tests which role is needed to submit a build vs. view logs.
- **Integration with GCP Services**: Deploying to Cloud Run, GKE, Compute Engine, and Cloud Functions. You must know the correct builder images and commands (e.g., `gcloud run deploy`, `kubectl apply`).
- **Substitution Variables**: Built-in variables like `$PROJECT_ID`, `$BUILD_ID`, `$BRANCH_NAME`, `$COMMIT_SHA`, `$SHORT_SHA`. The exam may ask which variable to use for a unique build identifier.
- **Private Pools**: When to use private pools (e.g., to access VPC resources) and how to configure them.
- **Build Approvals**: How manual approval works and which role is required.

### Common Wrong Answers and Traps

1. **Wrong: Cloud Build can only build Docker images.** Reality: Cloud Build can run arbitrary build steps using any container image. It is not limited to Docker builds; you can compile code, run tests, or deploy using any tool.
2. **Wrong: The `images` section automatically pushes images to Container Registry.** Reality: The `images` section only specifies image names; you must still run `docker build` in a step (or use the default builder). Cloud Build will then automatically push the images if the build succeeds.
3. **Wrong: You must use `gcloud builds submit` to trigger a build from a trigger.** Reality: Triggers automatically start builds based on repository events. `gcloud builds submit` is for manual builds.
4. **Wrong: Cloud Build can only access public repositories.** Reality: Cloud Build can access private GitHub, Bitbucket, and GitLab repositories by connecting via OAuth or using SSH keys.
5. **Wrong: Build logs are stored indefinitely.** Reality: Build logs are stored in Cloud Storage and have a retention period (default 30 days). You can change the retention policy.

### Specific Numbers and Values

- Default timeout: 10 minutes
- Max timeout: 24 hours
- Default machine type: e2-standard-2
- Source size limit: 50 GB
- Concurrent builds per region: 10 (default)
- Built-in substitution variables: `$PROJECT_ID`, `$BUILD_ID`, `$REPO_NAME`, `$BRANCH_NAME`, `$TAG_NAME`, `$COMMIT_SHA`, `$SHORT_SHA`
- Default log bucket: `gs://PROJECT_ID.cloudbuild-logs.googleusercontent.com/`

### Edge Cases

- **Build cancellation**: If a build is cancelled, partial artifacts are not pushed. Logs up to the cancellation point are retained.
- **Parallel step failure**: If one step in a parallel group fails, Cloud Build does not automatically cancel other steps in the same group.
- **Substitutions in step names**: Substitution variables are resolved before step execution, so you can use them in `args`, `env`, and `dir`.
- **`.gcloudignore`**: If you don't include a `.gcloudignore` file, Cloud Build will upload all files in the source directory, which might include large unnecessary files. The default `.gcloudignore` excludes common patterns like `.git`, `node_modules`, and `.pyc`.

## Step by step

1. **Create a cloudbuild.yaml File** — Define the build steps in a YAML or JSON file. Each step specifies a container image, entrypoint, and arguments. Use the `steps` array to list sequential or parallel steps. Include optional `images` and `artifacts` sections. The file must be in the root of your source directory or specified with `--config` in the `gcloud builds submit` command.
2. **Submit the Build to Cloud Build** — Use `gcloud builds submit` to upload the source code and trigger the build. Cloud Build compresses the source (excluding files listed in `.gcloudignore`), uploads it to Cloud Storage, and then executes the steps. You can also submit a build manually via the Console or using a trigger.
3. **Execute Build Steps Sequentially** — Cloud Build runs each step in a separate Docker container. The working directory is `/workspace`, which is shared across all steps. Steps run in order unless `waitFor` is used to run in parallel. Each step can access environment variables, including built-in substitutions like `$PROJECT_ID` and `$BUILD_ID`.
4. **Store Artifacts and Push Images** — After the build steps, Cloud Build can automatically push Docker images to Container Registry or Artifact Registry if specified in the `images` section. It can also upload arbitrary files to Cloud Storage using the `artifacts` section. You can also explicitly run `docker push` or `gsutil cp` in a step.
5. **View Build Logs and Status** — Build logs are streamed to Cloud Logging and stored in Cloud Storage. You can view them in the Console under Cloud Build > History, or use `gcloud builds log BUILD_ID`. Build status can be checked with `gcloud builds describe BUILD_ID`. Notifications can be sent via Pub/Sub or webhooks.

## Comparisons

### Cloud Build vs Jenkins on Compute Engine

**Cloud Build:**
- Fully managed, no infrastructure to maintain
- Automatic scaling and high availability
- Integrated with GCP services natively (Artifact Registry, Cloud Run, GKE)
- Pay per build minute; no cost when idle
- Limited to 10 concurrent builds by default (can increase)

**Jenkins on Compute Engine:**
- Self-managed, requires VM and storage provisioning
- Manual scaling; can be complex to achieve HA
- Requires plugins for GCP integration
- Pay for VM 24/7 regardless of build usage
- Highly customizable with plugins and shared libraries

### Cloud Build with Cloud Run vs Cloud Build with GKE

**Cloud Build with Cloud Run:**
- Simpler deployment: `gcloud run deploy`
- No cluster management; automatically scales to zero
- Ideal for stateless HTTP services
- Limited to container-based applications
- Supports traffic splitting and revisions

**Cloud Build with GKE:**
- More complex: requires kubectl and cluster credentials
- Full control over Kubernetes features (e.g., StatefulSets, CronJobs)
- Can run stateful workloads with persistent volumes
- Supports any containerized application
- Requires cluster management and node scaling

## Common misconceptions

- **Misconception:** Cloud Build only works with Docker containers. **Reality:** Cloud Build can execute any build step inside a container, but the steps themselves can run any command (e.g., compile C++, run shell scripts). The output can be any artifact, not just Docker images.
- **Misconception:** The `images` field in cloudbuild.yaml automatically builds the Docker image. **Reality:** The `images` field only specifies which images to push. You must still include a step that runs `docker build` (or use the implicit Docker build when no config file is provided).
- **Misconception:** Cloud Build triggers can only be created for Cloud Source Repositories. **Reality:** Triggers support GitHub, Bitbucket, GitLab, and Cloud Source Repositories. You can connect any of these by using the Cloud Build app or mirrored repositories.
- **Misconception:** Build steps always run sequentially. **Reality:** By default, steps run sequentially, but you can use `waitFor: ['-']` to run steps in parallel. You can also specify custom dependencies with `waitFor: ['step-id']`.
- **Misconception:** Cloud Build service account has all necessary permissions by default. **Reality:** The default Cloud Build service account (`[PROJECT_NUMBER]@cloudbuild.gserviceaccount.com`) has limited permissions. You must grant additional IAM roles (e.g., `container.deployer`, `run.admin`) for deployment steps.

## Key takeaways

- Cloud Build is a fully managed CI/CD platform that executes build steps in containerized environments.
- Build configuration is defined in `cloudbuild.yaml` with `steps`, `images`, `artifacts`, and `timeout`.
- Default build timeout is 10 minutes; maximum is 24 hours.
- Triggers automatically start builds on code push to GitHub, Bitbucket, GitLab, or Cloud Source Repositories.
- Use built-in substitution variables like `$PROJECT_ID`, `$BUILD_ID`, `$BRANCH_NAME`, and `$SHORT_SHA`.
- Cloud Build integrates with Cloud Run, GKE, Compute Engine, Cloud Functions, and Firebase.
- Private pools allow builds to access VPC resources; they scale to zero when idle.
- IAM roles: `cloudbuild.builds.builder` to submit builds, `cloudbuild.builds.viewer` to view, and `cloudbuild.builds.approver` to approve builds.
- Build logs are stored in Cloud Storage at `gs://PROJECT_ID.cloudbuild-logs.googleusercontent.com/` with 30-day retention.
- Parallel steps can be defined using `waitFor: ['-']` in the step configuration.

## FAQ

**How do I deploy a container image built by Cloud Build to Cloud Run?**

Use the `gcr.io/cloud-builders/gcloud` builder in a step with the `run deploy` command. For example: `steps: - name: 'gcr.io/cloud-builders/gcloud' args: ['run', 'deploy', 'my-service', '--image=gcr.io/$PROJECT_ID/my-image', '--region=us-central1', '--platform=managed']`. Ensure the Cloud Build service account has the `run.admin` IAM role on the project.

**Can Cloud Build trigger builds from private GitHub repositories?**

Yes. You can connect Cloud Build to a private GitHub repository by installing the Google Cloud Build app in your GitHub account or by mirroring the repository to Cloud Source Repositories. For the app method, you authorize Cloud Build to access your GitHub account and select specific repositories. Cloud Build will then create a webhook that triggers builds on push events.

**What is the difference between `gcloud builds submit` and a trigger?**

`gcloud builds submit` is a manual command that uploads source code from your local machine or a Cloud Storage bucket and starts a build immediately. A trigger is a configuration that automatically starts a build when an event occurs in a connected repository (e.g., push to a branch, creation of a tag). Triggers are ideal for automation; `gcloud builds submit` is used for one-off builds or testing.

**How do I pass environment variables to a Cloud Build step?**

Use the `env` field in the step definition. Example: `steps: - name: 'gcr.io/cloud-builders/gcloud' env: - 'MY_VAR=hello' args: ['echo', '$MY_VAR']`. You can also use substitution variables like `$PROJECT_ID` directly in args. For sensitive data, use Cloud KMS to encrypt and decrypt the variable in a step.

**Why is my Cloud Build step failing with a permission error when deploying to GKE?**

The Cloud Build service account (`[PROJECT_NUMBER]@cloudbuild.gserviceaccount.com`) needs the `container.developer` IAM role on the GKE cluster to deploy resources. Grant this role via the GKE cluster's IAM page or using `gcloud projects add-iam-policy-binding`. Also ensure the service account has `container.cluster.getCredentials` permission to fetch the kubeconfig.

**What is a Cloud Build private pool and when should I use it?**

A private pool is a set of worker machines that run in your VPC network, allowing build steps to access private IP resources (e.g., on-premises databases, private GKE clusters). Use private pools when your build needs to interact with resources that are not publicly accessible. Private pools scale to zero when idle and can be configured with specific machine types and network settings.

**How do I store build artifacts in Cloud Storage?**

Use the `artifacts` section in your `cloudbuild.yaml`. Example: `artifacts: objects: location: 'gs://my-bucket/artifacts/' paths: ['build/output/*.jar']`. This will upload files matching the paths to the specified Cloud Storage location after the build succeeds. You can also use `gsutil cp` in a step for more control.

---

Interactive version with quiz and diagrams: https://courseiva.com/learn/google-ace/gcp-cloud-build
