Deploying and managing Google Cloud Run for containerized applications
Google Cloud Run is a fully managed compute platform that allows you to run stateless containers directly on top of Google's scalable infrastructure. It abstracts away infrastructure management so you can focus on developing applications in the language of your choice.
Key Features
Fully managed: No infrastructure to provision or manage
Serverless: Pay only for the resources you use
Scale to zero: No charges when your service isn't running
Autoscaling: Automatically scales based on traffic
Multiple languages: Supports any language using a Docker container
Custom domains: Connect your own domain names
Private services: Restrict access to authorized users or internal services
Traffic splitting: Gradually roll out new versions with percentage-based traffic splitting
VPC connectivity: Connect to VPC resources
Cloud SQL connection: Direct connection to Cloud SQL databases
Concurrency: Process multiple requests per container instance
WebSockets: Support for WebSockets and HTTP/2
Deploying Cloud Run with Terraform
Basic Service Deployment
Cloud Run Service with VPC Access
Cloud Run Service with Traffic Splitting
Cloud Run with Custom Domain Mapping
Deploying Cloud Run with gcloud CLI
Building and Deploying a Container
Updating an Existing Service
Creating a Private Service
Configure VPC Connector
Configure Environment Variables and Secrets
Real-World Example: Microservices Application
This example demonstrates a complete microservices architecture using Cloud Run:
Step 1: Infrastructure Setup with Terraform
Step 2: Example Authentication Service Code
Step 3: Example Product Service Code
Step 4: Example of Frontend Integration
Best Practices
Container Design
Use distroless or minimal base images
Follow the single responsibility principle
Optimize Dockerfile for layer caching
Implement proper health checks
Handle graceful shutdowns (SIGTERM)
Keep container images small
Performance Optimization
Configure appropriate memory and CPU limits
Minimize container startup time
Implement connection pooling for databases
Use caching when appropriate
Scale container instances based on actual load
Use concurrency settings effectively
Security
Use dedicated service accounts with minimal permissions
Store secrets in Secret Manager
Enable binary authorization if needed
Implement proper authentication and authorization
Scan container images for vulnerabilities
Follow the principle of least privilege
Cost Optimization
Use CPU throttling for background services
Scale to zero when possible
Use min instances only for critical services
Monitor and set budget alerts
Optimize container image size
Use instance concurrency to handle multiple requests
# Create a service account for Cloud Run
resource "google_service_account" "cloud_run_sa" {
account_id = "cloud-run-sa"
display_name = "Cloud Run Service Account"
}
# Grant permissions to the service account
resource "google_project_iam_member" "cloud_run_permissions" {
project = var.project_id
role = "roles/logging.logWriter"
member = "serviceAccount:${google_service_account.cloud_run_sa.email}"
}
# Deploy the Cloud Run service
resource "google_cloud_run_service" "service" {
name = "my-service"
location = var.region
template {
spec {
containers {
image = "gcr.io/${var.project_id}/my-image:latest"
# Resource limits
resources {
limits = {
cpu = "1000m"
memory = "512Mi"
}
}
# Environment variables
env {
name = "ENV_VAR_NAME"
value = "env_var_value"
}
# Secret environment variables
env {
name = "SECRET_ENV_VAR"
value_from {
secret_key_ref {
name = google_secret_manager_secret.my_secret.secret_id
key = "latest"
}
}
}
# Container ports
ports {
container_port = 8080
}
}
# Service account
service_account_name = google_service_account.cloud_run_sa.email
# Concurrency settings
container_concurrency = 80
# Timeout
timeout_seconds = 300
}
metadata {
annotations = {
"autoscaling.knative.dev/maxScale" = "100"
"autoscaling.knative.dev/minScale" = "1"
"run.googleapis.com/client-name" = "terraform"
}
}
}
traffic {
percent = 100
latest_revision = true
}
# Auto-generate revision name
autogenerate_revision_name = true
}
# Allow public access to the service
resource "google_cloud_run_service_iam_member" "public_access" {
location = google_cloud_run_service.service.location
service = google_cloud_run_service.service.name
role = "roles/run.invoker"
member = "allUsers"
}
# Create a secret for the service
resource "google_secret_manager_secret" "my_secret" {
secret_id = "my-service-secret"
replication {
automatic = true
}
}
resource "google_secret_manager_secret_version" "my_secret_version" {
secret = google_secret_manager_secret.my_secret.id
secret_data = "my-secret-value"
}
# Grant access to secret
resource "google_secret_manager_secret_iam_member" "secret_access" {
secret_id = google_secret_manager_secret.my_secret.id
role = "roles/secretmanager.secretAccessor"
member = "serviceAccount:${google_service_account.cloud_run_sa.email}"
}
# Output the service URL
output "service_url" {
value = google_cloud_run_service.service.status[0].url
}
# Create a VPC
resource "google_compute_network" "vpc" {
name = "cloudrun-vpc"
auto_create_subnetworks = false
}
# Create a subnet
resource "google_compute_subnetwork" "subnet" {
name = "cloudrun-subnet"
ip_cidr_range = "10.0.0.0/24"
region = var.region
network = google_compute_network.vpc.id
}
# Create a VPC connector for Cloud Run
resource "google_vpc_access_connector" "connector" {
name = "vpc-connector"
region = var.region
ip_cidr_range = "10.8.0.0/28"
network = google_compute_network.vpc.name
}
# Deploy Cloud Run service with VPC connector
resource "google_cloud_run_service" "vpc_service" {
name = "vpc-service"
location = var.region
template {
spec {
containers {
image = "gcr.io/${var.project_id}/my-vpc-app:latest"
# Environment variables
env {
name = "INTERNAL_SERVICE_URL"
value = "http://internal-service.private.run:8080"
}
}
# Service account
service_account_name = google_service_account.cloud_run_sa.email
}
metadata {
annotations = {
"run.googleapis.com/vpc-access-connector" = google_vpc_access_connector.connector.id
"run.googleapis.com/vpc-access-egress" = "private-ranges-only"
}
}
}
}
# Deploy Cloud Run service with multiple revisions
resource "google_cloud_run_service" "multi_revision_service" {
name = "multi-revision-service"
location = var.region
template {
spec {
containers {
image = "gcr.io/${var.project_id}/my-image:v2"
}
}
metadata {
name = "multi-revision-service-green"
annotations = {
"run.googleapis.com/client-name" = "terraform"
}
}
}
# Traffic splitting between revisions
traffic {
percent = 80
revision_name = "multi-revision-service-green"
}
traffic {
percent = 20
revision_name = "multi-revision-service-blue"
# This revision needs to exist before applying this configuration
}
}
# Create a Cloud Run service
resource "google_cloud_run_service" "website" {
name = "website"
location = var.region
template {
spec {
containers {
image = "gcr.io/${var.project_id}/website:latest"
}
}
}
}
# Create a domain mapping
resource "google_cloud_run_domain_mapping" "domain_mapping" {
location = var.region
name = "example.com"
metadata {
namespace = var.project_id
}
spec {
route_name = google_cloud_run_service.website.name
}
}
# Output the resource records that should be added to DNS
output "dns_records" {
value = [for record in google_cloud_run_domain_mapping.domain_mapping.status[0].resource_records : {
name = record.name
type = record.type
rrdatas = record.rrdatas
}]
}
# Navigate to your app directory
cd ~/my-app
# Create a Dockerfile
cat > Dockerfile << EOF
FROM node:16-slim
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 8080
CMD [ "node", "index.js" ]
EOF
# Build and push the container image
gcloud builds submit --tag gcr.io/$(gcloud config get-value project)/my-app:v1
# Deploy to Cloud Run
gcloud run deploy my-app \
--image gcr.io/$(gcloud config get-value project)/my-app:v1 \
--platform managed \
--region us-central1 \
--allow-unauthenticated
# Deploy a new revision
gcloud run deploy my-app \
--image gcr.io/$(gcloud config get-value project)/my-app:v2 \
--platform managed \
--region us-central1
# Split traffic between revisions
gcloud run services update-traffic my-app \
--platform managed \
--region us-central1 \
--to-revisions my-app-00001-abc=80,my-app-00002-def=20
# Deploy a private Cloud Run service
gcloud run deploy private-service \
--image gcr.io/$(gcloud config get-value project)/private-service:v1 \
--platform managed \
--region us-central1 \
--no-allow-unauthenticated
# Grant access to a specific user
gcloud run services add-iam-policy-binding private-service \
--platform managed \
--region us-central1 \
--member="user:user@example.com" \
--role="roles/run.invoker"
# Grant access to another service account
gcloud run services add-iam-policy-binding private-service \
--platform managed \
--region us-central1 \
--member="serviceAccount:my-sa@$(gcloud config get-value project).iam.gserviceaccount.com" \
--role="roles/run.invoker"