How to connect Cloud Run to a Private Cloud SQL Instance

Connecting Google Cloud Run to a private Cloud SQL instance requires careful networking configuration to ensure secure, private communication between your containerised application and database. This guide walks through the essential steps using Terraform infrastructure as code.

Overview

A private Cloud SQL instance provides enhanced security by eliminating public IP addresses and restricting access to only resources within your VPC network. This approach is ideal for production workloads where database security is paramount.

Source Code

You could find all code related to this post in Github.

Implementation

For connecting Cloud run to a private Cloud SQL instance we are going to follow these 5 steps:

  1. Create a Private VPC Network – Set up a custom VPC network with dedicated subnets for your resources
  2. Configure VPC Peering for Cloud SQL – Establish private service networking connection to allow Cloud SQL access
  3. Deploy Private Cloud SQL Instance – Create the database instance with private IP only (no public access)
  4. Configure Cloud Run with VPC Access – Connect your Cloud Run job to the private network and mount Cloud SQL volumes
  5. Set Up Service Account Permissions – Grant necessary IAM roles for Cloud SQL client access

1. Private VPC Network Setup

First, let’s create a dedicated VPC network with custom subnets:

resource "google_compute_network" "private_network" {
  name                    = "private-network"
  project                 = var.project
  auto_create_subnetworks = false
}

resource "google_compute_subnetwork" "default" {
  project       = var.project
  name          = "test-subnetwork"
  ip_cidr_range = "10.2.0.0/16"
  region        = var.region
  network       = google_compute_network.private_network.id
}

2. VPC Peering for Cloud SQL

Configure VPC peering to allow Cloud SQL to communicate within your private network:

resource "google_compute_global_address" "private_ip_address" {
  name          = "private-ip-address"
  project       = var.project
  purpose       = "VPC_PEERING"
  address_type  = "INTERNAL"
  prefix_length = 16
  network       = google_compute_network.private_network.id
}

resource "google_service_networking_connection" "private_vpc_connection" {
  network                 = google_compute_network.private_network.id
  service                 = "servicenetworking.googleapis.com"
  reserved_peering_ranges = [google_compute_global_address.private_ip_address.name]
}

3. Private Cloud SQL Instance

Create the Cloud SQL instance with private networking enabled:

resource "google_sql_database_instance" "postgres_instance" {
  name                = var.instance_name
  database_version    = var.database_version
  region              = var.region
  project             = var.project
  deletion_protection = false

  depends_on = [google_service_networking_connection.private_vpc_connection]

  settings {
    tier = var.tier

    ip_configuration {
      ipv4_enabled    = false
      private_network = google_compute_network.private_network.id
    }

    disk_size       = var.disk_size
    disk_type       = var.disk_type
    disk_autoresize = false
  }
}

Key configuration points:
– Set `ipv4_enabled = false` to disable public IP access
– Reference your private network in `private_network`

4. Cloud Run Job with VPC Access

Configure your Cloud Run job to connect to the private network:

resource "google_cloud_run_v2_job" "k6" {
  project  = var.project
  name     = "k6-test"
  location = var.region

  depends_on = [docker_image.k6, docker_registry_image.k6]

  template {
    parallelism = 1
    task_count  = 1
    template {
      service_account = google_service_account.k6.email
      timeout         = "86400s"
      max_retries     = 1

      vpc_access {
        network_interfaces {
          network = google_compute_network.private_network.name
          subnetwork = google_compute_subnetwork.default.name
        }
      }

      containers {
        env {
          name  = "DB_USER"
          value = var.db_user
        }
        env {
          name  = "DB_PASSWORD"
          value = var.db_password
        }
        env {
          name  = "DB_HOST"
          value = google_sql_database_instance.postgres_instance.private_ip_address
        }
        env {
          name  = "DB_NAME"
          value = var.database_name
        }

        image = "${local.docker-image}:latest"
        args  = ["/k6", "run", "script.js"]
       
        volume_mounts {
          name       = "cloudsql"
          mount_path = "/cloudsql"
        }
      }

      volumes {
        name = "cloudsql"
        cloud_sql_instance {
          instances = [google_sql_database_instance.postgres_instance.connection_name]
        }
      }
    }
  }
}

Key configuration points:

  • Create a Cloud SQL connector by mounting the cloud sql volume
  • Run Cloud Run in the network created in step 1

5. Service Account Permissions

Ensure your Cloud Run service account has the necessary permissions:

resource "google_service_account" "k6" {
  project      = var.project
  account_id   = "ksix-sa"
  display_name = "Service Account"
}

resource "google_project_iam_member" "k6_sql_client" {
  project = var.project
  role    = "roles/cloudsql.client"
  member  = "serviceAccount:${google_service_account.k6.email}"
}

Connection Methods

To connect from Cloud Run to your Cloud SQL instance you can use the private IP address of your Cloud SQL instance:

const connectionString = `postgresql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:5432/${DB_NAME}?sslmode=disable`;

Where `DB_HOST` is the private IP address from `google_sql_database_instance.postgres_instance.private_ip_address`.

Summary

This configuration provides a secure, scalable foundation for connecting Cloud Run applications to private Cloud SQL instances while maintaining network isolation and following Google Cloud security best practices.

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.