Containers and Kubernetes are all the rage, but if you are new, you might still be wondering how Kubernetes actually connects your applications to the outside world and across your cluster. This article walks through that progression, from pods, services, ingress, and the modern Gateway API, in a way that maps to how real clusters handle traffic.
What is Kubernetes?
Kubernetes is an open-source platform for orchestrating containers at scale. It handles scheduling, self-healing, scaling, and (very importantly) networking for distributed applications. It is what teams use to deploy microservices in production.
In Kubernetes, your running workloads live in pods: the smallest deployable unit. If there’s one thing to know about pods: they are ephemeral. Kubernetes can create, re…
Containers and Kubernetes are all the rage, but if you are new, you might still be wondering how Kubernetes actually connects your applications to the outside world and across your cluster. This article walks through that progression, from pods, services, ingress, and the modern Gateway API, in a way that maps to how real clusters handle traffic.
What is Kubernetes?
Kubernetes is an open-source platform for orchestrating containers at scale. It handles scheduling, self-healing, scaling, and (very importantly) networking for distributed applications. It is what teams use to deploy microservices in production.
In Kubernetes, your running workloads live in pods: the smallest deployable unit. If there’s one thing to know about pods: they are ephemeral. Kubernetes can create, reschedule, or replace them at any time. That means their IP addresses can (and will) change. This isn’t a bug; it’s a feature that enables resilience. But it also introduces a networking challenge.
Pods and the Need for Stable Networking
Pods can come and go, so how do applications talk to each other reliably? If an application pod is replaced, its IP changes and other services shouldn’t break. Kubernetes solves this with stable Service objects.
A Service acts as a consistent endpoint for a group of pods. It gets a stable IP (called a ClusterIP) and can load-balance traffic to healthy pods behind it. This lets your services discover and talk to each other regardless of pod churn.
But there’s one big limitation: Kubernetes Services are in-cluster only. That means they help internal communication, but they don’t expose your application to the outside world on their own.
Ingress: The Classic North-South Gateway To expose HTTP(S) services outside the cluster, Kubernetes introduced Ingress. An Ingress resource contains rules for routing external HTTP(S) traffic into your cluster. For example:
rules:
- host: example.com
http:
paths:
- path: /
backend:
service:
name: web-service
port:
number: 80
Ingress comes with a controller (like NGINX, Traefik, or cloud-provider ingress controllers) that implements these rules and actually handles traffic.
Ingress was a huge step forward: it standardised the way many applications are exposed externally without resorting to ServiceType LoadBalancer for every service. But it also had limitations:
- Ingress was focused strictly on HTTP/HTTPS.
- It had limited expressiveness for advanced routing.
- Many features were controller-specific (e.g., annotations).
- Multiple teams touching ingress rules can cause configuration conflicts.
These shortcomings became clearer as Kubernetes adoption grew.
Enter Gateway API: The Next Generation
The Gateway API is a family of Kubernetes custom resource definitions (CRDs) designed to evolve and replace the old Ingress API with something more flexible, extensible, and expressive. It is being adopted across the industry as a future-proof way to manage traffic in Kubernetes.
Instead of one overloaded resource doing everything, responsibilities are explicit. To understand whether this actually improves things in practice, I built a small project.
Project goal: This project demonstrates how Kubernetes Gateway API improves traffic management compared to Ingress by deploying a multi-service application and exposing it externally using NGINX Gateway Fabric.
Prerequisites
- A running Kubernetes cluster (kind, minikube, or managed)
- kubectl
- helm
- Basic understanding of Kubernetes YAML
- Willingness to debug version mismatches
Step-by-Step Walkthrough
-
I carried out this walkthrough on an EC2 instance to simulate a realistic cloud environment. I launched an instance with sufficient memory and storage, then connected to it remotely using VS Code over SSH.
-
Before installing any tools, I updated the system’s package index to ensure I was working with the latest available versions.
I installed Kind, Docker, Helm, and kubectl, which are required for this walkthrough.
I installed the core tools needed for this walkthrough:
Docker – to run containers
Kind – to run Kubernetes locally inside Docker
kubectl – to interact with the Kubernetes cluster
Helm – to install the NGINX Gateway Fabric controller
-
I created a cluster named
gateway-api-demousing a config file.
I installed the gateway API CRD, which introduced several new resource types that will be used by Nginx Gateway Fabric.
I went ahead to install the nginx gateway fabric controller using Helm, and verified the resources. This automatically creates a gateway class
-
I deployed three simple Python-based HTTP servers to represent different device-specific frontends, all in the same namespace.
-
I created the gateway, which implements listener and traffic forwarding and also references the gatewayclass.
-
Describing the gateway shows that the NGINX gateway fabric saw the gateway, agrees it is valid, successfully created the underlying NGINX config + Service, exposed it on the network, and has zero attached routes.
-
This also applies to the gatewayclass.
-
At this point, there are resources for the controller and the gateway on the
ngf-gatewayapi-nsnamespace. -
Then I accessed the application on my browser via : (I added the port in my security group to allow inbound traffic).
I was unable to access the application from above because there was no backend service to route the traffic to; this is where the httproute comes in.
The httproute is responsible for forwarding requests to backend services; it references the gateway through the parentRefs attribute. I created 3 services for my application.
I was able to access the application as traffic was routed through the gateway and its proxy pods