To make a secure connection from serverless environments to private services (e.g. Compute Engine, Cloud Sql), we can use the Google Cloud-provided Serverless VPC connector. Serverless VPC Access allows your serverless environment to send requests to your VPC network using Google Cloud’s internal DNS and internal IP addressing systems. The responses to these requests also use your internal network.
Serverless VPC Access consists of a connector resource that is actually a cluster of VM instances ( f1-micro, e2-micro,e2-standard-4). Larger machine types provide more throughput.
You can configure the minimum and maximum number of VM instances for this cluster with a maximum number of instances of 10.
Note: Serverless VPC Access automatically scales out the number of instances in your connector as traffic increases. However, the number of connectors does not scale in. To prevent connectors from scaling out more than desired, set the maximum number of instances to as low number as possible/reasonable. If your connector has scaled out and you need to scale it back down, recreate the connector with the desired number of instances. This link explains the VPC connector throughput as per the machine size and number of instances.
When configuring the Serverless VPC connector, you need to associate the connector to a VPC network and assign an IP range. Below is the example code (in Terraform) for setting up a VPC connector in a GCP project.
Enable the API in the Google project:
resource "google_project_service" "vpcaccess" {
project = var.project_id
service = "vpcaccess.googleapis.com"
disable_on_destroy = false
}
Create the VPC network:
resource "google_compute_network" "custom-vpc" {
project = var.project_id
name = "test-vpc"
auto_create_subnetworks = false
}
Create a specific subnet for VPC access:
resource "google_compute_subnetwork" "vpc-access-subnet" {
project = var.project_id
name = "vpc-access-subnet"
ip_cidr_range = "10.100.1.32/28"
region = "us-central1"
network = google_compute_network.custom-vpc.id
}
Create VPC connector:
resource "google_vpc_access_connector" "connector" {
project = var.project_id
region = "us-central1"
name = "vpc-conn"
subnet {
name = google_compute_subnetwork.vpc-access-subnet.name
}
machine_type = "e2-standard-4"
}
You must configure /28 IP CIDR Range, unused inside the same VPC (example 192.168.1.0/28). The IP range must not overlap with any existing Subnet range inside the VPC.
You cannot use a different mask other than /28, and the reason behind it rests in the maximum configuration. You can have up to 10 connector instances; if you require more throughput, you will need to increase the machine type of the instances.
Note:
After creating the serverless VPC connector, you need to configure your serverless environment to use this VPC connector to connect with your VPC network. To specify a connector during deployment, use the --vpc-connector flag. Below is an example of a Cloud Function service in Python. A similar approach can be used for App Engine and Cloud Run. Below is an example of setting up a cloud function. This function is going to provide the IP address for a given hostname. In this case, we should be able to get the IP address of a private compute VM from this cloud function because of serverless VPC access.
Sample source code for cloud function to get the IP of a hostname created in a private VPC using internal DNS.
from flask import Flask, request, jsonify
import socket
app = Flask(__name__)
def get_ip_address(hostname):
try:
ip_address = socket.gethostbyname(hostname)
return ip_address
except socket.error as e:
return None
@app.route('/')
def get_ip(self):
hostname = request.args.get('hostname', '')
if not hostname:
return jsonify({'error': 'Hostname parameter is missing'}), 400
ip_address = get_ip_address(hostname)
if ip_address:
return jsonify({'hostname': hostname, 'ip_address': ip_address})
else:
return jsonify({'error': 'Failed to retrieve IP address for the given hostname'}), 500
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
Create the zip archive for the code to be deployed in the cloud function:
resource "google_storage_bucket" "bucket" {
project = var.project_id
name = "test-bucket-vpc"
location = "US"
}
data "archive_file" "main" {
type = "zip"
output_path = pathexpand("${var.source_directory}.zip")
source_dir = pathexpand(var.source_directory)
}
resource "google_storage_bucket_object" "archive" {
name = "${data.archive_file.main.output_md5}-${basename(data.archive_file.main.output_path)}"
bucket = google_storage_bucket.bucket.name
source = data.archive_file.main.output_path
content_disposition = "attachment"
content_type = "application/zip"
}
resource "google_project_service" "function" {
project = var.project_id
service = "cloudfunctions.googleapis.com"
disable_on_destroy = false
}
resource "google_cloudfunctions_function" "function" {
project = var.project_id
region = "us-central1"
name = "test-function"
description = "My test function"
runtime = "python310"
available_memory_mb = 128
source_archive_bucket = google_storage_bucket.bucket.name
source_archive_object = google_storage_bucket_object.archive.name
trigger_http = true
entry_point = "get_ip"
vpc_connector = google_vpc_access_connector.connector.id
depends_on = [
google_project_service.function
]
}
resource "google_cloudfunctions_function_iam_member" "invoker" {
project = google_cloudfunctions_function.function.project
region = google_cloudfunctions_function.function.region
cloud_function = google_cloudfunctions_function.function.name
role = "roles/cloudfunctions.invoker"
member = "allUsers"
}
Now, we can create a test VM, which will be created on a private VPC network. In the Terraform configuration below, we are creating the subnet “demovm-subnet” and creating the VM instance for demovm within the subnet.
resource "google_compute_subnetwork" "demovm-subnet" {
project = var.project_id
name = "demovm-subnet"
ip_cidr_range = "10.99.1.0/24"
region = "us-central1"
network = google_compute_network.custom-vpc.id
}
resource "google_compute_instance" "demovm" {
project = var.project_id
name = "demovm"
machine_type = "e2-micro"
zone = "us-central1-a"
boot_disk {
initialize_params {
image = "ubuntu-os-cloud/ubuntu-2004-lts"
}
}
network_interface {
subnetwork = google_compute_subnetwork.demovm-subnet.self_link
}
}
Now, it's time to test our configuration. If we access the function URL with the hostname as an argument, we should get the IP address details. We are using the FQDN for the hostname, which accesses the VPC-scoped private DNS zone to get the IP of the host.
https:///test-function?hostname=demovm.c..internal
Serverless VPC access is a great way to connect Google-provided services to private network services. There are still some points which should be considered while designing the architecture.
Reference Links: https://cloud.google.com/vpc/docs/serverless-vpc-access
Ready to optimize your use of Google Cloud's AI tools?