Skip to content

Cloud Build: Execution Model & Architecture

Tại sao lại quan trọng

Khi bạn submit một build đến Cloud Build, nó không chỉ "chạy command của bạn trên một server nào đó". Cloud Build là một distributed build system được thiết kế để vừa an toàn, vừa scalable, vừa auditable. Nếu bạn không hiểu cách nó thực sự hoạt động bên trong, bạn sẽ gặp phải:

  • Bí ẩn về state: Tại sao step này không thấy output của step trước? (giải pháp là hiểu network model)
  • Performance surprises: Tại sao build của tôi bị slow, hay sudden timeouts? (thường là ephemeral environment chưa warm)
  • Debugging nightmares: Container của bạn crash ở bước 3 nhưng error message mơ hồ — không có cách để SSH vào và investigate live
  • Scaling problems: Khi build volume tăng, bạn không hiểu tại sao queue lại backing up

Chương này sẽ dạy bạn mental model chính xác của Cloud Build execution, từ đó bạn sẽ dễ dàng design pipelines hiệu quả và debug failures nhanh chóng.


Internal Model: Cách Cloud Build Vận Hành

Cloud Build là gì?

Cloud Build là một managed CI/CD service trên GCP, nhưng nó không phải là những gì nhiều người tưởng. Nó không phải là một "server chạy mãi mãi" chạy các jobs của bạn — nó là một stateless, ephemeral build orchestrator mà:

  1. Nhận build request (thông qua trigger hoặc API call)
  2. Provisioning một worker (một VM mới) hoặc sử dụng available capacity từ shared default pool
  3. Execute các build steps trong sequence
  4. Capture outputs (logs, images, artifacts)
  5. Destroy worker (hoặc return nó về pool)

Toàn bộ quá trình này được thiết kế để immutable, auditable, và ephemeral.

Build Steps: Mô hình chính

Mỗi Cloud Build được định nghĩa là một sequence của build steps trong cloudbuild.yaml. Ví dụ:

yaml
steps:
  - name: gcr.io/cloud-builders/git
    args: ['clone', 'https://...']
  - name: gcr.io/cloud-builders/docker
    args: ['build', '-t', 'gcr.io/$PROJECT_ID/app:$COMMIT_SHA', '.']
  - name: gcr.io/cloud-builders/docker
    args: ['push', 'gcr.io/$PROJECT_ID/app:$COMMIT_SHA']

Thực tế hoạt động như thế nào:

Mỗi step là một Docker container execution. Cloud Build:

  1. Spins up container từ image được chỉ định (ví dụ: gcr.io/cloud-builders/docker)
  2. Mounts shared volume (workspace) vào container
  3. Executes command/args trong context của container đó
  4. Captures stdout/stderr để logging
  5. Waits for exit code (0 = success, non-zero = failure)
  6. Moves to next step nếu previous step thành công

Điểm quan trọng: Mỗi step chạy trong một container riêng, nhưng chúng chia sẻ cùng một workspace volume. Điều này nghĩa là:

  • Step 1 tạo file output.txt → file này visible cho step 2, 3, 4, ...
  • Nếu step 1 install dependencies (ví dụ: npm install), step 2 sẽ nhìn thấy node_modules/ folder
  • Nhưng environment variables không chia sẻ — mỗi step là một process mới

Workers: Ephemeral Build Execution Environments

Đây là yếu tố critical mà nhiều people không hiểu rõ.

Default Pool Architecture:

Khi bạn submit một build, Cloud Build:

  1. Picks available worker từ shared default pool (hoặc waits nếu tất cả busy)
  2. Fresh VM provisioned — thường là Linux VM với Docker daemon sẵn sàng
  3. Workspace initialized — một ephemeral GCS bucket hoặc local disk được chuẩn bị
  4. Steps executed — lần lượt trong order
  5. Artifacts captured — logs written to Cloud Logging, images pushed to registry
  6. VM destroyed — sau khi build xong, worker được destroy hoặc reset

Tại sao ephemeral?

  • Security: Mỗi build chạy trong isolated environment, không có state từ previous builds
  • Failure isolation: Nếu step 1 crash, step 2 không execute (fail-fast model)
  • Resource efficiency: Cloud Build không maintain idle workers — chỉ spin up khi cần

Worker capacity trong default pool:

Theo Google Cloud documentation, default pool có:

  • Max 30 concurrent builds per project (mặc định)
  • Machine types limited — thường là e2-standard machines
  • No custom networking — worker chạy trên Google's managed network, không connect vào customer VPC

Nếu bạn cần private networking, higher concurrency, hoặc custom machine types, bạn phải dùng private worker pools (xem Chương 3).

Build Network Model: Isolation & Connectivity

Đây là part mà rất hay gây confusion.

Default Pool Network Topology:

Khi step chạy, nó chạy trong một container với:

  • Internal hostname: localhost (step có thể communicate với nhau qua localhost ports? Không, vì chúng chạy trong separate containers)
  • Shared volume: /workspace — nơi source code được clone và artifacts được lưu
  • Cloud Logging agent: Built-in, automatic capture logs
  • No persistent home directory — container filesystem là ephemeral

Connectivity to external services:

  • Public internet: Default pool CÓ access to public internet (Google maintains egress routes)
  • Cloud APIs: Fully accessible (built-in service account có IAM bindings)
  • Customer VPC: NO direct access (unless you use private worker pools with VPC peering)
  • Private databases behind VPC: Not accessible từ default pool

Đây là lý do tại sao private worker pools quan trọng — nếu bạn cần access private database hoặc internal services trong VPC, bạn không thể dùng default pool.

Build Storage: Workspace & Artifacts

Mỗi build có một ephemeral workspace:

Workspace location:

Khi bạn submit build, Google Cloud tạo một temporary directory (thường trên local disk của worker):

/workspace
├── (source code được clone vào đây)
├── .git/ (git history nếu source là từ Cloud Source Repos)
└── (artifacts được tạo ra ở đây)

Workspace này được automatically mounted vào mỗi step container. Điều này cho phép steps chia sẻ files.

Artifact persistence:

Sau khi build xong, artifacts phải được explicitly pushed đến persistent storage:

  • Docker images: docker push đến Artifact Registry hoặc Container Registry
  • Build outputs: gsutil cp đến GCS bucket
  • Logs: Automatically captured by Cloud Logging

Nếu bạn KHÔNG explicitly push artifacts, chúng sẽ mất mãi sau khi build complete. Workspace là ephemeral.

Build Metadata & Environment Variables

Cloud Build injects built-in substitutions vào mỗi build:

yaml
substitutions:
  _SERVICE_ACCOUNT: "cloud-builds@${PROJECT_ID}.iam.gserviceaccount.com"
  
steps:
  - name: 'gcr.io/cloud-builders/docker'
    args: 
      - 'build'
      - '-t'
      - 'gcr.io/${PROJECT_ID}/app:${SHORT_SHA}'
      - '.'

Built-in substitutions:

  • ${PROJECT_ID}: GCP project ID
  • ${BUILD_ID}: Unique build ID
  • ${COMMIT_SHA}: Full commit hash (nếu trigger từ repo)
  • ${SHORT_SHA}: First 7 chars của commit hash
  • ${BRANCH_NAME}: Git branch name
  • ${REPO_NAME}: Repository name

Những variables này automatically populated bởi Cloud Build base trên trigger source. Nếu bạn define custom substitutions, bạn cần pass chúng qua gcloud builds submit --substitutions=KEY=VALUE.

Build Execution Guarantees & Failure Handling

Cloud Build có clear execution model:

  1. Sequential execution: Steps chạy theo thứ tự (step 1, sau đó step 2, ...)
  2. Fail-fast: Nếu step N fail (non-zero exit code), build stop tại đây. Step N+1 không execute
  3. No retry by default: Nếu step fail, build FAIL. Không auto-retry
  4. All-or-nothing per step: Step execute toàn bộ hoặc không — không có partial execution

Nếu bạn muốn conditional execution (ví dụ: run step A nếu step B fail), bạn phải dùng workaround — ví dụ: bash script check exit code và decide:

yaml
steps:
  - name: 'gcr.io/cloud-builders/gke-deploy'
    args: ['run', '--filename=k8s/', ...]
    # nếu step này fail, step kế tiếp không execute
  - name: 'gcr.io/cloud-builders/git'
    args: ['log', '--oneline', '-1']  # This won't run if previous step failed

Để implement "run on failure" logic, bạn phải dùng shell script wrapper:

yaml
steps:
  - name: 'gcr.io/cloud-builders/docker'
    entrypoint: 'bash'
    args:
      - '-c'
      - |
        docker build . || echo "Build failed, but continuing..."

Build Timeout & Resource Limits

Mỗi build có default timeout của 600 seconds (10 phút). Nếu build vẫn chạy sau 10 phút, Cloud Build sẽ kill nó và mark as FAILURE.

Bạn có thể override timeout:

yaml
timeout: '3600s'  # 1 hour

Resource constraints:

  • Default pool: Machine type được fixed, không thể customize
  • Private pools: Bạn có thể chọn machine type (e2-standard, n2-standard, n2d-standard, ...)
  • Memory: Tùy machine type (e2-standard-2 có 8 GB RAM, ...)
  • Disk: Worker node có enough disk space cho workspace, nhưng không unbounded

Nếu build của bạn lại touch disk limit hoặc memory limit, nó sẽ được kill (thường với cryptic error message).

Build Logging & Observability

Cloud Build automatically logs tất cả output từ steps:

  • stdout/stderr: Captured và written to Cloud Logging
  • Build metadata: Build ID, trigger source, service account, start time, end time
  • Step-level logs: Mỗi step có separate log stream

Bạn có thể query logs:

bash
gcloud builds log BUILD_ID  # View logs in terminal

Hoặc trong Cloud Console → Cloud Build → Build history → Click vào build ID → Logs tab.

Logging limitations:

  • Logs retained for 30 days by default (configurable via Cloud Logging retention)
  • Large logs (>100 MB) có thể bị truncated ở Cloud Console UI
  • Nhưng full logs luôn available via API / Cloud Logging

Mental Model Summary

Cloud Build Execution:

submit build

pick available worker from pool (or wait)

provision fresh VM / container environment

initialize workspace (empty or with source code)

FOR EACH step:
    ├── Spin up container từ step image
    ├── Mount shared /workspace volume
    ├── Execute command/args
    ├── Capture logs
    └── Check exit code (0 = success, non-zero = fail build)

capture outputs (push to registry, write to GCS, ...)

destroy worker / reset environment

return build result (SUCCESS, FAILURE, TIMEOUT)

Key constraints:

  1. Ephemeral: Mỗi build chạy clean, không share state với previous builds
  2. Isolated: Mỗi step là separate container, nhưng share workspace volume
  3. Sequential: Steps chạy một sau một
  4. Fail-fast: Build stop ở step đầu tiên fail
  5. Timeoutted: Max 600s by default
  6. Logged: Tất cả output captured to Cloud Logging

Constraints & Failure Modes

Resource Bottlenecks

Default pool saturation:

Nếu bạn có 20 concurrent build submissions nhưng default pool chỉ support 30 concurrent builds, builds mới sẽ queue và wait cho available worker. Queueing time có thể từ vài giây đến vài phút tùy queue depth.

Mitigation:

  • Dùng private worker pools nếu cần higher concurrency
  • Schedule builds strategically nếu possible (không chạy all tests every minute)
  • Monitor build queue qua Cloud Build metrics → set up alerts nếu queue depth > threshold

Networking Limitations in Default Pool

You cannot:

  • Access customer VPC resources (databases, caches, internal services)
  • Use static IPs (Cloud Build assigns dynamic public IPs)
  • Guarantee outbound IP address (NAT happens through Google's shared IPs)

Workaround:

  • Move sensitive operations thành separate GCP services (Cloud Run, GKE) — Cloud Build triggers chúng từ default pool (public accessible), nhưng sensitive work happen trong VPC
  • Dùng private worker pools nếu absolutely need VPC access

Build Failure Debugging

Khi build fail, investigation model:

  1. Check Cloud Build UI → Build logs tab
  2. Search for error messages ở tail của logs
  3. If error cryptic, reproduce locally:
    bash
    # Clone same source
    git clone <repo>
    git checkout <commit>
    # Run same build steps locally
    docker build -t test .
  4. If still unclear, enable Cloud Build debug (set --verbosity=debug nếu dùng CLI)

Limitation: Bạn không thể SSH vào running worker — nó ephemeral, không có persistent identity. Nếu bạn perlu debug interactively, bạn phải:

  • Modify build config để run một shell loop thay vì exit immediately
  • Capture logs more verbosely (set set -x ở bash scripts)

Build timeout thường happen vì:

  1. Network timeout: Docker pull từ slow registry, hoặc download large dependencies
  2. Build step hanging: Step bị stuck waiting for I/O, lock, hoặc network response
  3. Insufficient resources: Machine out of disk space, runs out of memory

Mitigations:

  • Increase timeout (nếu legitimate long-running step):
    yaml
    timeout: '1800s'  # 30 minutes
  • Use caching (xem Chương 5-6) để skip redundant work
  • Parallelize build steps (nếu possible) bằng cách dùng multiple parallel jobs

Real-world Implication

Case: Slow Docker Builds

Scenario: Bạn có build step docker build . mà takes 5 phút. Tại sao?

Possible causes:

  1. No layer caching: Nếu bạn build ở default pool lần đầu, Docker layers không cached. Lần đầu build từ scratch takes much longer
  2. Large base image: Pulling ubuntu:22.04 từ Docker Hub takes time
  3. Slow package managers: apt-get install ở default pool chạy slow vì network
  4. Compilation steps: C++ / Go compilation step bị slow

Solutions:

  • Use Dockerfile best practices: Put slow layers sau fast layers, chia RUN commands efficiently
  • Dùng pre-built base images từ Artifact Registry (close to Cloud Build workers)
  • Cache layers ở Artifact Registry: docker build --cache-from gcr.io/$PROJECT_ID/app:latest

References