← Back to posts

GCP ACE: GKE Networking — Ingress and NetworkPolicy

Earlier in this series, exposing a workload meant a LoadBalancer Service — one external IP, one Service, no routing logic. That’s fine for a single app. Real deployments usually need to route different paths or hostnames to different Services behind one IP, and to restrict which Pods can talk to which. That’s Ingress and NetworkPolicy.

Ingress — one IP, many Services

GKE Ingress is GKE’s built-in, managed Ingress controller. When you create an Ingress object, the controller provisions an actual HTTP(S) Application Load Balancer and configures it to route traffic to your Services based on the rules you wrote — no manual load balancer setup.

Two Ingress types in GKE:

TypeWhat it creates
ExternalA classic Application Load Balancer — internet-facing and deployed globally on Google’s edge network
InternalAn internal Application Load Balancer, reachable only from within your VPC, backed by NEGs

Note the word classic: external GKE Ingress doesn’t create the newer global external Application Load Balancer (EXTERNAL_MANAGED) — that’s one of the things Gateway API gives you. And because the internal variant is Envoy-based, the region needs a proxy-only subnet before an internal Ingress can be created (the same requirement covered in the load balancing article).

Ingress vs. a LoadBalancer Service: a LoadBalancer Service exposes exactly one Service on one external IP with no routing rules. An Ingress sits a layer above Services — one external IP, with a URL map that routes /api to one Service and / to another, by path or by host.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-ingress
spec:
  rules:
  - http:
      paths:
      - path: /*
        pathType: ImplementationSpecific
        backend:
          service:
            name: my-products
            port:
              number: 60000
      - path: /discounted
        pathType: ImplementationSpecific
        backend:
          service:
            name: my-discounted-products
            port:
              number: 80

This routes everything to my-products:60000, except /discounted, which goes to my-discounted-products:80. Under the hood, the controller builds a URL map with one path rule per entry, each pointing at a backend service whose NEGs hold the actual Pod IPs.

One detail worth flagging for current and future exam versions: GKE Ingress is in maintenance mode. Google now recommends the Gateway API for new HTTP(S) exposure — it’s the only option of the two that supports certificates managed by Certificate Manager. Ingress still appears on exam questions as the classic answer, but don’t be surprised to see Gateway API show up as the “more modern” correct answer on newer question sets.

NetworkPolicy — Pod-level firewalling

By default, every Pod in a cluster can talk to every other Pod, with no restriction. A NetworkPolicy adds firewall rules at the Pod level — useful for namespace-level tenant isolation or basic defense in depth (so a compromised Pod in one namespace can’t freely reach a database Pod in another).

Enabling enforcement on GKE depends on your dataplane:

gcloud container clusters update CLUSTER_NAME --update-addons=NetworkPolicy=ENABLED
gcloud container clusters update CLUSTER_NAME --enable-network-policy

Enabling only the add-on enforces nothing by itself — both commands are required. Turning on enforcement also recreates the cluster’s nodes, which can disrupt running workloads, so plan it like a maintenance operation.

Example policy restricting ingress traffic by source:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-non-pod-traffic
spec:
  podSelector: {}               # applies to every Pod in the namespace
  ingress:
  - from:
    - ipBlock:
        cidr: '0.0.0.0/0'
        except: ['10.95.0.0/17']   # the cluster's Pod IP range

Google’s own guidance: prefer podSelector and namespaceSelector over ipBlock for selecting sources, because Pod IP ranges are ephemeral and will drift out from under a hardcoded CIDR. ipBlock rules also behave differently depending on whether the cluster runs Dataplane V2 or Calico — another reason to default to label-based selectors.

Exam Takeaways

Sources

↗ LinkedIn