Redis with Shared VPC and Private Service Access
Imagine that we wish a Memorystore redis managed instance to be available to a number of our projects. How might we achieve this?
One answer is to leverage Shared VPC and Private Service Access. Let us now look at a high level topology diagram and then we will explain its parts followed by the recipe to create it.
Our goal is to create a Cloud Memorystore instance. We will create that in a project called data-tier which owns our data service components. Cloud Memorystore is a managed service so although we create it in the data-tier project, we still need to consider its networking considerations. Since we want the service to be reachable by multiple projects, we need a routing story that allows such. This is where we bring in Shared VPC. We create a Shared VPC Network that we call “shared-networking” and within that create a subnet called “data-tier-us-central1”. In the shared-networking Shared VPC, we add two Service Projects called “app-tier” and “service-tier”.
- Create three projects
- shared-networking
- data-tier
- app-tier
2. In Console, open each of the three new projects and enable Compute Engine.
3. Open cloud console to the shared-networking project.
4. Delete the Default network (we don’t need it)
5. Create a VPC network called shared-networking with a subnet called data-tier-us-central1 in region us-central1. Specify an IP address range of 10.0.0.0/16.
The end result will be:
6. Enable Private Service connection
Drill down into the shared-networking … VPC network. Click the Private service connection:
Enable the Service Networking API:
Allocate an IP range
Select Automatic to allow Google to choose the range.
Create Private connections to services:
7. Create a firewall rule to open port 22 on the network to allow incoming SSH.
8. Create a Shared VPC
9. Enable Service Networking API in the project called data-tier.
10. Open a Cloud Shell in the data-tier project.
11. Create a Memory store instance. Currently, Redis can only be configured to use Private Service Access by creating it through gcloud. There is not yet a Cloud Console mechanism to create it in this mode. We will need to find the project ids for the projects we called data-tier and shared-networking.
gcloud redis instances create myinst \
--size=5 \
--region=us-central1 \
--project=data-tier-XXX \
--network=projects/shared-networking-XXX/global/networks/shared-networking \
--connect-mode=private-service-access
You may be asked to enable the redis API. When completed, we can look at our Memorystore — Redis instances and see the new instance.
Notice that the instance’s IP address is taken from the allocated IP range for Private Service connections.
12. Switch to the app-tier project in Cloud Console.
13. Create a Compute Engine attached to the data-tier-us-central1 subnet.
We create a micro instance for testing:
—
We attach the micro instance to the data-tier-us-central1 subnet that is part of the Shared VPC in which we created the Redis instance.
14. SSH into the newly created Compute Engine. Our goal is to test that we have access to Redis.
15. Install telnet.
sudo apt-get install telnet
16. Telnet to the Redis instance:
telnet 10.96.0.3 6379
enter
INFO
You will now see the details of the Redis instance.
enter
QUIT
to exit from the Redis connection.
To further illustrate the story, here is a YouTube video showing all of the steps necessary to create the Redis instance in a Shared VPC:
Here is an example Terraform script that can be used as a basis for building the solution:
variable "billing_account" {
type = string
default = "XXXX"
}variable "org-id" {
type = string
default = "XXXX"
}output "redis_ip" {
description = "IP of redis"
value = google_redis_instance.cache.host
}provider "google" {
}resource "google_project" "shared-networking" {
name = "shared-networking"
project_id = "XXX-shared-networking"
org_id = var.org-id
billing_account = var.billing_account
}resource "google_project" "data-tier" {
name = "data-tier"
project_id = "XXX-data-tier"
org_id = var.org-id
billing_account = var.billing_account
}resource "google_project" "app-tier" {
name = "app-tier"
project_id = "XXX-app-tier"
org_id = var.org-id
billing_account = var.billing_account
}resource "google_project_service" "compute-shared-networking" {
project = google_project.shared-networking.project_id
service = "compute.googleapis.com"
}resource "google_project_service" "compute-data-tier" {
project = google_project.data-tier.project_id
service = "compute.googleapis.com"
}resource "google_project_service" "redis-data-tier" {
project = google_project.data-tier.project_id
service = "redis.googleapis.com"
}resource "google_project_service" "compute-app-tier" {
project = google_project.app-tier.project_id
service = "compute.googleapis.com"
}resource "google_project_service" "enable-service-networking" {
project = google_project.shared-networking.project_id
service = "servicenetworking.googleapis.com"
}resource "google_project_service" "enable-service-networking-data-tier" {
project = google_project.data-tier.project_id
service = "servicenetworking.googleapis.com"
}resource "google_compute_network" "shared-networking" {
project = google_project.shared-networking.project_id
name = "shared-networking"
auto_create_subnetworks = false
}resource "google_compute_subnetwork" "data-tier-us-central1" {
name = "data-tier-us-central1"
ip_cidr_range = "10.0.0.0/16"
region = "us-central1"
network = google_compute_network.shared-networking.id
project = google_project.shared-networking.project_id
}resource "google_compute_global_address" "managed-data-services" {
name = "managed-data-services"
address_type = "INTERNAL"
purpose = "VPC_PEERING"
prefix_length = 16
network = google_compute_network.shared-networking.self_link
project = google_project.shared-networking.project_id
}resource "google_service_networking_connection" "private-connection" {
network = google_compute_network.shared-networking.id
service = "servicenetworking.googleapis.com"
reserved_peering_ranges = [google_compute_global_address.managed-data-services.name]
}resource "google_compute_firewall" "ssh" {
name = "allow-ingress-tcp-22-shared-networking"
network = google_compute_network.shared-networking.name
project = google_project.shared-networking.project_id
source_ranges = ["0.0.0.0/0"]
allow {
protocol = "tcp"
ports = ["22"]
}
}resource "google_compute_shared_vpc_host_project" "host" {
project = google_project.shared-networking.project_id
}resource "google_compute_shared_vpc_service_project" "service1" {
host_project = google_compute_shared_vpc_host_project.host.project
service_project = google_project.app-tier.project_id
}resource "google_compute_shared_vpc_service_project" "service2" {
host_project = google_compute_shared_vpc_host_project.host.project
service_project = google_project.data-tier.project_id
}resource "google_redis_instance" "cache" {
name = "memory-cache"
memory_size_gb = 5
project = google_project.data-tier.project_id
region = "us-central1"
authorized_network = google_compute_network.shared-networking.self_link
connect_mode = "PRIVATE_SERVICE_ACCESS"
}resource "google_compute_instance" "client" {
name = "client"
machine_type = "f1-micro"
zone = "us-central1-a"
project = google_project.app-tier.project_id
boot_disk {
initialize_params {
image = "debian-cloud/debian-11"
}
}network_interface {
access_config {
// Ephemeral public IP
}
subnetwork = google_compute_subnetwork.data-tier-us-central1.self_link
}
}