Back to posts

Shared VPC with Google Managed Services on GKE

Read the full guide on docs.beyondyou.my.id
gcpgkekubernetesshared-vpcnetworkingprivate-service-access

GCP Shared VPC — Private Service Access for Google Managed Services

Table of Contents

SectionTopicDescription
01Architecture OverviewShared VPC model, host vs service projects, and Private Service Access fundamentals.
02Prerequisites & API EnablementRequired APIs, service agents, and project-level preparation.
03Private Service Access ConfigurationIP range reservation, VPC peering, and service connection setup.
04IAM Bindings for Managed ServicesGranting networkUser to each managed service agent on the host project.
05Cloud Composer Shared VPCManaged Airflow (Gen 3) in Shared VPC environments.
06Verification & TroubleshootingValidating peering, IP ranges, and common failure modes.
07Best PracticesProduction-grade patterns, security hardening, and operational guidance.

1. Architecture Overview

Shared VPC lets you centralize network administration in a host project while allowing multiple service projects to attach and use those networks. This separation enforces budgeting and access control boundaries at the project level while enabling secure cross-project communication over private IPs.

Why Shared VPC for Managed Services?

Google managed services (Cloud SQL, Memorystore, AlloyDB, Vertex AI, etc.) need private connectivity to your VPC. Without Shared VPC, each service project would need its own VPC peering — creating network fragmentation. Shared VPC solves this by:

  • Centralizing IP range management in one host project
  • Eliminating per-project VPC peering for each managed service
  • Enabling private-only access (no public internet exposure)
  • Simplifying firewall rule and DNS management

Host vs Service Project

RoleResponsibility
Host ProjectOwns the VPC network, subnets, IP ranges, and VPC peering connections. Administered by the networking team.
Service ProjectHosts the managed services (Cloud SQL, Composer, GKE). Attached to the host project via Shared VPC.
graph TB
    subgraph HOST["HOST PROJECT"]
        subgraph VPC["VPC Network (shared-vpc)"]
            subnetA["Subnet A\n10.0.0.0/24"]
            subnetB["Subnet B\n10.0.1.0/24"]
            managedRange["Google Managed Services Range\n10.100.0.0/16 (VPC_PEERING)"]
            psc["Private Service Connection\nservicenetworking.googleapis.com"]
        end
    end

    subgraph SVC_A["SERVICE PROJECT A"]
        sql["Cloud SQL"]
    end

    subgraph SVC_B["SERVICE PROJECT B"]
        composer["Composer (Airflow)"]
    end

    subgraph SVC_C["SERVICE PROJECT C"]
        gke["GKE"]
    end

    sql -->|"Shared VPC"| VPC
    composer -->|"Shared VPC"| VPC
    gke -->|"Shared VPC"| VPC

2. Prerequisites & API Enablement

2.1 Required APIs

Enable the following APIs in both host and service projects:

Host Project:

gcloud services enable \
  compute.googleapis.com \
  servicenetworking.googleapis.com \
  cloudresourcemanager.googleapis.com \
  --project=[HOST_PROJECT_ID]

Service Project:

gcloud services enable \
  compute.googleapis.com \
  servicenetworking.googleapis.com \
  sqladmin.googleapis.com \
  redis.googleapis.com \
  memcache.googleapis.com \
  file.googleapis.com \
  alloydb.googleapis.com \
  aiplatform.googleapis.com \
  cloudbuild.googleapis.com \
  --project=[SERVICE_PROJECT_ID]

2.2 Create Service Agents

Service agents must exist before you can grant them IAM roles. Create them for each managed service your service project will use:

services=(
  "sqladmin.googleapis.com"
  "redis.googleapis.com"
  "memcache.googleapis.com"
  "file.googleapis.com"
  "alloydb.googleapis.com"
  "aiplatform.googleapis.com"
  "cloudbuild.googleapis.com"
)

for service in "${services[@]}"; do
  gcloud beta services identity create \
    --service="$service" \
    --project=[SERVICE_PROJECT_ID]
done

Important: Run these commands in the service project. The service agents are created in the service project but will be granted roles on the host project.


3. Private Service Access Configuration

Private Service Access (PSA) creates a VPC peering connection between your VPC and Google’s managed services network. This is how managed services like Cloud SQL, Memorystore, and AlloyDB communicate with your applications privately.

3.1 Reserve an IP Range

Reserve a /16 CIDR block for Google managed services in the host project:

gcloud compute addresses create google-managed-services \
  --global \
  --purpose=VPC_PEERING \
  --prefix-length=16 \
  --addresses=10.100.0.0 \
  --network=[NETWORK_NAME] \
  --project=[HOST_PROJECT_ID] \
  --description="Reserved range for Google managed services"

Choosing the right range: Use RFC 1918 ranges that don’t overlap with your existing subnets, VPN tunnels, or Cloud Interconnect ranges. A /16 gives you 65,536 addresses — sufficient for most multi-service deployments.

3.2 Create the Private Service Connection

Establish the VPC peering with Google’s service networking API:

gcloud services vpc-peerings connect \
  --service=servicenetworking.googleapis.com \
  --ranges=google-managed-services \
  --network=[NETWORK_NAME] \
  --project=[HOST_PROJECT_ID]

3.3 Verify Peering Status

gcloud services vpc-peerings list \
  --network=[NETWORK_NAME] \
  --project=[HOST_PROJECT_ID]

The peering should show ACTIVE state. If it shows CREATING, wait a few minutes and recheck.


4. IAM Bindings for Managed Services

Each managed service has a dedicated service agent that needs roles/compute.networkUser on the host project to access the VPC. Always add roles — never replace existing roles.

Cloud SQL

gcloud projects add-iam-policy-binding [HOST_PROJECT_ID] \
  --member="serviceAccount:service-[SERVICE_PROJECT_NUMBER]@gcp-sa-cloud-sql.iam.gserviceaccount.com" \
  --role="roles/compute.networkUser"

Memorystore Redis

gcloud projects add-iam-policy-binding [HOST_PROJECT_ID] \
  --member="serviceAccount:service-[SERVICE_PROJECT_NUMBER]@cloud-redis.iam.gserviceaccount.com" \
  --role="roles/compute.networkUser"

Memorystore Memcached

gcloud projects add-iam-policy-binding [HOST_PROJECT_ID] \
  --member="serviceAccount:service-[SERVICE_PROJECT_NUMBER]@cloud-memcache.iam.gserviceaccount.com" \
  --role="roles/compute.networkUser"

Filestore

gcloud projects add-iam-policy-binding [HOST_PROJECT_ID] \
  --member="serviceAccount:service-[SERVICE_PROJECT_NUMBER]@cloud-filer.iam.gserviceaccount.com" \
  --role="roles/compute.networkUser"

AlloyDB

gcloud projects add-iam-policy-binding [HOST_PROJECT_ID] \
  --member="serviceAccount:service-[SERVICE_PROJECT_NUMBER]@gcp-sa-alloydb.iam.gserviceaccount.com" \
  --role="roles/compute.networkUser"

Vertex AI

gcloud projects add-iam-policy-binding [HOST_PROJECT_ID] \
  --member="serviceAccount:service-[SERVICE_PROJECT_NUMBER]@gcp-sa-aiplatform.iam.gserviceaccount.com" \
  --role="roles/compute.networkUser"

Cloud Build

gcloud projects add-iam-policy-binding [HOST_PROJECT_ID] \
  --member="serviceAccount:service-[SERVICE_PROJECT_NUMBER]@gcp-sa-cloudbuild.iam.gserviceaccount.com" \
  --role="roles/compute.networkUser"

GKE (if using private clusters)

gcloud projects add-iam-policy-binding [HOST_PROJECT_ID] \
  --member="serviceAccount:service-[SERVICE_PROJECT_NUMBER]@container-engine-robot.iam.gserviceaccount.com" \
  --role="roles/compute.networkUser"

5. Cloud Composer Shared VPC

Managed Airflow (Composer Gen 3) uses Shared VPC differently than other managed services. Instead of Private Service Access, it uses network attachments to connect to the host project’s VPC.

5.1 Host Project Setup

Enable Shared VPC and attach the service project:

# Enable Shared VPC on the host project
gcloud compute shared-vpc enable [HOST_PROJECT_ID]

# Attach the service project
gcloud compute shared-vpc associated-projects add \
  [SERVICE_PROJECT_ID] \
  --host-project=[HOST_PROJECT_ID]

5.2 Required APIs

Host Project:

gcloud services enable \
  container.googleapis.com \
  compute.googleapis.com \
  --project=[HOST_PROJECT_ID]

Service Project:

gcloud services enable \
  container.googleapis.com \
  compute.googleapis.com \
  iam.googleapis.com \
  iamcredentials.googleapis.com \
  --project=[SERVICE_PROJECT_ID]

5.3 Grant Composer Service Agent Permissions

# Grant the Composer Service Agent the sharedVpcAgent role
gcloud projects add-iam-policy-binding [HOST_PROJECT_ID] \
  --member="serviceAccount:service-[SERVICE_PROJECT_NUMBER]@cloudcomposer-accounts.iam.gserviceaccount.com" \
  --role="roles/composer.sharedVpcAgent"

5.4 DNS Limitations

  • Managed Airflow (Gen 3) has a limitation of one transitive DNS hop. Ensure your DNS configuration respects this.
  • Managed Airflow (Gen 3) does not support user-defined .internal DNS zones. If you create a .internal zone, Managed Airflow cannot resolve it.

6. Verification & Troubleshooting

Validate VPC Peering

gcloud services vpc-peerings list \
  --network=[NETWORK_NAME] \
  --project=[HOST_PROJECT_ID]

Validate Reserved IP Ranges

gcloud compute addresses list \
  --global \
  --filter="purpose=VPC_PEERING" \
  --project=[HOST_PROJECT_ID]

Validate IAM Bindings

gcloud projects get-iam-policy [HOST_PROJECT_ID] \
  --flatten="bindings[].members" \
  --format="table(bindings.role, bindings.members)" \
  --filter="bindings.members:service-[SERVICE_PROJECT_NUMBER]"

Common Issues

SymptomCauseFix
FAILED_PRECONDITION when creating Cloud SQLMissing networkUser role on host projectGrant roles/compute.networkUser to the Cloud SQL service agent
VPC peering stuck in CREATINGIP range conflict or quota exceededCheck for overlapping CIDRs; verify peering quota
DNS resolution fails across projectsToo many transitive DNS hopsFlatten DNS architecture; use Cloud DNS private zones
Composer environment creation failsMissing composer.sharedVpcAgent roleGrant the role to the Composer service agent in the host project

7. Best Practices

IP Range Planning

  • Reserve a dedicated /16 for managed services (e.g., 10.100.0.0/16)
  • Keep managed service ranges separate from GKE pod/service ranges
  • Document all reserved ranges in a central IPAM or wiki
  • Use non-overlapping ranges if you have multiple Shared VPC hosts

Security

  • Never expose managed services to the public internet. Use Private IP only.
  • Apply VPC Service Controls around sensitive projects (Cloud SQL, AlloyDB)
  • Use Organization Policy constraints to restrict public IP on managed services
  • Audit IAM bindings regularly — only grant networkUser to services that need it

Operational

  • Automate with Terraform. Manual gcloud commands are error-prone at scale
  • Use a single host project per environment (dev/staging/prod) to simplify networking
  • Monitor VPC peering status with Cloud Monitoring alerts
  • Tag all managed service instances for cost allocation

Terraform Example (Private Service Access)

# Reserve IP range for managed services
resource "google_compute_global_address" "google_managed_services" {
  name          = "google-managed-services"
  purpose       = "VPC_PEERING"
  address_type  = "INTERNAL"
  prefix_length = 16
  address       = "10.100.0.0"
  network       = google_compute_network.host_vpc.id
}

# Create the private service connection
resource "google_service_networking_connection" "private_vpc_connection" {
  network                 = google_compute_network.host_vpc.id
  service                 = "servicenetworking.googleapis.com"
  reserved_peering_ranges = [google_compute_global_address.google_managed_services.name]
}

Terraform Example (Shared VPC)

# Enable Shared VPC on host project
resource "google_compute_shared_vpc_host_project" "host" {
  project = var.host_project_id
}

# Attach service project
resource "google_compute_shared_vpc_service_project" "service" {
  host_project    = google_compute_shared_vpc_host_project.host.project
  service_project = var.service_project_id
}

References