Skip to main content

Command Palette

Search for a command to run...

Microservices with GCP CloudRun & FastAPI

Updated
6 min readView as Markdown
P

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

  1. Create Instance: Navigate to the SQL section in the Google Cloud Console and create a PostgreSQL instance.

  2. Configuration: Define your Instance ID, set a strong password for the postgres user, and select a region (e.g., us-central1) that matches your intended Cloud Run location to minimize latency.

  3. Database & User: Create a specific database (e.g., app_db) and a dedicated application user. Avoid using the default root user for production applications.

  4. 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.

main.py

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:

  1. GCP SDK installed and authenticated: gcloud auth application-default login.

  2. Required Libraries: pip install google-api-python-client google-cloud-run google-cloud-sql google-cloud-build.

  3. Project ID: Replace YOUR_PROJECT_ID in 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

  1. Managed PostgreSQL: It provisions a db-f1-micro instance, which is ideal for testing and lightweight applications.

  2. Database Scoping: It creates a fresh database and a restricted user, following the principle of least privilege.

  3. Cloud SQL Auth Proxy Integration: By adding the run.googleapis.com/cloudsql-instances annotation, it tells Cloud Run to mount the SQL socket into the container automatically.

  4. Environment Injection: It passes the connection metadata into your FastAPI app via environment variables so psycopg2 knows 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"}}}