GCP for DevOps — Container Exercises

3 minute read

Containers — Practice Exercises

Test your knowledge of GKE, Cloud Run, container workloads, and container CI/CD.


Exercise 1 — GKE Autopilot Cluster with Deployment

Create a GKE Autopilot cluster and deploy a 3-replica nginx Deployment with a LoadBalancer Service.

Show Answer
# Create Autopilot cluster
$ gcloud container clusters create-auto exercise-cluster \
    --region=us-central1 \
    --release-channel=regular

# Get credentials
$ gcloud container clusters get-credentials exercise-cluster \
    --region=us-central1
# nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:alpine
          ports:
            - containerPort: 80
          resources:
            requests:
              cpu: "250m"
              memory: "256Mi"
            limits:
              cpu: "500m"
              memory: "512Mi"
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-lb
spec:
  type: LoadBalancer
  selector:
    app: nginx
  ports:
    - port: 80
      targetPort: 80
$ kubectl apply -f nginx-deployment.yaml
$ kubectl get pods -w
$ kubectl get svc nginx-lb

Exercise 2 — Cloud Run Service with Secrets

Deploy a Cloud Run service that reads a database password from Secret Manager and connects via VPC connector.

Show Answer
# Create a secret in Secret Manager
$ echo -n "s3cur3p@ss" | gcloud secrets create db-password \
    --data-file=- \
    --replication-policy=automatic

# Create a VPC connector
$ gcloud compute networks vpc-access connectors create my-connector \
    --region=us-central1 \
    --subnet=default \
    --min-instances=2 \
    --max-instances=5

# Deploy Cloud Run with secret and VPC connector
$ gcloud run deploy my-api \
    --image=us-central1-docker.pkg.dev/my-project/my-repo/api:v1.0.0 \
    --region=us-central1 \
    --set-secrets="DB_PASSWORD=db-password:latest" \
    --set-env-vars="DB_HOST=10.0.1.5,DB_USER=admin" \
    --vpc-connector=my-connector \
    --vpc-egress=private-ranges-only \
    --min-instances=1

# Verify the service
$ gcloud run services describe my-api --region=us-central1

Exercise 3 — GKE Workload with ConfigMap and Network Policy

Deploy an app that reads config from a ConfigMap and is only accessible from pods labeled app=frontend.

Show Answer
# app-workload.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: api-config
data:
  LOG_LEVEL: "info"
  CACHE_TTL: "300"
  config.json: |
    {"feature_flags": {"new_ui": true}}
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
spec:
  replicas: 2
  selector:
    matchLabels:
      app: api
  template:
    metadata:
      labels:
        app: api
    spec:
      containers:
        - name: api
          image: nginx:alpine
          envFrom:
            - configMapRef:
                name: api-config
          volumeMounts:
            - name: config-vol
              mountPath: /etc/config
          resources:
            requests:
              cpu: "250m"
              memory: "256Mi"
      volumes:
        - name: config-vol
          configMap:
            name: api-config
            items:
              - key: config.json
                path: config.json
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-allow-frontend-only
spec:
  podSelector:
    matchLabels:
      app: api
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: frontend
      ports:
        - protocol: TCP
          port: 80
$ kubectl apply -f app-workload.yaml
$ kubectl get configmap api-config -o yaml
$ kubectl get networkpolicy

Exercise 4 — Cloud Build Pipeline for GKE

Write a cloudbuild.yaml that builds a container, pushes to Artifact Registry, and deploys to GKE using kubectl.

Show Answer
# cloudbuild.yaml
steps:
  # Build
  - name: 'gcr.io/cloud-builders/docker'
    args:
      - 'build'
      - '-t'
      - 'us-central1-docker.pkg.dev/$PROJECT_ID/my-repo/api:$SHORT_SHA'
      - '.'
    id: 'build'

  # Push
  - name: 'gcr.io/cloud-builders/docker'
    args: ['push', 'us-central1-docker.pkg.dev/$PROJECT_ID/my-repo/api:$SHORT_SHA']
    id: 'push'
    waitFor: ['build']

  # Get GKE credentials
  - name: 'gcr.io/cloud-builders/gcloud'
    args:
      - 'container'
      - 'clusters'
      - 'get-credentials'
      - 'exercise-cluster'
      - '--region=us-central1'
    id: 'auth'
    waitFor: ['push']

  # Deploy to GKE
  - name: 'gcr.io/cloud-builders/kubectl'
    args:
      - 'set'
      - 'image'
      - 'deployment/api'
      - 'api=us-central1-docker.pkg.dev/$PROJECT_ID/my-repo/api:$SHORT_SHA'
    env:
      - 'CLOUDSDK_COMPUTE_REGION=us-central1'
      - 'CLOUDSDK_CONTAINER_CLUSTER=exercise-cluster'
    waitFor: ['auth']

images:
  - 'us-central1-docker.pkg.dev/$PROJECT_ID/my-repo/api:$SHORT_SHA'
# Submit the build
$ gcloud builds submit --config=cloudbuild.yaml .

Exercise 5 — Canary Deployment on Cloud Run

Deploy v2 of a Cloud Run service as a canary with 10% traffic, verify, then promote to 100%.

Show Answer
# Deploy v2 with no traffic and a canary tag
$ gcloud run deploy my-api \
    --image=us-central1-docker.pkg.dev/my-project/my-repo/api:v2.0.0 \
    --region=us-central1 \
    --no-traffic \
    --tag=canary

# Route 10% traffic to canary
$ gcloud run services update-traffic my-api \
    --region=us-central1 \
    --to-tags=canary=10

# Check traffic split
$ gcloud run services describe my-api \
    --region=us-central1 \
    --format="yaml(status.traffic)"

# Test canary at its dedicated URL
$ curl https://canary---my-api-abc123.a.run.app/healthz

# If healthy, promote to 100%
$ gcloud run services update-traffic my-api \
    --region=us-central1 \
    --to-latest

# If unhealthy, rollback
$ gcloud run services update-traffic my-api \
    --region=us-central1 \
    --to-tags=canary=0

Cleanup

$ gcloud container clusters delete exercise-cluster --region=us-central1 --quiet
$ gcloud run services delete my-api --region=us-central1 --quiet
$ gcloud compute networks vpc-access connectors delete my-connector --region=us-central1 --quiet
$ gcloud secrets delete db-password --quiet

Next Chapter → Infrastructure as Code

Updated: