Kubernetes Ingress doesn’t actually do any load balancing itself; it’s just a set of rules that tells an external load balancer how to route traffic.
Let’s see this in action. Imagine you have a simple web application deployed in Kubernetes. You’ve got a Deployment for your pods and a Service of type ClusterIP to give them a stable internal IP.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-web-app
spec:
replicas: 3
selector:
matchLabels:
app: my-web-app
template:
metadata:
labels:
app: my-web-app
spec:
containers:
- name: web
image: nginx:1.21.6 # A simple web server
ports:
- containerPort: 80
apiVersion: v1
kind: Service
metadata:
name: my-web-app-svc
spec:
selector:
app: my-web-app
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
Right now, my-web-app-svc is only accessible within your Kubernetes cluster. To expose it to the outside world, we need an Ingress resource and an Ingress controller. The Ingress controller is the actual software (like Nginx, Traefik, or HAProxy) that runs inside your cluster and watches for Ingress resources. When it sees one, it configures itself to act as a load balancer.
Here’s an Ingress resource that directs traffic for myapp.example.com to our my-web-app-svc:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-web-app-ingress
spec:
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-web-app-svc
port:
number: 80
When you apply this Ingress resource, your chosen Ingress controller (let’s assume it’s the Nginx Ingress Controller, a very common choice) will read it. The controller will then typically provision an external load balancer (like an AWS ELB, GCP Load Balancer, or a MetalLB IP address if you’re on-prem) and configure it to forward incoming HTTP/S traffic for myapp.example.com to the Nginx Ingress Controller’s own service, which is usually of type LoadBalancer. The Ingress controller then uses the rules defined in your Ingress resource to route that traffic to the correct internal Kubernetes Service (my-web-app-svc in this case), and from there to one of your application pods.
The Ingress controller is the key here. It’s a Deployment itself, running in your cluster, often exposed via a Service of type: LoadBalancer. This service provides the external IP address that you point your DNS records to. The Ingress controller then dynamically updates its internal configuration (e.g., Nginx’s nginx.conf) based on the Ingress resources it finds.
The problem Ingress solves is centralizing external access. Instead of creating a separate LoadBalancer service for every single application, you have one or a few Ingress controllers that manage routing for many different applications based on hostname and path. This drastically simplifies your external IP management and firewall rules.
You can also define TLS termination at the Ingress level. This means your Ingress controller handles the SSL/TLS handshake, decrypting traffic before sending it to your backend pods. This offloads certificate management from your application pods.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-web-app-ingress-tls
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true" # Example annotation for Nginx
spec:
tls:
- hosts:
- myapp.example.com
secretName: myapp-tls-secret # A Kubernetes Secret containing your TLS cert and key
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-web-app-svc
port:
number: 80
Here, myapp-tls-secret would be a Kubernetes Secret of type kubernetes.io/tls containing your certificate (tls.crt) and private key (tls.key). The Ingress controller will use these to serve HTTPS traffic for myapp.example.com.
The most surprising thing about Kubernetes Ingress is that it’s not a built-in Kubernetes object that does the routing; it’s purely a specification that describes the routing. The actual routing logic is implemented by an Ingress controller, which is itself a separate piece of software you deploy into your cluster. Without an Ingress controller running, your Ingress resources do absolutely nothing.
You can configure complex routing scenarios, like path-based routing (/api to one service, /app to another) or hostname-based routing (api.example.com to one set of services, www.example.com to another), all within the Ingress resource. The Ingress controller then translates these high-level rules into the specific configuration for its underlying proxy (e.g., Nginx, HAProxy).
The next thing you’ll likely run into is dealing with advanced Ingress controller features, such as custom annotations for fine-grained control, or migrating to a more robust Ingress solution like Istio or Linkerd for service mesh capabilities.