梦兽编程
AI_SUITE

Cloud Run / GKE Deployment: Complete Cloud Deployment Guide

A detailed guide to deploying an ADK Go Agent on Google Cloud Run and GKE, including logging, metrics, traces, and secret management.

Cloud-native deployment is not just about putting containers in the cloud; it is about fully leveraging the platform’s managed capabilities — auto-scaling, load balancing, service discovery, secret management, observability, and more. Google Cloud offers a full spectrum of deployment options from Serverless (Cloud Run) to Kubernetes (GKE). Understanding their differences and applicable scenarios lets you choose the most suitable architecture for your Agent system.

This article dives into Cloud Run and GKE deployment practices, including CI/CD pipelines, secret management, monitoring, alerting, and cost optimization.


Cloud Run Deployment: Serverless Simplicity

Cloud Run is Google Cloud’s fully managed container platform. It abstracts server management so you only need to focus on the container image. For Agent scenarios, Cloud Run’s per-request billing and auto-scaling are very attractive — instances can scale to 0 when there are no requests, and new instances start in milliseconds when requests arrive.

Build and Push the Image

# Option 1: use Cloud Build (recommended, no local Docker needed)
gcloud builds submit \
    --tag gcr.io/$PROJECT_ID/my-agent:v1.2.3 \
    --build-arg VERSION=v1.2.3 \
    .

# Option 2: build locally and push (requires Docker and gcloud auth)
docker build \
    --build-arg VERSION=v1.2.3 \
    -t gcr.io/$PROJECT_ID/my-agent:v1.2.3 \
    -t gcr.io/$PROJECT_ID/my-agent:latest \
    .

docker push gcr.io/$PROJECT_ID/my-agent:v1.2.3
docker push gcr.io/$PROJECT_ID/my-agent:latest

# Option 3: use Cloud Build config (supports complex builds)
# cloudbuild.yaml
gcloud builds submit --config cloudbuild.yaml
# cloudbuild.yaml
steps:
  # build image
  - name: 'gcr.io/cloud-builders/docker'
    args:
      - 'build'
      - '--build-arg'
      - 'VERSION=${_VERSION}'
      - '--build-arg'
      - 'BUILD_TIME=${_BUILD_TIME}'
      - '--build-arg'
      - 'GIT_COMMIT=${SHORT_SHA}'
      - '-t'
      - 'gcr.io/$PROJECT_ID/my-agent:${_VERSION}'
      - '-t'
      - 'gcr.io/$PROJECT_ID/my-agent:latest'
      - '.'

  # push image
  - name: 'gcr.io/cloud-builders/docker'
    args: ['push', 'gcr.io/$PROJECT_ID/my-agent:${_VERSION}']

  # security scan
  - name: 'gcr.io/cloud-builders/gcloud'
    entrypoint: 'bash'
    args:
      - '-c'
      - |
        gcloud artifacts docker images scan \
          gcr.io/$PROJECT_ID/my-agent:${_VERSION} \
          --remote

  # deploy to Cloud Run
  - name: 'gcr.io/cloud-builders/gcloud'
    args:
      - 'run'
      - 'deploy'
      - 'my-agent'
      - '--image'
      - 'gcr.io/$PROJECT_ID/my-agent:${_VERSION}'
      - '--region'
      - 'asia-east1'
      - '--platform'
      - 'managed'
      - '--no-traffic'  # deploy first, do not receive traffic until verified

substitutions:
  _VERSION: v1.2.3
  _BUILD_TIME: '2026-05-29T10:00:00Z'

images:
  - 'gcr.io/$PROJECT_ID/my-agent:${_VERSION}'
  - 'gcr.io/$PROJECT_ID/my-agent:latest'

Cloud Run Deployment Configuration

# basic deployment
gcloud run deploy my-agent \
    --image gcr.io/$PROJECT_ID/my-agent:v1.2.3 \
    --platform managed \
    --region asia-east1 \
    --memory 1Gi \
    --cpu 1 \
    --concurrency 100 \
    --max-instances 10 \
    --min-instances 1 \
    --port 8080 \
    --timeout 300 \
    --set-env-vars "LOG_LEVEL=info,LOG_FORMAT=json,MAX_CONCURRENT=100" \
    --allow-unauthenticated

# deploy with YAML config (recommended for version control)
gcloud run services replace service.yaml
# service.yaml - Cloud Run service configuration
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: my-agent
  annotations:
    run.googleapis.com/ingress: all  # allow all ingress traffic
    run.googleapis.com/execution-environment: gen2  # second-generation execution environment
spec:
  template:
    metadata:
      annotations:
        # autoscaling config
        autoscaling.knative.dev/minScale: "1"      # keep at least 1 instance to avoid cold start
        autoscaling.knative.dev/maxScale: "20"     # max 20 instances
        autoscaling.knative.dev/targetConcurrency: "50"  # 50 concurrent requests per instance

        # connection config
        run.googleapis.com/cpu-throttling: "false"  # always allocate CPU (good for long connections)
        run.googleapis.com/startup-cpu-boost: "true"  # boost CPU at startup

        # cloud monitoring
        run.googleapis.com/execution-environment: gen2
    spec:
      containerConcurrency: 100  # max concurrent requests per container
      timeoutSeconds: 300        # 5-minute request timeout
      serviceAccountName: my-agent-sa@$PROJECT_ID.iam.gserviceaccount.com

      containers:
        - image: gcr.io/$PROJECT_ID/my-agent:v1.2.3
          ports:
            - containerPort: 8080
          env:
            - name: LOG_LEVEL
              value: "info"
            - name: LOG_FORMAT
              value: "json"
            - name: MAX_CONCURRENT
              value: "100"
            - name: REDIS_URL
              valueFrom:
                secretKeyRef:
                  name: redis-url
                  key: latest
            - name: GOOGLE_API_KEY
              valueFrom:
                secretKeyRef:
                  name: google-api-key
                  key: latest
          resources:
            limits:
              cpu: "2"
              memory: "2Gi"
          startupProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 5
            failureThreshold: 6  # must be ready within 30 seconds
          livenessProbe:
            httpGet:
              path: /health
              port: 8080
            periodSeconds: 10
            failureThreshold: 3

Secret Management: Secret Manager

Never hard-code secrets in code or environment variables in production. Use Google Secret Manager:

# create secret
echo -n "your-api-key" | gcloud secrets create google-api-key --data-file=-

# create Redis connection string secret
echo -n "redis://10.0.0.3:6379/0" | gcloud secrets create redis-url --data-file=-

# list secret versions
gcloud secrets versions list google-api-key

# update secret
echo -n "new-api-key" | gcloud secrets versions add google-api-key --data-file=-

Reference a secret in Cloud Run:

# Option 1: inject as environment variable
env:
  - name: GOOGLE_API_KEY
    valueFrom:
      secretKeyRef:
        name: google-api-key
        key: latest  # or a specific version

# Option 2: mount as a file (safer, avoids env leakage)
volumeMounts:
  - name: secrets
    mountPath: /secrets
volumes:
  - name: secrets
    secret:
      secretName: google-api-key
      items:
        - key: latest
          path: api-key.txt

Read from Go code:

import (
    secretmanager "cloud.google.com/go/secretmanager/apiv1"
    "cloud.google.com/go/secretmanager/apiv1/secretmanagerpb"
)

func getSecret(ctx context.Context, name string) (string, error) {
    client, err := secretmanager.NewClient(ctx)
    if err != nil {
        return "", err
    }
    defer client.Close()

    req := &secretmanagerpb.AccessSecretVersionRequest{
        Name: fmt.Sprintf("projects/%s/secrets/%s/versions/latest", projectID, name),
    }

    result, err := client.AccessSecretVersion(ctx, req)
    if err != nil {
        return "", err
    }

    return string(result.Payload.Data), nil
}

// read secrets at initialization
func initSecrets(ctx context.Context) error {
    apiKey, err := getSecret(ctx, "google-api-key")
    if err != nil {
        return fmt.Errorf("failed to get API key: %w", err)
    }
    os.Setenv("GOOGLE_API_KEY", apiKey)

    redisURL, err := getSecret(ctx, "redis-url")
    if err != nil {
        return fmt.Errorf("failed to get Redis URL: %w", err)
    }
    os.Setenv("REDIS_URL", redisURL)

    return nil
}

Cold Start Optimization

Cold starts are Cloud Run’s biggest challenge, especially for Agent applications that may need to load models or initialize connections:

# 1. keep minimum instances (avoid full cold start)
gcloud run services update my-agent --min-instances 1

# 2. use startupProbe to ensure readiness before receiving traffic
# 3. optimize container image size (smaller starts faster)
# 4. use second-generation execution environment (faster startup)
gcloud run services update my-agent --execution-environment gen2
// optimize startup: lazily initialize non-critical components
func main() {
    // 1. start HTTP server first (fast readiness)
    server := startServer()

    // 2. initialize other components in the background
    go func() {
        if err := initLLMClient(); err != nil {
            log.Printf("failed to init LLM client: %v", err)
        }
    }()

    go func() {
        if err := initRedis(); err != nil {
            log.Printf("failed to init Redis: %v", err)
        }
    }()

    // 3. wait for signals
    // ...
}

GKE Deployment: Full Kubernetes Control

When you need finer control (custom networking, persistent storage, complex scaling strategies), GKE is the better choice.

Create the Cluster

# create a standard cluster
gcloud container clusters create my-cluster \
    --zone asia-east1-a \
    --machine-type e2-standard-2 \
    --num-nodes 3 \
    --enable-autoscaling \
    --min-nodes 2 \
    --max-nodes 10 \
    --enable-autorepair \
    --enable-autoupgrade \
    --disk-size 100GB \
    --disk-type pd-ssd \
    --workload-pool=$PROJECT_ID.svc.id.goog  # enable Workload Identity

# get credentials
gcloud container clusters get-credentials my-cluster --zone asia-east1-a

# verify connection
kubectl get nodes

Deployment Configuration

# k8s/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: agent-system
  labels:
    istio-injection: enabled  # enable Istio service mesh

---
# k8s/serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-agent
  namespace: agent-system
  annotations:
    iam.gke.io/gcp-service-account: my-agent-sa@$PROJECT_ID.iam.gserviceaccount.com

---
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-agent
  namespace: agent-system
  labels:
    app: my-agent
    version: v1.2.3
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1           # allow 1 extra Pod during upgrade
      maxUnavailable: 0     # keep all Pods available during upgrade
  selector:
    matchLabels:
      app: my-agent
  template:
    metadata:
      labels:
        app: my-agent
        version: v1.2.3
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "9090"
        prometheus.io/path: "/metrics"
    spec:
      serviceAccountName: my-agent
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000
        runAsGroup: 1000
        fsGroup: 1000

      # affinity: spread across different nodes
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              podAffinityTerm:
                labelSelector:
                  matchExpressions:
                    - key: app
                      operator: In
                      values:
                        - my-agent
                topologyKey: kubernetes.io/hostname

      # graceful shutdown
      terminationGracePeriodSeconds: 60

      containers:
        - name: agent
          image: gcr.io/$PROJECT_ID/my-agent:v1.2.3
          imagePullPolicy: Always

          ports:
            - name: http
              containerPort: 8080
              protocol: TCP
            - name: metrics
              containerPort: 9090
              protocol: TCP

          env:
            - name: PORT
              value: "8080"
            - name: LOG_LEVEL
              value: "info"
            - name: LOG_FORMAT
              value: "json"
            - name: MAX_CONCURRENT
              value: "200"
            - name: MAX_SESSIONS
              value: "50000"
            - name: REDIS_URL
              valueFrom:
                secretKeyRef:
                  name: agent-secrets
                  key: redis-url
            - name: GOOGLE_API_KEY
              valueFrom:
                secretKeyRef:
                  name: agent-secrets
                  key: google-api-key

          resources:
            requests:
              cpu: "500m"
              memory: "512Mi"
            limits:
              cpu: "2000m"
              memory: "2Gi"

          # health checks
          startupProbe:
            httpGet:
              path: /health
              port: http
            initialDelaySeconds: 5
            periodSeconds: 5
            failureThreshold: 12  # 60-second startup window

          livenessProbe:
            httpGet:
              path: /health
              port: http
            periodSeconds: 10
            failureThreshold: 3

          readinessProbe:
            httpGet:
              path: /health
              port: http
            periodSeconds: 5
            failureThreshold: 3

          # security context
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities:
              drop:
                - ALL

          # mount tmp directory
          volumeMounts:
            - name: tmp
              mountPath: /tmp

      volumes:
        - name: tmp
          emptyDir:
            sizeLimit: 100Mi

---
# k8s/service.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-agent
  namespace: agent-system
  labels:
    app: my-agent
spec:
  type: ClusterIP
  ports:
    - name: http
      port: 80
      targetPort: http
      protocol: TCP
    - name: metrics
      port: 9090
      targetPort: metrics
      protocol: TCP
  selector:
    app: my-agent

---
# k8s/hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: my-agent
  namespace: agent-system
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-agent
  minReplicas: 3
  maxReplicas: 20
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 80
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 60
      policies:
        - type: Percent
          value: 100
          periodSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
        - type: Percent
          value: 10
          periodSeconds: 60

---
# k8s/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-agent
  namespace: agent-system
  annotations:
    kubernetes.io/ingress.class: gce
    kubernetes.io/ingress.global-static-ip-name: my-agent-ip
    networking.gke.io/managed-certificates: my-agent-cert
    networking.gke.io/v1beta1.FrontendConfig: my-agent-frontend-config
spec:
  rules:
    - host: my-agent.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: my-agent
                port:
                  number: 80

---
# k8s/certificate.yaml
apiVersion: networking.gke.io/v1
kind: ManagedCertificate
metadata:
  name: my-agent-cert
  namespace: agent-system
spec:
  domains:
    - my-agent.example.com

---
# k8s/secrets.yaml
apiVersion: v1
kind: Secret
metadata:
  name: agent-secrets
  namespace: agent-system
type: Opaque
stringData:
  redis-url: "redis://10.0.0.3:6379/0"
  google-api-key: ***

Deployment Commands

# apply all configs
kubectl apply -f k8s/

# view deployment status
kubectl get deployments -n agent-system
kubectl get pods -n agent-system -w

# view Pod logs
kubectl logs -f deployment/my-agent -n agent-system

# view HPA status
kubectl get hpa -n agent-system

# manual scaling
kubectl scale deployment my-agent --replicas=5 -n agent-system

# rolling update
kubectl set image deployment/my-agent agent=gcr.io/$PROJECT_ID/my-agent:v1.2.4 -n agent-system

# rollback
kubectl rollout undo deployment/my-agent -n agent-system

# view rollout history
kubectl rollout history deployment/my-agent -n agent-system

Monitoring Configuration: Cloud-Native Observability

Cloud Logging (Structured Logs)

import (
    "cloud.google.com/go/logging"
    "cloud.google.com/go/logging/logadmin"
)

func setupCloudLogging(ctx context.Context, projectID string) (*logging.Logger, error) {
    client, err := logging.NewClient(ctx, projectID)
    if err != nil {
        return nil, err
    }

    logger := client.Logger("my-agent", logging.CommonResource(
        &monitoredres.MonitoredResource{
            Type: "cloud_run_revision",
            Labels: map[string]string{
                "service_name": "my-agent",
                "revision_name": os.Getenv("K_REVISION"),
            },
        },
    ))

    return logger, nil
}

// use Cloud Logging
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()

    // log structured entry
    h.logger.Log(logging.Entry{
        Severity: logging.Info,
        Payload: map[string]interface{}{
            "message":       "request received",
            "request_id":    r.Header.Get("X-Request-ID"),
            "user_id":       r.Header.Get("X-User-ID"),
            "path":          r.URL.Path,
            "method":        r.Method,
            "remote_addr":   r.RemoteAddr,
        },
        Trace: r.Header.Get("X-Cloud-Trace-Context"),  // correlate with Trace
    })

    // ... handle request ...
}

Cloud Monitoring (Metrics)

import (
    "cloud.google.com/go/monitoring/apiv3/v2/monitoringpb"
    "google.golang.org/protobuf/types/known/metricpb"
    "google.golang.org/protobuf/types/known/timestamppb"
)

func recordMetric(ctx context.Context, client *monitoring.MetricClient, value float64) error {
    req := &monitoringpb.CreateTimeSeriesRequest{
        Name: fmt.Sprintf("projects/%s", projectID),
        TimeSeries: []*monitoringpb.TimeSeries{
            {
                Metric: &metricpb.Metric{
                    Type: "custom.googleapis.com/agent/request_latency",
                    Labels: map[string]string{
                        "service": "my-agent",
                    },
                },
                Resource: &monitoredres.MonitoredResource{
                    Type: "global",
                },
                Points: []*monitoringpb.Point{
                    {
                        Interval: &monitoringpb.TimeInterval{
                            EndTime: timestamppb.New(time.Now()),
                        },
                        Value: &monitoringpb.TypedValue{
                            Value: &monitoringpb.TypedValue_DoubleValue{
                                DoubleValue: value,
                            },
                        },
                    },
                },
            },
        },
    }

    return client.CreateTimeSeries(ctx, req)
}

Cloud Trace (Distributed Tracing)

import (
    "contrib.go.opencensus.io/exporter/stackdriver"
    "go.opencensus.io/trace"
)

func initTracing(projectID string) error {
    exporter, err := stackdriver.NewExporter(stackdriver.Options{
        ProjectID: projectID,
    })
    if err != nil {
        return err
    }

    trace.RegisterExporter(exporter)
    trace.ApplyConfig(trace.Config{
        DefaultSampler: trace.ProbabilitySampler(0.1),  // 10% sampling rate
    })

    return nil
}

// use in request handling
func handleRequest(ctx context.Context, req *Request) {
    ctx, span := trace.StartSpan(ctx, "agent.handleRequest")
    defer span.End()

    span.AddAttributes(
        trace.StringAttribute("request_id", req.ID),
        trace.StringAttribute("user_id", req.UserID),
    )

    // child span: LLM call
    ctx, llmSpan := trace.StartSpan(ctx, "llm.call")
    resp, err := llmClient.Generate(ctx, req.Input)
    llmSpan.End()

    if err != nil {
        span.SetStatus(trace.Status{Code: trace.StatusCodeInternal, Message: err.Error()})
        return
    }

    span.AddAttributes(trace.Int64Attribute("response_length", int64(len(resp))))
}

In-Depth Troubleshooting

Q: Cloud Run Cold Start Is Slow

Root cause analysis:

  1. Container image is too large, taking a long time to pull.
  2. Application initialization logic is too heavy.
  3. No min-instances configured; instances start only when requests arrive.
  4. First-generation execution environment is used.

Optimization plan:

# 1. configure minimum instances (trade cost vs latency)
gcloud run services update my-agent --min-instances 1

# 2. use second-generation execution environment
gcloud run services update my-agent --execution-environment gen2

# 3. optimize image size (use distroless)
# 4. lazily initialize non-critical components
# 5. use startupProbe to ensure readiness

Cost impact:

  • min-instances=0: zero cost when idle, but cold start latency (5-30s).
  • min-instances=1: always 1 instance running, no cold start, but continuous billing.
  • min-instances=3: suitable for high availability; cost is 3x min-instances=1.

Q: GKE Pod Cannot Be Scheduled

Diagnosis:

# view Pod status
kubectl describe pod my-agent-xxx -n agent-system

# view events
kubectl get events -n agent-system --sort-by='.lastTimestamp'

# view node resources
kubectl top nodes
kubectl describe node

# view reasons for Pending Pods
kubectl get pods -n agent-system -o wide

Common causes and solutions:

CauseSymptomSolution
Insufficient resourcesInsufficient cpu / Insufficient memoryScale node pool or lower requests
Image pull failureImagePullBackOffCheck image name, permissions, and network
Node taintsPodToleratesNodeTaintsAdd tolerations
Affinity conflictMatchNodeSelectorAdjust affinity config
PVC unboundUnboundImmediatePVCCheck StorageClass

Q: How to Pass Secrets Securely

Solution comparison:

SolutionSecurityComplexityUse Case
Environment variablesLowLowDevelopment / testing
Kubernetes SecretMediumMediumGKE basic
Secret ManagerHighMediumRecommended for Cloud Run
Workload IdentityHighestHighGKE production
HashiCorp VaultHighestHighMulti-cloud environments

Workload Identity configuration (GKE best practice):

# 1. create GCP service account
gcloud iam service-accounts create my-agent-sa \
    --display-name="My Agent Service Account"

# 2. grant Secret Manager access
gcloud secrets add-iam-policy-binding google-api-key \
    --member="serviceAccount:my-agent-sa@$PROJECT_ID.iam.gserviceaccount.com" \
    --role="roles/secretmanager.secretAccessor"

# 3. bind K8s ServiceAccount to GCP ServiceAccount
gcloud iam service-accounts add-iam-policy-binding \
    my-agent-sa@$PROJECT_ID.iam.gserviceaccount.com \
    --role="roles/iam.workloadIdentityUser" \
    --member="serviceAccount:$PROJECT_ID.svc.id.goog[agent-system/my-agent]"

# 4. configure ServiceAccount in K8s (see serviceaccount.yaml above)

Cost Optimization Strategies

Cloud Run Cost Optimization

# 1. set max instance limit
gcloud run services update my-agent --max-instances 10

# 2. optimize concurrency (each instance handles more requests)
gcloud run services update my-agent --concurrency 100

# 3. right-size memory (do not over-provision)
gcloud run services update my-agent --memory 512Mi

# 4. use request timeout control (prevent long occupation)
gcloud run services update my-agent --timeout 300

GKE Cost Optimization

# use Spot instances (save 60-90%)
apiVersion: apps/v1
kind: Deployment
spec:
  template:
    spec:
      nodeSelector:
        cloud.google.com/gke-spot: "true"
      tolerations:
        - key: cloud.google.com/gke-spot
          operator: Equal
          value: "true"
          effect: NoSchedule
# enable cluster autoscaling
gcloud container clusters update my-cluster \
    --enable-autoscaling \
    --min-nodes 1 \
    --max-nodes 10

# use e2-medium machine type (best price/performance)
gcloud container node-pools create spot-pool \
    --cluster my-cluster \
    --machine-type e2-medium \
    --spot \
    --num-nodes 1 \
    --enable-autoscaling \
    --min-nodes 0 \
    --max-nodes 10

Summary

Module 7 complete. We learned:

  • The deep mechanisms of Agent Runtime architecture
  • Complete production practices for CLI deployment
  • High-availability solutions for Web deployment
  • Docker containerization security and optimization
  • Cloud Run / GKE cloud deployment strategies

Next, enter Module 8: A2A protocol — how Agents communicate with each other.

Docker Containerized Deployment | A2A Protocol Introduction →


Want to learn more Go ADK hands-on tips? Follow the “Full Stack Summit - Mengshou Programming” WeChat official account for weekly Go / AI programming updates.

Frequently Asked Questions

What scenarios are Cloud Run and GKE best suited for?

Cloud Run is ideal for serverless, pay-per-request lightweight scenarios; GKE is ideal when you need fine-grained control, custom networking, and complex scaling strategies.

How do you optimize Cloud Run cold starts?

Keep min-instances, use the second-generation execution environment, optimize image size, lazily initialize non-critical components, and configure startupProbe.

How should secrets be passed securely in production?

Use Google Secret Manager, Kubernetes Secrets, GKE Workload Identity, or HashiCorp Vault; never hard-code them.

How do you configure the three pillars of cloud observability?

Use Cloud Logging for structured logs, Cloud Monitoring for custom metrics, and Cloud Trace for distributed tracing.

How can you optimize costs on Cloud Run and GKE?

Set max instances, right-size memory and concurrency, use GKE Spot instances, and enable cluster autoscaling.