100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Multi-Cloud Architecture & Serverless
55 minadvanced

Lab — deploy Cloud Run with Cloud SQL via private VPC connector

This lab deploys a Cloud Run service connected to a Cloud SQL PostgreSQL instance through a private VPC Serverless Connector, without exposing either service to the public internet. The lab demonstrates four Module 4 integration patterns: project-level API enablement, Workload Identity for keyless Cloud SQL authentication, VPC Serverless Connector for private Cloud Run-to-Cloud SQL connectivity, and Secret Manager for database credentials without environment variable secrets. By the end, you have a production-pattern Cloud Run service that never stores database passwords in code or configuration.

Analogy🏏Cricket
🏏 Think of it like cricket: In Test cricket, the ICC publishes playing conditions — governing over rates, DRS quotas, pitch inspection protocols, and player conduct — that both captains sign before the first session, whether the match is at Lord’s, the MCG, or Eden Gardens. Just as the playing conditions give umpires a single authoritative standard so every ruling references the same document rather than personal judgement, the Well-Architected Framework gives architects a shared evaluation language so every workload is measured against the same six pillars rather than each engineer’s intuition. Just as a team posting a slow over rate incurs penalties regardless of their score, a workload with Security or Reliability gaps carries structural risk regardless of how quickly it shipped. Just as every specialist role — opener, keeper, tail — has defined performance expectations against which selectors evaluate each player, every workload component is evaluated against pillar-specific best-practice questions. This reveals why the framework must precede any advanced architectural decision: a shared, evidence-based standard transforms subjective trade-offs into structured, auditable risk assessments that hold across teams, accounts, and regions.

The lab builds on the keyless authentication patterns from the Workload Identity Federation lesson, applying them within a single GCP project using GCP’s built-in Workload Identity rather than the cross-cloud WIF pattern. A Cloud Run service’s service account receives the Cloud SQL Client role, enabling the Cloud SQL Auth Proxy to authenticate using IAM credentials rather than a database password. This IAM-based database authentication is the GCP equivalent of the AWS RDS IAM authentication pattern and the Azure managed identity pattern covered in Module 3.

Prerequisites: gcloud CLI installed and authenticated; a GCP project with billing enabled; permissions for Cloud Run, Cloud SQL, VPC, Secret Manager, and Service Account APIs. All resources deploy to asia-south1 (Mumbai) region. Estimated lab duration is 55 minutes including Cloud SQL instance provisioning time, which takes 5 to 10 minutes.

Prerequisites

  • gcloud CLI installed and authenticated; verify with `gcloud auth list`.
  • Active GCP project with billing account linked; verify with `gcloud billing projects describe YOUR_PROJECT`.
  • Required APIs not yet enabled will be enabled in Step 1.
  • Docker installed locally for building the container image, or Cloud Build enabled for cloud-based builds.
  • Python 3.11 and pip installed for local application development.

Step 1 — Enable APIs and Create Service Account

Enable the six required APIs for this lab and create a dedicated service account for the Cloud Run service. The service account replaces the default Compute Engine service account, which has broad project-level Editor permissions that violate least privilege. The Cloud Run service account will receive only the two permissions it actually needs: reading secrets from Secret Manager and connecting to the Cloud SQL instance via IAM authentication.

Analogy🏏Cricket
🏏 Think of it like cricket: Before a match a ground manager first switches on only the systems the day requires — floodlights, scoreboard, PA — rather than every switch in the stadium, and issues each staff member a pass carrying only the access their job needs. Just as leaving the default master keycard on every worker would let a caterer wander into the umpires' room, using Cloud Run's default Compute Engine service account hands the app broad project-level Editor permissions it should never have. Just as this step powers on exactly six systems and no more, you enable the six specific APIs the lab needs. Just as the physio's pass opens only the medical room and the equipment store, the dedicated service account you create receives only two roles — roles/cloudsql.client to reach the database and roles/secretmanager.secretAccessor to read the connection secret. The payoff: least privilege from the very first step means a compromise of this service can touch only the database and one secret, not the entire project.
python
import subprocess

PROJECT  = 'ipl-scorecard-prod'
REGION   = 'asia-south1'
SA_NAME  = 'sa-cloudrun-scorecard'
SA_EMAIL = f'{SA_NAME}@{PROJECT}.iam.gserviceaccount.com'

# Enable required APIs.
apis = [
    'run.googleapis.com',
    'sqladmin.googleapis.com',
    'secretmanager.googleapis.com',
    'vpcaccess.googleapis.com',
    'compute.googleapis.com',
    'iam.googleapis.com',
]
subprocess.run([
    'gcloud', 'services', 'enable', *apis, '--project', PROJECT
], check=True)
print(f'Enabled {len(apis)} APIs.')

# Create dedicated service account.
subprocess.run([
    'gcloud', 'iam', 'service-accounts', 'create', SA_NAME,
    '--display-name', 'IPL Cloud Run Scorecard SA',
    '--project', PROJECT,
], check=True)

# Grant only the specific roles needed (no Editor or broad roles).
for role in ['roles/cloudsql.client', 'roles/secretmanager.secretAccessor']:
    subprocess.run([
        'gcloud', 'projects', 'add-iam-policy-binding', PROJECT,
        '--member', f'serviceAccount:{SA_EMAIL}',
        '--role',   role,
    ], check=True)
print(f'Service account: {SA_EMAIL} with cloudsql.client and secretmanager.secretAccessor.')

Step 2 — VPC and Serverless Connector

Create the VPC Serverless Connector that enables Cloud Run services to make outbound connections to resources in the VPC, specifically the Cloud SQL instance’s private IP. The connector reserves a /28 subnet in the VPC specifically for serverless connector instances. Cloud Run services configured with the VPC connector route all outbound traffic through the connector into the VPC, enabling private Cloud SQL connections without the Cloud SQL instance requiring a public IP address.

Analogy🏏Cricket
🏏 Think of it like cricket: Cloud Run containers live in Google's own multi-tenant sandbox with no native route into your private network, the way a visiting team's players arrive outside the ground with no automatic access to the field. Just as the stadium provides a dedicated, staffed access corridor — with its own reserved lane — that escorts visitors onto the pitch, the VPC Serverless Connector is a managed fleet of small instances that reserves a /28 subnet and forwards Cloud Run's outbound traffic into your VPC. Just as every player must use that corridor rather than climbing a fence, Cloud Run services configured with the connector route their outbound traffic through it to reach resources like the Cloud SQL instance's private IP. Just as the corridor scales its staffing up and down with the size of the touring party, the connector has its own min- and max-instance scaling. The payoff: Cloud Run reaches a private-IP-only database without that database ever needing a public endpoint exposed to the internet.
python
import subprocess

PROJECT = 'ipl-scorecard-prod'
REGION  = 'asia-south1'

# Create VPC (or use existing).
subprocess.run([
    'gcloud', 'compute', 'networks', 'create', 'ipl-vpc',
    '--subnet-mode', 'auto', '--project', PROJECT,
], check=False)  # may already exist

# Enable Private Google Access on the asia-south1 subnet.
subprocess.run([
    'gcloud', 'compute', 'networks', 'subnets', 'update', 'ipl-vpc',
    '--region', REGION,
    '--enable-private-ip-google-access',
    '--project', PROJECT,
], check=True)

# Create VPC Serverless Connector.
subprocess.run([
    'gcloud', 'compute', 'networks', 'vpc-access', 'connectors', 'create',
    'ipl-connector',
    '--region',         REGION,
    '--network',        'ipl-vpc',
    '--range',          '10.8.0.0/28',   # reserved /28 for connector
    '--min-instances',  '2',
    '--max-instances',  '10',
    '--machine-type',   'e2-micro',
    '--project',         PROJECT,
], check=True)
print('VPC Serverless Connector created: ipl-connector')

Step 3 — Cloud SQL with Private IP

Create the Cloud SQL PostgreSQL instance with a private IP only, no public IP, in the VPC. Private IP Cloud SQL instances are accessible only from resources in the same VPC or connected networks, preventing any internet-facing database exposure. Enable IAM database authentication on the instance, which allows the Cloud Run service’s IAM service account to authenticate as a database user using its GCP IAM credentials rather than a database password.

Analogy🏏Cricket
🏏 Think of it like cricket: A team's inner sanctum — the dressing room — has no door onto the public street; it opens only onto the secured players' area, and entry is granted by verified accreditation rather than a shared spare key hidden under a mat. Just as that room is reachable only from inside the secured zone, this step creates the Cloud SQL PostgreSQL instance with a private IP and no public IP, so it is reachable only from resources in the same VPC — there is simply no internet-facing endpoint to attack. Just as the door reads each player's accreditation to admit them, enabling IAM database authentication lets the Cloud Run service account authenticate as a database user using its GCP IAM identity rather than a stored password. Just as accreditation can be revoked centrally the instant a pass is lost, IAM auth issues short-lived, revocable credentials instead of a static password that could be copied and reused. The payoff: a database with zero public exposure whose access is granted and revoked through central identity, not through passwords that leak and linger.
python
import subprocess, time

PROJECT    = 'ipl-scorecard-prod'
REGION     = 'asia-south1'
SQL_INST   = 'ipl-postgres'
DB_NAME    = 'ipl_scores'

# Create Cloud SQL with private IP only and IAM authentication enabled.
subprocess.run([
    'gcloud', 'sql', 'instances', 'create', SQL_INST,
    '--database-version',   'POSTGRES_15',
    '--tier',               'db-g1-small',
    '--region',             REGION,
    '--network',            'ipl-vpc',
    '--no-assign-ip',       # private IP only, no public endpoint
    '--database-flags',     'cloudsql.iam_authentication=on',
    '--project',             PROJECT,
], check=True)
print(f'Cloud SQL instance {SQL_INST} created. Waiting for provisioning...')
time.sleep(60)  # Cloud SQL provisioning takes 5-10 minutes in production

# Create the database.
subprocess.run([
    'gcloud', 'sql', 'databases', 'create', DB_NAME,
    '--instance', SQL_INST, '--project', PROJECT,
], check=True)

# Create an IAM database user for the Cloud Run service account.
# This maps the service account to a PostgreSQL IAM user.
SA_EMAIL = f'sa-cloudrun-scorecard@{PROJECT}.iam.gserviceaccount.com'
subprocess.run([
    'gcloud', 'sql', 'users', 'create',
    # IAM user name is the SA email without the .gserviceaccount.com suffix.
    SA_EMAIL.replace('.gserviceaccount.com', ''),
    '--instance',  SQL_INST,
    '--type',      'CLOUD_IAM_SERVICE_ACCOUNT',
    '--project',   PROJECT,
], check=True)
print(f'IAM database user created for {SA_EMAIL}')

Step 4 — Deploy Cloud Run with IAM Database Auth

Write the Cloud Run service application code using the Cloud SQL Python connector with IAM authentication, store the Cloud SQL instance connection name in Secret Manager rather than in the Cloud Run service configuration, and deploy the service with the VPC connector, the dedicated service account, and the Cloud SQL instance connection. The application code uses the Cloud SQL Python connector, which handles the Cloud SQL Auth Proxy functionality in-process without requiring a sidecar container.

Analogy🏏Cricket
🏏 Think of it like cricket: On match day every element finally comes together — the accredited player, the secured tunnel, and the tactics sheet fetched from the team vault rather than carried on a printed card that could be dropped. Just as the player walks out through the tunnel using accreditation the officials verify at the boundary, this step deploys the Cloud Run service with the dedicated service account and the VPC connector, and the Cloud SQL Python connector authenticates in-process using IAM — no sidecar container, no database password. Just as the tactics sheet is pulled from the vault at the moment of need instead of being printed into the kit, the Cloud SQL connection name is read from Secret Manager rather than baked into the service configuration. Just as a coach can update the vaulted plan without recalling every player, rotating the secret needs no redeploy. The payoff: the application connects to a private database with a revocable IAM identity and a rotatable, externally-stored connection detail — the complete production pattern with no password anywhere in code or config.
python
import subprocess

PROJECT  = 'ipl-scorecard-prod'
REGION   = 'asia-south1'
SA_EMAIL = f'sa-cloudrun-scorecard@{PROJECT}.iam.gserviceaccount.com'
SQL_INST = f'{PROJECT}:{REGION}:ipl-postgres'

# Store the Cloud SQL connection name in Secret Manager (no hardcoded config).
subprocess.run([
    'gcloud', 'secrets', 'create', 'ipl-cloudsql-connection',
    '--data-stdin', '--project', PROJECT,
], input=SQL_INST.encode(), check=True)
print('Cloud SQL connection name stored in Secret Manager.')

# Application code: src/main.py
app_code = '''
from flask import Flask, jsonify
from google.cloud.sql.connector import Connector, IPTypes
from google.cloud import secretmanager
import pg8000.native
import os

app = Flask(__name__)
SM_CLIENT = secretmanager.SecretManagerServiceClient()

def get_db_connection():
    """Create IAM-authenticated Cloud SQL connection via the Python connector."""
    project_id = os.environ.get("GOOGLE_CLOUD_PROJECT", "ipl-scorecard-prod")
    # Retrieve connection name from Secret Manager (not an env var with the value).
    secret_name = f"projects/{project_id}/secrets/ipl-cloudsql-connection/versions/latest"
    conn_name = SM_CLIENT.access_secret_version(
        name=secret_name
    ).payload.data.decode()

    connector = Connector(ip_type=IPTypes.PRIVATE)  # use private IP via VPC connector
    # enable_iam_auth=True authenticates using the Cloud Run SA\'s IAM identity.
    conn = connector.connect(
        conn_name, "pg8000", user="sa-cloudrun-scorecard",
        db="ipl_scores", enable_iam_auth=True,
    )
    return conn

@app.route("/api/scores/<match_id>")
def get_score(match_id):
    conn = get_db_connection()
    cursor = conn.cursor()
    cursor.execute(
        "SELECT batting_team, runs, wickets, overs FROM innings WHERE match_id = %s",
        (match_id,)
    )
    rows = cursor.fetchall()
    return jsonify([dict(zip(["battingTeam","runs","wickets","overs"], row)) for row in rows])

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8080)
'''
with open('src/main.py', 'w') as f: f.write(app_code)

# Deploy Cloud Run service with VPC connector and IAM database auth.
subprocess.run([
    'gcloud', 'run', 'deploy', 'ipl-scorecard-api',
    '--source',          'src/',              # Cloud Build builds the container
    '--region',          REGION,
    '--project',         PROJECT,
    '--service-account', SA_EMAIL,
    '--vpc-connector',   'ipl-connector',     # route to Cloud SQL private IP
    '--vpc-egress',      'all-traffic',
    '--no-allow-unauthenticated',             # authenticated via Entra ID or API key
    '--set-secrets',     'GOOGLE_CLOUD_PROJECT=ipl-project-id:latest',
], check=True)
print('Cloud Run service deployed with VPC connector and IAM database auth.')

Expected Results

  • curl https://ipl-scorecard-api-HASH-em.a.run.app/api/scores/IPL2024FINAL with an Authorization header returns a JSON array of innings records from the Cloud SQL database.
  • The Cloud SQL instance’s Connections tab shows connections from the Cloud Run connector IP range (10.8.0.0/28) and no connections from public IPs, confirming the private-only network configuration.
  • Cloud Audit Logs show the sa-cloudrun-scorecard service account acquiring a Cloud SQL IAM token and a Secret Manager accessSecretVersion event on each cold start, confirming no passwords are stored in the container or environment variables.
  • Attempting to connect to the Cloud SQL instance from outside the VPC (using the public IP, which does not exist) returns a connection refused error, confirming the private-only configuration.
  • The Cloud Run service’s revision shows VPC Connector: ipl-connector in its configuration details, confirming the connector attachment.

Pro Tip

Use the Cloud SQL Python Connector’s connection pooling by initialising the Connector object and the connection pool outside the request handler function, in the module scope, so they are reused across warm invocations. Creating a new connector and pool on every request adds 100 to 300 milliseconds of connection overhead per request. The pool maintains warm connections to Cloud SQL across invocations, amortising connection setup cost over the Cloud Run instance’s warm lifetime.

Warning: The VPC Serverless Connector’s --vpc-egress all-traffic flag routes all Cloud Run outbound traffic through the VPC, including traffic to external APIs and Google services. This prevents Cloud Run from reaching Google services like Secret Manager directly via their public endpoints. Ensure Private Google Access is enabled on the connector’s subnet so that traffic to Google APIs (secretmanager.googleapis.com, run.googleapis.com) routes through the private Google network rather than failing due to no internet access from the private VPC. Without Private Google Access, Cloud Run services behind the connector cannot reach GCP managed services.

Lesson 28 of 40
0% complete