# Microservices with GCP CloudRun & FastAPI

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**](http://main.py)

**Python**

```plaintext
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**

```plaintext
# 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**

```plaintext
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**

```plaintext
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.

<table style="min-width: 75px;"><colgroup><col style="min-width: 25px;"><col style="min-width: 25px;"><col style="min-width: 25px;"></colgroup><tbody><tr><td colspan="1" rowspan="1"><p><strong>Feature</strong></p></td><td colspan="1" rowspan="1"><p><strong>Standard (Public IP)</strong></p></td><td colspan="1" rowspan="1"><p><strong>Enhanced (Private IP)</strong></p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>Connectivity</strong></p></td><td colspan="1" rowspan="1"><p>Via Cloud SQL Proxy</p></td><td colspan="1" rowspan="1"><p>Via VPC Connector</p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>Internet Exposure</strong></p></td><td colspan="1" rowspan="1"><p>Limited by IAM</p></td><td colspan="1" rowspan="1"><p>None</p></td></tr><tr><td colspan="1" rowspan="1"><p><strong>Latency</strong></p></td><td colspan="1" rowspan="1"><p>Low</p></td><td colspan="1" rowspan="1"><p>Lowest</p></td></tr></tbody></table>

* * *

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**

```plaintext
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**
    
    ```plaintext
    # Example of secret reference in the container env list
    {"name": "DB_PASS", "value_source": {"secret_key_ref": {"secret": "DB_PASSWORD_SECRET", "version": "latest"}}}
    ```
