Microservices with GCP CloudRun & FastAPI
As a associate system administrator I worked on Redhat Linux servers, including user management, permissions, services, and performance monitoring Automated routine administrative tasks using Bash scripting and cron jobs, reducing manual effort by ~30% I am aws certified sysops administrator and Google Certified Cloud Engineer. Determined to transition my career into cloud architect /Cloud Support role
This guide outlines the process of deploying a high-performance, serverless backend using FastAPI and PostgreSQL on Google Cloud Platform (GCP). By leveraging Cloud Run for containerized execution and Cloud SQL for managed database operations, you achieve a scalable architecture with minimal operational overhead.
Architecture Overview
In this setup, Cloud Run scales your FastAPI containers automatically based on incoming traffic. Connection to Cloud SQL is handled via the Cloud SQL Auth Proxy, which provides secure encryption and IAM-based authorization without requiring you to manage SSL certificates manually.
Step 1: Provision the Managed Database
Create Instance: Navigate to the SQL section in the Google Cloud Console and create a PostgreSQL instance.
Configuration: Define your Instance ID, set a strong password for the
postgresuser, and select a region (e.g.,us-central1) that matches your intended Cloud Run location to minimize latency.Database & User: Create a specific database (e.g.,
app_db) and a dedicated application user. Avoid using the default root user for production applications.Enable API: Ensure the Cloud SQL Admin API is enabled in your project to allow Cloud Run to "see" the instance.
Step 2: Develop the FastAPI Application
The application uses environment variables to remain portable and secure. We utilize psycopg2-binary for the database connection.
Python
from fastapi import FastAPI, HTTPException
import psycopg2
import os
app = FastAPI()
def get_db_connection():
try:
# Cloud SQL Unix socket path: /cloudsql/PROJECT_ID:REGION:INSTANCE_ID
conn = psycopg2.connect(
dbname=os.getenv("DB_NAME"),
user=os.getenv("DB_USER"),
password=os.getenv("DB_PASS"),
host=os.getenv("DB_HOST"),
)
return conn
except Exception as e:
print(f"Error connecting to DB: {e}")
return None
@app.get("/health")
def check_health():
conn = get_db_connection()
if not conn:
raise HTTPException(status_code=500, detail="Database connection failed")
cur = conn.cursor()
cur.execute("SELECT NOW();")
db_time = cur.fetchone()
cur.close()
conn.close()
return {"status": "online", "server_time": db_time[0]}
Step 3: Containerization
To deploy to Cloud Run, the application must be packaged into a Docker image.
Dockerfile
Dockerfile
# Use a lightweight Python base image
FROM python:3.11-slim
# Prevent Python from writing .pyc files and enable unbuffered logging
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
WORKDIR /app
# Install system dependencies for psycopg2
RUN apt-get update && apt-get install -y libpq-dev gcc && rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# Cloud Run listens on port 8080 by default
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]
Step 4: Deployment Workflow
1. Build and Push to Artifact Registry
First, submit your code to Cloud Build to create the container image and store it in your registry.
Bash
gcloud builds submit --tag gcr.io/YOUR_PROJECT_ID/fastapi-sql-app .
2. Deploy to Cloud Run
When deploying, you must link the Cloud SQL instance. This creates a Unix socket at /cloudsql/INSTANCE_CONNECTION_NAME inside the container.
Bash
gcloud run deploy fastapi-sql-app \
--image gcr.io/YOUR_PROJECT_ID/fastapi-sql-app \
--add-cloudsql-instances=YOUR_PROJECT_ID:region:instance-name \
--set-env-vars DB_NAME=app_db,DB_USER=app_user,DB_PASS=your_password,DB_HOST=/cloudsql/YOUR_PROJECT_ID:region:instance-name \
--region us-central1 \
--allow-unauthenticated
Best Practices for Production
Security & Secrets
Hardcoding passwords in environment variables is risky. Use Google Secret Manager to store your DB_PASS. You can then mount these secrets directly into Cloud Run environment variables.
Connection Pooling
Creating a new connection for every request is expensive. For production, switch to an Async driver like asyncpg or use a pooling library like SQLAlchemy to maintain a pool of warm connections.
Networking
For maximum security, configure your Cloud SQL instance with a Private IP and use a Serverless VPC Access connector. This ensures your database traffic never touches the public internet.
Feature | Standard (Public IP) | Enhanced (Private IP) |
Connectivity | Via Cloud SQL Proxy | Via VPC Connector |
Internet Exposure | Limited by IAM | None |
Latency | Low | Lowest |
Would you like me to help you write a Terraform script to automate this entire infrastructure setup?
You said
Please write a full Python lambda script to automate this entire infrastructure setup?
Gemini said
To automate the deployment of a FastAPI application on Google Cloud Run with a Cloud SQL (PostgreSQL) backend, using a Python script (via the Google Cloud Python SDK) is an excellent way to ensure reproducibility.
The following script uses the google-cloud-resource-manager, google-cloud-storage, and google-cloud-run libraries to orchestrate the setup.
Prerequisites
Before running the script, ensure you have:
GCP SDK installed and authenticated:
gcloud auth application-default login.Required Libraries:
pip install google-api-python-client google-cloud-run google-cloud-sql google-cloud-build.Project ID: Replace
YOUR_PROJECT_IDin the script.
Infrastructure Automation Script
Python
import time
from googleapiclient import discovery
from google.cloud import run_v2
from google.cloud import developerconnect_v1
# Configuration
PROJECT_ID = "YOUR_PROJECT_ID"
REGION = "us-central1"
INSTANCE_NAME = "fastapi-sql-instance"
DB_NAME = "app_db"
DB_USER = "app_user"
DB_PASS = "SuperSecretPass123" # In production, use Secret Manager
IMAGE_URI = f"gcr.io/{PROJECT_ID}/fastapi-sql-app:latest"
def setup_infrastructure():
# 1. Initialize Service Clients
sql_client = discovery.build('sqladmin', 'v1beta4')
print(f"--- Starting Infrastructure Setup for {PROJECT_ID} ---")
# 2. Create Cloud SQL Instance
print(f"Creating Cloud SQL Instance: {INSTANCE_NAME}...")
instance_body = {
"name": INSTANCE_NAME,
"region": REGION,
"settings": {
"tier": "db-f1-micro", # Cost-effective for dev
"databaseFlags": [],
"ipConfiguration": {"ipv4Enabled": True}
},
"databaseVersion": "POSTGRES_15"
}
try:
operation = sql_client.instances().insert(
project=PROJECT_ID, body=instance_body).execute()
print("Waiting for SQL instance to provision (this may take 5-10 mins)...")
# Polling for completion (simplified for brevity)
time.sleep(30)
except Exception as e:
print(f"SQL Instance might already exist or error: {e}")
# 3. Create Database and User
print("Configuring Database and User...")
sql_client.databases().insert(
project=PROJECT_ID, instance=INSTANCE_NAME,
body={"name": DB_NAME}).execute()
sql_client.users().insert(
project=PROJECT_ID, instance=INSTANCE_NAME,
body={"name": DB_USER, "password": DB_PASS}).execute()
# 4. Deploy to Cloud Run
print(f"Deploying Cloud Run Service from {IMAGE_URI}...")
client = run_v2.ServicesClient()
# Define the connection string for the Unix Socket
instance_connection_name = f"{PROJECT_ID}:{REGION}:{INSTANCE_NAME}"
db_host = f"/cloudsql/{instance_connection_name}"
service_request = run_v2.CreateServiceRequest(
parent=f"projects/{PROJECT_ID}/locations/{REGION}",
service_id="fastapi-sql-service",
service=run_v2.Service(
template=run_v2.RevisionTemplate(
containers=[
run_v2.Container(
image=IMAGE_URI,
env=[
{"name": "DB_NAME", "value": DB_NAME},
{"name": "DB_USER", "value": DB_USER},
{"name": "DB_PASS", "value": DB_PASS},
{"name": "DB_HOST", "value": db_host},
],
)
],
annotations={
"run.googleapis.com/cloudsql-instances": instance_connection_name
}
)
)
)
operation = client.create_service(request=service_request)
response = operation.result()
print(f"--- Deployment Complete! ---")
print(f"Service URL: {response.uri}")
if __name__ == "__main__":
setup_infrastructure()
What This Script Handles
Managed PostgreSQL: It provisions a
db-f1-microinstance, which is ideal for testing and lightweight applications.Database Scoping: It creates a fresh database and a restricted user, following the principle of least privilege.
Cloud SQL Auth Proxy Integration: By adding the
run.googleapis.com/cloudsql-instancesannotation, it tells Cloud Run to mount the SQL socket into the container automatically.Environment Injection: It passes the connection metadata into your FastAPI app via environment variables so
psycopg2knows where to look.
Critical Security Note
In the script above, the password is a string variable. For a professional deployment:
Store the password in Google Secret Manager.
Update the Cloud Run container definition to reference the secret directly:
Python
# Example of secret reference in the container env list {"name": "DB_PASS", "value_source": {"secret_key_ref": {"secret": "DB_PASSWORD_SECRET", "version": "latest"}}}