GCP VPC networks are not just a way to connect your resources; they’re the fundamental fabric that dictates how your applications communicate, scale, and, most importantly, stay secure.
Let’s see this in action. Imagine we’re setting up a simple web application. We’ll have a frontend tier and a backend tier.
resource "google_compute_network" "main_vpc" {
name = "my-app-vpc"
auto_create_subnetworks = false
}
resource "google_compute_subnetwork" "frontend_subnet" {
name = "frontend-subnet"
region = "us-central1"
network = google_compute_network.main_vpc.id
ip_cidr_range = "10.10.1.0/24"
}
resource "google_compute_subnetwork" "backend_subnet" {
name = "backend-subnet"
region = "us-central1"
network = google_compute_network.main_vpc.id
ip_cidr_range = "10.10.2.0/24"
}
resource "google_compute_instance" "frontend_vm" {
name = "frontend-vm"
machine_type = "e2-medium"
zone = "us-central1-a"
tags = ["frontend"]
boot_disk {
initialize_params {
image = "debian-cloud/debian-11"
}
}
network_interface {
subnetwork = google_compute_subnetwork.frontend_subnet.id
}
}
resource "google_compute_instance" "backend_vm" {
name = "backend-vm"
machine_type = "e2-medium"
zone = "us-central1-a"
tags = ["backend"]
boot_disk {
initialize_params {
image = "debian-cloud/debian-11"
}
}
network_interface {
subnetwork = google_compute_subnetwork.backend_subnet.id
}
}
Here, we’ve created a VPC named my-app-vpc and explicitly defined two subnets: frontend-subnet in 10.10.1.0/24 and backend-subnet in 10.10.2.0/24. We then launched two VMs, tagging one as frontend and the other as backend, placing them in their respective subnets.
The core problem VPCs solve is providing a private, isolated network for your cloud resources. Without it, your VMs would be exposed directly to the internet, or you’d be managing complex, brittle VPNs. VPCs abstract this away, offering a familiar networking model within GCP.
Internally, a VPC network in GCP is a global resource. However, the subnets within it are regional. When you create a VM, you attach it to a network interface within a specific subnet. Traffic between VMs within the same VPC but different subnets is routed by GCP’s global network fabric.
The key levers you control are:
- Subnet CIDR Ranges: These define the IP address space available within a region. Careful planning here prevents overlaps and ensures sufficient capacity.
- Firewall Rules: This is where security is enforced. You define ingress and egress rules based on network tags, service accounts, or IP ranges.
- Routes: GCP automatically creates routes for subnets and some external connections. You can add custom static routes for more complex routing scenarios.
- Network Peering: For connecting VPCs in different projects or organizations.
- Private Google Access: Allows VMs without external IPs to reach Google APIs and services.
The most surprising thing about GCP firewall rules is their implicit deny behavior for ingress traffic. You must explicitly allow what you want to permit. This is a powerful security posture, but it means you can’t just "open things up" without thinking. Every rule has a purpose.
Consider the frontend VM. By default, it can’t receive any traffic from the internet. To allow HTTP traffic, we’d add a firewall rule like this:
resource "google_compute_firewall" "allow_frontend_http" {
name = "allow-frontend-http"
network = google_compute_network.main_vpc.name
direction = "INGRESS"
priority = 1000
source_ranges = ["0.0.0.0/0"] # Allow from anywhere
allow {
protocol = "tcp"
ports = ["80"]
}
target_tags = ["frontend"]
}
This rule allows TCP traffic on port 80 from any IP address (0.0.0.0/0) to any VM tagged with frontend.
Now, let’s say our backend VMs need to talk to our frontend VMs. This is an egress scenario from the backend’s perspective, but ingress from the frontend’s. We need to allow the backend subnet to reach the frontend subnet.
resource "google_compute_firewall" "allow_backend_to_frontend" {
name = "allow-backend-to-frontend"
network = google_compute_network.main_vpc.name
direction = "INGRESS"
priority = 1000
source_ranges = ["10.10.2.0/24"] # The backend subnet CIDR
allow {
protocol = "tcp"
ports = ["8080"] # Assuming backend API on port 8080
}
target_tags = ["frontend"] # Apply to frontend VMs
}
This rule allows traffic from the backend_subnet’s IP range (10.10.2.0/24) to reach VMs tagged as frontend on port 8080. Notice how we use target_tags to apply the rule to the correct set of VMs.
The "secret sauce" for scalability with VPCs often lies in how you segment your network and leverage GCP’s global infrastructure. For instance, using multiple subnets across different regions, each with its own carefully crafted firewall rules, allows you to distribute your application and maintain strict security boundaries. You can then use global load balancers to direct traffic to the nearest healthy instance, all managed within your VPC.
The next step in mastering GCP networking is understanding how to connect multiple VPCs securely and efficiently using VPC Network Peering and Shared VPC.