← Back to posts

GCP ACE: Containers, Kubernetes, and Building Images with Cloud Build

Google Kubernetes Engine shows up across the Associate Cloud Engineer exam’s deployment and operations domains, but GKE itself rests on two ideas you need solid first: what a container is, and what problem Kubernetes solves once you have more than a handful of them. This article covers both, then walks through building a container image with Cloud Build — the step that normally comes right before a GKE deployment.

Containers vs. virtual machines

A container packages application code together with its runtime, libraries, and configuration into a single unit that runs the same way regardless of the host. Unlike a VM, a container doesn’t carry its own kernel — it shares the host machine’s kernel and gets its isolation from two Linux kernel features:

ContainersVirtual Machines
VirtualizesThe operating systemThe hardware
Guest OSShares the host kernelRuns a full guest OS per VM
SizeLightweight (MBs)Heavier (GBs)
IsolationLogical, at the OS levelHardware-level
Startup timeSeconds or lessMinutes

An image is the read-only template a container is created from — your filesystem plus metadata (entrypoint, exposed ports, environment variables). Each instruction in a Dockerfile produces a cacheable, read-only layer; a running container adds one writable layer on top. Ordering a Dockerfile so rarely-changing steps (installing dependencies) come before frequently-changing ones (copying application code) keeps builds fast, since Docker reuses cached layers that haven’t changed:

FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
# Cached unless requirements.txt changes
RUN pip install -r requirements.txt
# Changes most often, so it goes last
COPY . .
CMD ["python", "app.py"]

Why Kubernetes

Running one container is trivial. Running hundreds across multiple machines, keeping them healthy, routing traffic to them, and rolling out updates without downtime is not — that’s the problem Kubernetes solves. It’s a portable, open-source platform for managing containerized workloads through declarative configuration: you describe the state you want (3 replicas of this image, reachable on port 80), and Kubernetes’ control loop continuously reconciles the actual state to match it.

The objects you’ll see repeatedly on the exam:

ObjectPurpose
PodSmallest deployable unit — one or more containers sharing network and storage
DeploymentManages a set of identical Pods; handles rolling updates and self-healing
ServiceStable network endpoint and load balancing for a set of Pods
ConfigMapNon-sensitive configuration injected into Pods
SecretSensitive data (credentials, tokens) injected into Pods
Namespace (Kubernetes, not Linux)Logical partition of a cluster — isolates resources between teams or environments

A minimal Deployment manifest:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: nginx
        image: nginx:1.27
        ports:
        - containerPort: 80
        readinessProbe:
          httpGet:
            path: /
            port: 80
kubectl apply -f deployment.yaml      # declare desired state
kubectl get pods -l app=web           # check actual state
kubectl scale deployment web --replicas=5

If a Pod crashes, the Deployment’s controller replaces it automatically — that’s the self-healing behavior the exam expects you to recognize. Google Kubernetes Engine (GKE) is Google Cloud’s managed implementation of this platform: Google runs and upgrades the control plane, and (depending on the mode — covered in the next article) can manage the worker nodes too.

Building a container image with Cloud Build

Before a container can run on GKE, it has to exist as an image in a registry. Cloud Build is Google Cloud’s managed CI/CD service for building, testing, and pushing images — typically to Artifact Registry.

Image URLs for Artifact Registry follow this exact format:

LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE_NAME

Option 1 — Dockerfile only, no config file

Run from the directory containing your source and Dockerfile:

gcloud builds submit --tag europe-southwest1-docker.pkg.dev/my-project/my-repo/my-app

Option 2 — a cloudbuild.yaml build config

steps:
- name: 'gcr.io/cloud-builders/docker'
  args: ['build', '-t', 'europe-southwest1-docker.pkg.dev/my-project/my-repo/my-app', '.']
- name: 'gcr.io/cloud-builders/docker'
  args: ['push', 'europe-southwest1-docker.pkg.dev/my-project/my-repo/my-app']
images: ['europe-southwest1-docker.pkg.dev/my-project/my-repo/my-app']
gcloud builds submit --config cloudbuild.yaml .

Two ways to push the built image matter for the exam:

MethodWhen the image is pushedShows up in build results
Top-level images: fieldAfter the build completesYes — visible via console, Build.get(), gcloud builds list
A docker push stepMid-buildNo
Both togetherMid-build, and recordedYes

Option 3 — Buildpacks, no Dockerfile needed

gcloud builds submit --pack image=europe-southwest1-docker.pkg.dev/my-project/my-repo/my-app

If you don’t specify a builder, Cloud Build defaults to gcr.io/buildpacks/builder.

Running the image locally

One-time Docker credential setup for your Artifact Registry host:

gcloud auth configure-docker europe-southwest1-docker.pkg.dev
docker run europe-southwest1-docker.pkg.dev/my-project/my-repo/my-app

Exam Takeaways

Sources

↗ LinkedIn