GCP Cloud Run Container Deployment Tutorial
GCP Cloud Run Container Deployment Tutorial
Google Cloud Run is a fully managed serverless container runtime platform provided by GCP. It lets you run containerized applications without managing any infrastructure. Cloud Run combines the flexibility of containers with the convenience of serverless, making it an ideal choice for modern cloud-native applications. This tutorial walks you through deploying a containerized application to Cloud Run from start to finish.
Core Concepts
Cloud Run is built on the Knative open-source framework and provides these core features:
- Fully Managed: Google handles underlying infrastructure, security patches, and runtime maintenance
- Autoscaling: Automatically scales from zero to thousands of instances; scales to zero when idle
- Pay-per-use: Only pay for requests processed and actual CPU/memory consumption
- Container-native: Support any language and framework β as long as it can be containerized
- HTTPS by default: Automatic SSL certificates and custom domain support
Cloud Run Pricing
| Resource | Free Tier | Paid Tier | |----------|-----------|-----------| | vCPU | 2 vCPU-seconds/sec per month | $0.00004000/vCPU-second | | Memory | 1 GB-second/sec per month | $0.00000475/GB-second | | Requests | 2 million per month | $0.40/million | | Network (egress) | 5GB/month (North America) | $0.08/GB |
Prerequisites
# Install Google Cloud SDK
curl https://sdk.cloud.google.com | bash
# Initialize and authenticate
gcloud init
# Enable required APIs
gcloud services enable \
run.googleapis.com \
cloudbuild.googleapis.com \
artifactregistry.googleapis.com \
containerregistry.googleapis.com
Sample Application: Go Web Service
We will build a simple Web API service using Go. Create a project directory and add the following files:
// main.go
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"os"
)
type Response struct {
Message string `json:"message"`
Hostname string `json:"hostname"`
Version string `json:"version"`
}
func handler(w http.ResponseWriter, r *http.Request) {
hostname, _ := os.Hostname()
resp := Response{
Message: "Hello from Cloud Run!",
Hostname: hostname,
Version: "1.0.0",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
}
func healthHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "OK")
}
func main() {
http.HandleFunc("/", handler)
http.HandleFunc("/health", healthHandler)
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
log.Printf("Server starting on port %s", port)
log.Fatal(http.ListenAndServe(":"+port, nil))
}
Creating the Dockerfile
# Dockerfile
# Build stage
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o server main.go
# Runtime stage
FROM alpine:3.19
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/server .
EXPOSE 8080
CMD ["./server"]
Option 1: One-Click Deploy with Cloud Build
The most convenient deployment method uses Cloud Build's automatic build-and-deploy pipeline:
# Build and deploy in one command
gcloud run deploy my-service \
--source . \
--region us-central1 \
--allow-unauthenticated \
--cpu 1 \
--memory 512Mi \
--min-instances 0 \
--max-instances 100 \
--set-env-vars "VERSION=1.0.0"
This command automatically: builds from source β creates Docker image β pushes to Artifact Registry β deploys to Cloud Run.
Option 2: Manual Build and Deploy
For more control, break it into separate steps:
# 1. Build the Docker image
docker build -t us-central1-docker.pkg.dev/$(gcloud config get-value project)/my-repo/my-service:v1 .
# 2. Push to Artifact Registry
docker push us-central1-docker.pkg.dev/$(gcloud config get-value project)/my-repo/my-service:v1
# 3. Deploy to Cloud Run
gcloud run deploy my-service \
--image us-central1-docker.pkg.dev/$(gcloud config get-value project)/my-repo/my-service:v1 \
--region us-central1 \
--allow-unauthenticated \
--cpu 1 \
--memory 512Mi \
--port 8080
Configuring Autoscaling
Autoscaling is one of Cloud Run's core advantages:
gcloud run services update my-service \
--region us-central1 \
--min-instances 1 \
--max-instances 50 \
--concurrency 80 \
--cpu-throttling
| Parameter | Description | Recommended Value |
|-----------|-------------|-------------------|
| --min-instances | Minimum instances; set to 1 to avoid cold starts | 0 (cost savings) / 1 (low latency) |
| --max-instances | Maximum instances to prevent unbounded scaling | 10-100 depending on traffic |
| --concurrency | Max concurrent requests per instance | 80 (default) / 1000 (I/O-bound) |
| --cpu-throttling | Whether CPU is throttled when idle | Enabled (savings) / Disabled (CPU-bound) |
Custom Domain and HTTPS
# Map a custom domain
gcloud run domain-mappings create \
--service my-service \
--domain api.example.com \
--region us-central1
# After domain ownership verification, Cloud Run automatically provisions a managed SSL certificate
Continuous Deployment (CI/CD)
Use Cloud Build Triggers for automated deployments:
# cloudbuild.yaml
steps:
# Build image
- name: 'gcr.io/cloud-builders/docker'
args: ['build', '-t', 'us-central1-docker.pkg.dev/$PROJECT_ID/my-repo/my-service:$COMMIT_SHA', '.']
# Push image
- name: 'gcr.io/cloud-builders/docker'
args: ['push', 'us-central1-docker.pkg.dev/$PROJECT_ID/my-repo/my-service:$COMMIT_SHA']
# Deploy to Cloud Run
- name: 'gcr.io/cloud-builders/gcloud'
args:
- 'run'
- 'deploy'
- 'my-service'
- '--image=us-central1-docker.pkg.dev/$PROJECT_ID/my-repo/my-service:$COMMIT_SHA'
- '--region=us-central1'
- '--platform=managed'
images:
- 'us-central1-docker.pkg.dev/$PROJECT_ID/my-repo/my-service:$COMMIT_SHA'
Security Best Practices
- Use Artifact Registry instead of Container Registry (more secure, regional)
- Least privilege: Grant only necessary permissions to Cloud Run service accounts
- Secret Manager integration: Mount secrets for sensitive configuration
# Mount a Secret
gcloud run services update my-service \
--update-secrets=DB_PASSWORD=my-secret:latest
- VPC Connector: Configure Serverless VPC Access when accessing VPC-internal resources (e.g., Cloud SQL)
- IAM authentication: Disable
--allow-unauthenticatedfor internal services and use IAM to control access
Monitoring and Observability
Cloud Run integrates automatically with the GCP observability suite:
- Cloud Logging: Container stdout/stderr automatically collected
- Cloud Monitoring: Latency, error rate, instance count, and other metrics
- Cloud Trace: Distributed tracing with code integration
// Add OpenTelemetry tracing
import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/trace/jaeger"
)
func initTracer() {
exporter, _ := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint("http://localhost:14268/api/traces")))
tp := tracesdk.NewTracerProvider(tracesdk.WithBatcher(exporter))
otel.SetTracerProvider(tp)
}
Cloud Run vs Other Container Deployment Options
| Feature | Cloud Run | GKE | Compute Engine | |---------|-----------|-----|----------------| | Management overhead | Zero | Medium | High | | Autoscaling | Automatic (to zero) | Requires HPA config | Self-implemented | | Cold start | Yes (~1-3 seconds) | No | No | | Network control | Limited | Full | Full | | Max request duration | 60 minutes | Unlimited | Unlimited | | Pay-per-use | Yes | Per-node | Per-instance |
Conclusion
GCP Cloud Run provides ultimate deployment convenience for containerized applications, especially suited for API services, web applications, and event-driven workloads. Going from zero to production takes just one command, and autoscaling means you never need to worry about traffic fluctuations.
Purchase GCP resources through Duoyun Cloud and enjoy partner-exclusive discounts with expert technical support, saving up to 20% on cloud spending. Visit duoyun.io to learn more.
Need Professional Cloud Consulting?
Our cloud architect team will customize the best solution for you β free
Free Consultation