DevOps on Google Cloud, part 2: Compute — Cloud Run, GKE, and Cloud Functions
Part 2from the DevOps on Google Cloud series · 6 parts in all
Part 2: where code runs on GCP. This is the cloud where "just give me a container and an HTTPS endpoint" is a first-class product rather than a bolt-on — Cloud Run — and where Kubernetes itself was born (GKE). Our companies split across all three tiers.
Cloud Run: the PaaS that respects containers
Deploy any container listening on $PORT; get HTTPS, autoscaling (including to
zero), and request-based billing. No cluster to create, no YAML to worship:
# Build with Cloud Build (or Docker locally) and deploy in one command
gcloud builds submit --tag us-central1-docker.pkg.dev/maplecart/web/web:v42
gcloud run deploy maplecart-web \
--image us-central1-docker.pkg.dev/maplecart/web/web:v42 \
--region us-central1 \
--service-account [email protected] \ # identity, not keys
--set-env-vars DB_HOST=10.0.2.3 \
--min-instances 1 --max-instances 20 \ # keep one warm; cap the spend
--cpu 1 --memory 512Mi \
--ingress internal-and-cloud-load-balancing # reachable only via our LB
# Concurrency: Cloud Run's superpower - up to 1000 requests PER instance
gcloud run services update maplecart-web --concurrency 80
# (AWS Lambda and Azure Functions handle exactly ONE request per instance -
# Cloud Run's model means a traffic spike needs fewer instances, less money)
MapleCart and NewsGrid both run here. NewsGrid especially: scale-to-zero makes idle weekends free, and the viral spike just raises instance count — with per-instance concurrency, Cloud Run absorbs bursts that would need 10x the instances of a function-per- request platform.
GKE: Kubernetes as it was meant to be
FleetView runs a dozen microservices with service-to-service auth and per-service scaling — GKE territory. Choose Autopilot mode: Google manages nodes, capacity, and security patches; you write workloads:
gcloud container clusters create-auto fleetview \
--region us-central1 \ # regional cluster: 3 zones, no extra work
--enable-private-nodes # pods without public IPs
# A workload with Workload Identity (the pod BECOMES a service account):
apiVersion: apps/v1
kind: Deployment
metadata: { name: ingest-api }
spec:
replicas: 3
template:
spec:
serviceAccountName: ingest-api # mapped to a GCP SA by annotation
containers:
- name: api
image: us-docker.pkg.dev/fleetview/api/ingest:v9
resources: { requests: { cpu: 250m, memory: 512Mi } }
Workload Identity deserves the highlight: the pod's service account is a real GCP identity — no node-level credential sharing, no secrets in environment variables. It is the cleanest workload-identity story of the three clouds.
Cloud Functions: the event tier
# 2nd-gen functions ARE Cloud Run under the hood - one platform, two ergonomics.
import functions_framework
from PIL import Image
@functions_framework.http
def resize(request):
# HTTP-triggered image resizer for NewsGrid. Runs on Cloud Run infra.
from google.cloud import storage
client = storage.Client() # credentials from the SA, as always
bucket = client.bucket("newsgrid-media")
blob = bucket.get_blob(request.args["path"])
if not blob or not blob.name.lower().endswith((".jpg", ".png")):
return ("not found", 404)
local = "/tmp/img"; blob.download_to_filename(local)
im = Image.open(local); im.thumbnail((800, 800)); im.save(local, optimize=True)
bucket.blob(f"thumbs/{blob.name}").upload_from_filename(local)
return "ok"
The decision table
| Cloud Run | GKE | Cloud Functions | |
|---|---|---|---|
| You manage | container | workloads (Autopilot) | function |
| Scale to zero | yes | no (Autopilot) | yes |
| Concurrency | up to 1000/instance | app-defined | 1/instance |
| Fit | web/API default | microservice fleets | single events |
Next: data — Cloud SQL, Firestore, and the real-time differentiator.