Production GKE Platform — From Zero to Production›04 · VPC-native networking & IP planning

Lesson 04 of 18 · Part 2 — Build the platform

VPC-native networking & IP planning

Plan GKE IP ranges so clusters never run out: how VPC-native clusters use alias ranges, how max Pods per node decides how many nodes fit, how to add Pod ranges later, which address space to use, and how Dataplane V2 enforces network policy.

Practitioner → Advanced
Key wordsVPC-nativealias IPPod rangeService rangemax Pods per nodeIP exhaustionadditional Pod rangesdiscontiguous multi-Pod CIDRnon-RFC 1918Class EDataplane V2network policyIP masquerading
Subnet in the (Shared) VPC, one region Primary range 10.10.0.0/22 node IPs (1,000+ nodes) Pod range (secondary) 10.20.0.0/16 a /24 per node at 110 Pods Service range 10.30.0.0/20 ClusterIPs Additional Pod range added later, per node pool Rule of thumb max nodes = Pod range size / per-node block (/16 Pod range, /24 per node -> 256 nodes) the per-node block comes from max Pods per node: 110 -> /24, 32 -> /26
A VPC-native cluster takes node IPs from the subnet's primary range, Pod IPs from a secondary range (a block per node), and Service IPs from another range.

How VPC-native clusters use IPs

In a VPC-native cluster (the default and the only sensible choice today), Pods and Services get IPs from alias IP ranges of the subnet:

Range Used for Sized by
Subnet primary range Node IPs, internal load balancers Max nodes (+ headroom for surge upgrades)
Pod secondary range A block per node for its Pods Max nodes × per-node block
Service range ClusterIP Services Number of Services

Pod IPs are real VPC addresses: routable across the VPC, Shared VPC, peering and hybrid links, visible in flow logs and firewall rules. That's the big advantage, and it's also why the ranges must fit the organisation's IP plan.

Each node is a block of flats, and each flat needs a postbox number. GKE hands every block a whole street's worth of numbers when it's built, even if only a few people move in. Run out of streets, and no new block can be built, however empty the existing ones are.

The rule that surprises everyone

GKE gives each node a Pod block twice the size of its max Pods per node (rounded to a power of two), so Pods can be replaced without reusing IPs too fast:

Max Pods per node Block per node Nodes in a /16 Pod range Nodes in a /20
110 (Standard default) /24 (256) 256 16
64 /25 (128) 512 32
32 (Autopilot default) /26 (64) 1024 64
16 /27 (32) 2048 128

Max nodes = Pod range size / per-node block. A cluster that "only" runs 300 Pods on 16 nodes can still be full: the 17th node can't get a block. Lowering max Pods per node on new node pools stretches the range; existing pools keep their setting.

Sizing a cluster

  1. Estimate max nodes at peak, including surge during upgrades (each pool can add maxSurge nodes at once) and autoscaling headroom.
  2. Choose max Pods per node from your real density (most clusters run 20–60 Pods per node).
  3. Pod range = max nodes × block, rounded up. Service range: a /20 (4,096 Services) suits most clusters.
  4. Primary range: max nodes + internal load balancers + headroom.

Example: 300 nodes at 64 max Pods → 300 × /25 = 38,400 addresses → a /16 Pod range (65,536). Primary: a /23 (512).

Address space

  • RFC 1918 space (10/8, 172.16/12, 192.168/16) is often crowded in large organisations.
  • GKE also supports non-RFC 1918 ranges, including Class E (240.0.0.0/4) and privately used public ranges, for Pods. They are routable inside your VPC but not usable on the internet. Check on-prem equipment before sending such ranges over hybrid links: some firewalls and routers drop Class E.
  • Pods talking to destinations outside the VPC may be masqueraded (SNAT) to the node IP depending on the destination; the ip-masq-agent configuration decides which ranges keep Pod IPs.
  • IPv6 (dual-stack) clusters remove the shortage for Pods, but every system they talk to must support IPv6.

Running out, and fixing it

Symptoms: new nodes fail to be created, the cluster autoscaler logs scale-up failures about IP space, Pods stay Pending while nodes look half empty.

Fixes, least disruptive first:

  1. Additional Pod ranges (discontiguous multi-Pod CIDR): add a secondary range to the subnet, and create new node pools that use it (--pod-ipv4-range). Newer versions can also add ranges cluster-wide for auto-provisioned pools.
  2. Lower max Pods per node on new pools, then move workloads and delete old pools.
  3. As a last resort, a new cluster with a proper plan, and migrate via GitOps.

You can't resize the original Pod or Service range in place, which is why the plan matters.

Dataplane V2 and network policy

Dataplane V2 (eBPF, based on Cilium) is the default for Autopilot and recommended for new Standard clusters (it's chosen at creation):

  • Replaces kube-proxy and iptables for Service routing.
  • Enforces Kubernetes NetworkPolicy natively, and can log allowed and denied connections.
  • Supports FQDN network policies in recent versions (GKE Enterprise features may apply; check your version).

A starting set of policies per namespace:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny
  namespace: shop
spec:
  podSelector: {}
  policyTypes: [ Ingress, Egress ]
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns
  namespace: shop
spec:
  podSelector: {}
  policyTypes: [ Egress ]
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kube-system
      ports:
        - { protocol: UDP, port: 53 }
        - { protocol: TCP, port: 53 }

Then allow each real flow explicitly (ingress from the load balancer's health-check and proxy ranges, egress to the database). The Kubernetes Networking track explains policy design in depth.

Try it: fill up a Pod range on purpose

  1. Create a Standard zonal cluster with a /24 Pod range and max Pods per node 110 (one /24 block per node).
  2. Scale the node pool to 2 and read the cluster operation error and the events.
  3. Add a secondary range pods-2 (/22) to the subnet and create a node pool with --pod-ipv4-range pods-2 --max-pods-per-node 32.
  4. Scale it to 4 nodes and check each node's podCIDR.
  5. On a Dataplane V2 cluster, apply default-deny to a namespace, watch a curl fail, then add an allow rule.

Going deeper: IP plans at organisation scale

  • Keep an IP address management record (even a reviewed YAML file in Git) for every VPC, subnet, Pod and Service range across clouds and on-prem.
  • Give each cluster its own Pod and Service ranges, even when they share a subnet, so you can route, firewall and troubleshoot per cluster.
  • For many short-lived clusters (CI, previews), use Class E or a dedicated non-routed space for Pods.

Recap

  • VPC-native clusters use the subnet's primary range for nodes and secondary ranges for Pods and Services.
  • Each node takes a Pod block of 2 × max Pods per node: max nodes = Pod range / block.
  • Fix exhaustion with additional Pod ranges on new node pools, or lower max Pods per node.
  • Consider non-RFC 1918 / Class E space and IPv6 when private space is scarce.
  • Dataplane V2 enforces network policy with eBPF; start every namespace from default-deny.

This site is a public version of my personal engineering knowledge hub. It intentionally excludes confidential company information and internal operational details.