GKE Networking#
GKE networking centers on VPC-native clusters, where pods and services get IP addresses from VPC subnet ranges. This integrates Kubernetes networking directly into Google Cloud’s VPC, enabling native routing, firewall rules, and load balancing without extra overlays.
VPC-Native Clusters and Alias IP Ranges#
VPC-native clusters use alias IP ranges on the subnet. You allocate two secondary ranges: one for pods, one for services.
# Create subnet with secondary ranges
gcloud compute networks subnets create gke-subnet \
--network my-vpc \
--region us-central1 \
--range 10.0.0.0/20 \
--secondary-range pods=10.4.0.0/14,services=10.8.0.0/20
# Create cluster using those ranges
gcloud container clusters create my-cluster \
--region us-central1 \
--network my-vpc \
--subnetwork gke-subnet \
--cluster-secondary-range-name pods \
--services-secondary-range-name services \
--enable-ip-aliasThe pod range needs to be large. A /14 gives about 262,000 pod IPs. Each node reserves a /24 from the pod range (256 IPs, 110 usable pods per node). If you have 100 nodes, that consumes 100 /24 blocks. Undersizing the pod range is a common cause of IP exhaustion – the cluster cannot add nodes even though VMs are available.
Shared VPC#
In multi-project setups, Shared VPC lets a host project own the VPC while service projects run GKE clusters in it. This centralizes network management (firewall rules, subnets, Cloud NAT) in one project.
# In the host project, share the VPC
gcloud compute shared-vpc enable host-project
# Associate the service project
gcloud compute shared-vpc associated-projects add service-project \
--host-project host-projectThe GKE service account in the service project needs compute.networkUser on the shared subnet and container.hostServiceAgentUser on the host project. Missing IAM bindings are the number one cause of Shared VPC cluster creation failures.
GKE Gateway API#
Gateway API is the successor to Ingress in GKE. It provides multi-cluster routing, traffic splitting, and header-based matching that Ingress cannot do. Google recommends Gateway API for all new deployments.
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: external-gateway
namespace: infra
annotations:
networking.gke.io/certmap: my-cert-map
spec:
gatewayClassName: gke-l7-global-external-managed
listeners:
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
options:
networking.gke.io/cert-manager-certs: wildcard-cert
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: app-route
namespace: production
spec:
parentRefs:
- name: external-gateway
namespace: infra
hostnames:
- "app.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: api-service
port: 8080
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: web-frontend
port: 80GKE Gateway classes:
gke-l7-global-external-managed– Global external HTTP(S) load balancer (Envoy-based)gke-l7-regional-external-managed– Regional external HTTP(S) load balancergke-l7-rilb– Internal HTTP(S) load balancergke-l7-gxlb– Classic global external (legacy)
GKE Ingress (Legacy)#
If you are using the built-in GKE Ingress controller (not nginx), it provisions a Google Cloud Load Balancer:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
annotations:
kubernetes.io/ingress.class: "gce"
kubernetes.io/ingress.global-static-ip-name: "my-static-ip"
networking.gke.io/managed-certificates: "my-managed-cert"
spec:
rules:
- host: app.example.com
http:
paths:
- path: /*
pathType: ImplementationSpecific
backend:
service:
name: web-frontend
port:
number: 80Note the /* path – GKE Ingress uses ImplementationSpecific path matching, not standard Prefix. This is a common source of confusion when migrating from nginx ingress.
Container-Native Load Balancing with NEGs#
By default, GKE load balancers route to node IPs, which then kube-proxy forwards to pods. Network Endpoint Groups (NEGs) bypass this by routing directly to pod IPs, reducing an extra hop and improving latency.
NEGs are enabled automatically when using Gateway API. For Ingress, annotate the Service:
apiVersion: v1
kind: Service
metadata:
name: web-frontend
annotations:
cloud.google.com/neg: '{"ingress": true}'
spec:
type: ClusterIP
selector:
app: web-frontend
ports:
- port: 80
targetPort: 8080With NEGs, the load balancer health checks pods directly. Your pods must respond to health checks on the serving port. If they do not, the NEG marks all endpoints as unhealthy and the service returns 502s.
Cloud NAT for Private Clusters#
Private cluster nodes have no external IPs, so they cannot reach the internet (for pulling images from Docker Hub, for example) without Cloud NAT:
# Create a Cloud Router
gcloud compute routers create my-router \
--network my-vpc \
--region us-central1
# Create Cloud NAT
gcloud compute routers nats create my-nat \
--router my-router \
--region us-central1 \
--auto-allocate-nat-external-ips \
--nat-all-subnet-ip-rangesThis allows all subnets (including pod and service ranges) to reach external addresses. For tighter control, use --nat-custom-subnet-ip-ranges to specify only the GKE subnet.
Cloud Armor WAF Integration#
Cloud Armor applies WAF rules at the load balancer level, before traffic reaches your cluster. Create a security policy and attach it via BackendConfig (Ingress) or GCPBackendPolicy (Gateway API):
# For Gateway API
apiVersion: networking.gke.io/v1
kind: GCPBackendPolicy
metadata:
name: app-backend-policy
namespace: production
spec:
default:
securityPolicy: my-armor-policy
targetRef:
group: ""
kind: Service
name: web-frontend# Create the Cloud Armor policy
gcloud compute security-policies create my-armor-policy
# Add OWASP ModSecurity rules
gcloud compute security-policies rules create 1000 \
--security-policy my-armor-policy \
--expression "evaluatePreconfiguredExpr('sqli-v33-stable')" \
--action deny-403
# Rate limiting
gcloud compute security-policies rules create 2000 \
--security-policy my-armor-policy \
--src-ip-ranges="*" \
--action throttle \
--rate-limit-threshold-count 100 \
--rate-limit-threshold-interval-sec 60 \
--conform-action allow \
--exceed-action deny-429Managed SSL Certificates#
Google-managed certificates automate TLS provisioning and renewal:
apiVersion: networking.gke.io/v1
kind: ManagedCertificate
metadata:
name: my-managed-cert
spec:
domains:
- app.example.com
- api.example.comReference it in your Ingress annotation: networking.gke.io/managed-certificates: my-managed-cert. The certificate provisions automatically once DNS points to the load balancer IP. Provisioning takes 15-60 minutes. Check status with kubectl describe managedcertificate my-managed-cert.