Ingress
Ingress
TL;DR
- Ingress operates at L7; provides host/path-based HTTP routing, TLS termination, and single external entry point for many backend Services
- An Ingress object without a running Ingress Controller is inert—stored successfully but has zero effect; deployment of controller is essential
ingressClassNamedisambiguates between multiple Ingress Controllers; omitting it causes unpredictable routing behavior in multi-controller clusters- pathType:
Exactmatches single path,Prefixmatches /api and /api/v1 (but not /apiextra) - Backend communication is plain HTTP by default; implement backend re-encryption or mutual TLS separately if encryption needed
- cert-manager automates TLS certificate provisioning and renewal based on Ingress Secret references
Core Concepts
Ingress vs. Service
Service (L4): Exposes pods via ClusterIP/NodePort/LoadBalancer. Layer 4 (TCP/UDP). Cannot route on HTTP host or path.
Ingress (L7): Routes HTTP/HTTPS traffic to Services based on hostname and path. Layer 7 (HTTP/HTTPS). Provides TLS termination. Single external IP routes to many backends.
Ingress Architecture
Client → External LoadBalancer (or NodePort) → Ingress Controller Pod → Backend Service → Pod
The Ingress object is a declarative spec; an Ingress Controller (nginx, Istio, AWS ALB) watches for Ingress objects and programs actual proxies.
ingressClassName
Specifies which Ingress Controller should handle the Ingress object. On clusters with multiple controllers, omitting ingressClassName creates ambiguity: which controller should respond? Behavior becomes unpredictable.
pathType Matching
Exact: Match only/api;/api/v1does not matchPrefix: Match/apiand/api/v1and/api/v1/users;/apiextradoes not match (path-segment aware)ImplementationSpecific: Controller-dependent (usually same as Prefix)
TLS Termination in Ingress
Ingress terminates HTTPS from client → Ingress Controller. Backend communication to pods is plain HTTP by default (not encrypted). If encrypted backend communication is needed, implement separately (e.g., Istio mTLS, backend TLS re-encryption).
spec:
tls:
- hosts:
- api.example.com
secretName: api-tls-cert
The Secret api-tls-cert must contain tls.crt and tls.key (PEM format).
Comparison Table
| Aspect | Service | Ingress |
|---|---|---|
| OSI Layer | L4 (TCP/UDP) | L7 (HTTP/HTTPS) |
| Routing granularity | Port/protocol only | Host/path-based |
| TLS termination | No | Yes |
| External IP | Direct Service IP | External LB IP |
| Multi-backend routing | No (one backend IP/port) | Yes (many Services) |
| Requires Controller | No (built-in) | Yes (separate Ingress Controller) |
Command Reference
Ingress Manifest
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-gateway
namespace: production
spec:
ingressClassName: nginx
tls:
- hosts:
- api.example.com
secretName: api-tls-cert
rules:
- host: api.example.com
http:
paths:
- path: /users
pathType: Prefix
backend:
service:
name: user-service
port:
number: 8080
- path: /orders
pathType: Prefix
backend:
service:
name: order-service
port:
number: 8080
Essential Commands
# List Ingress resources
kubectl get ingress
kubectl describe ingress <ingress-name>
# Check Ingress status (shows external IP)
kubectl get ingress -o wide
# List available IngressClasses
kubectl get ingressclass
# Check Ingress Controller pods
kubectl get pods -n kube-system | grep -i ingress
# Or namespace where controller is installed
kubectl get pods -n ingress-nginx
# View Ingress Controller logs
kubectl logs -n ingress-nginx deployment/nginx-ingress-controller
# Verify backend Service endpoints
kubectl get endpoints <service-name>
# Test Ingress routing (port-forward to controller)
kubectl port-forward -n ingress-nginx svc/nginx-ingress-controller 8080:80
curl -H "Host: api.example.com" http://localhost:8080/users
Create TLS Secret Manually
# Generate self-signed cert
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout tls.key -out tls.crt -subj "/CN=api.example.com"
# Create Secret
kubectl create secret tls api-tls-cert --cert=tls.crt --key=tls.key
# Reference in Ingress
# ... tls:
# - secretName: api-tls-cert
Common Pitfalls
Pitfall: Ingress Object Created but No External IP
Creating an Ingress, but kubectl get ingress shows no external IP and traffic doesn’t reach backend.
- Why: No Ingress Controller is running in the cluster. Ingress objects are declarative specs; without a controller watching and responding, they’re inert. API server accepts them but nothing acts on them.
- Fix: Install an Ingress Controller (nginx, AWS ALB Controller, etc.) appropriate for your cluster. Verify controller pods are running:
kubectl get pods -n ingress-nginx(or controller’s namespace).
Pitfall: Multiple Ingress Controllers, Ambiguous Behavior Cluster has both nginx and Istio ingress controllers; Ingress objects route inconsistently.
- Why: Without
ingressClassName, which controller should handle the Ingress is ambiguous. Both controllers may attempt to handle it, or neither does, depending on controller defaults. - Fix: Always specify
ingressClassNameon Ingress objects. Verify controller name:kubectl get ingressclass.
Pitfall: Backend Pod Cannot Connect (Plain HTTP Assumed) Ingress Controller fails to reach backend pods; logs show connection refused or TLS errors.
- Why: Backend communication defaults to plain HTTP. If backend pods expect HTTPS, the controller’s plain HTTP connections fail.
- Fix: Either backend pods accept HTTP on separate port, or configure backend re-encryption (controller-specific, e.g., nginx
backend-protocol: HTTPSannotation) or mutual TLS (via Istio or similar).
Pitfall: TLS Secret Missing or Expired HTTPS requests to Ingress fail; browser shows certificate error or connection refused.
- Why: TLS Secret referenced in Ingress doesn’t exist or contains invalid cert/key. Ingress Controller cannot load the cert; HTTPS listener fails to start.
- Fix: Verify Secret exists:
kubectl get secret <secret-name>. Verify contents:kubectl get secret <secret-name> -o yaml | grep tls.crt | head -1. Use cert-manager to automate certificate provisioning and renewal.
Pitfall: pathType Prefix Matches Unintended Paths
Path /api is configured with pathType: Prefix. Request to /apiextra unexpectedly matches.
- Why: Naive prefix matching includes
/apiextra. Kubernetes pathType Prefix is path-segment aware (respects/boundaries), so/apishould NOT match/apiextra. If this happens, controller may use naive string prefix matching. - Fix: Use
pathType: Exactif exact match required. Verify controller’s pathType implementation (nginx and most controllers follow spec correctly). Use more specific path if needed:/api/instead of/api.
Interview Questions
Q — An Ingress object is created successfully, but traffic doesn’t route. What’s the likely cause and how would you diagnose it?
The Ingress Controller is likely not running. Ingress is declarative spec stored in etcd; without a controller watching for Ingress objects and programming actual proxies, the spec has no effect. Diagnose: Check controller pods running in their namespace (usually ingress-nginx, istio-system, or similar). Verify ingressClassName in Ingress matches an available IngressClass. Check controller logs for errors. If no controller is installed, install one (e.g., helm install ingress-nginx nginx-ingress-helm-repo/nginx-ingress).
Q — What is the difference between Service and Ingress? When would you use each? Service operates at L4 (TCP/UDP), exposes pods via stable IP/port, but cannot route based on HTTP hostname or path. Ingress operates at L7 (HTTP/HTTPS), routes traffic based on hostname and path, provides TLS termination, and enables multiple backend Services behind a single external IP. Use Service for basic pod exposure; use Ingress for HTTP-based routing (web apps, APIs with multiple backend services). An Ingress internally uses Services; it routes to Service names, not pods directly.
Q — Explain TLS termination in Ingress. Where is traffic encrypted, and where is it plain? TLS termination occurs at the Ingress Controller. Client-to-Ingress traffic is HTTPS (encrypted). Ingress-to-backend-pod traffic is plain HTTP by default (unless re-encryption is explicitly configured). The TLS Secret referenced in the Ingress contains the public certificate and private key; the controller uses these to establish HTTPS with clients. To encrypt backend communication, implement mutual TLS (via service mesh like Istio) or configure controller-specific backend re-encryption annotations.
Q — Why is ingressClassName essential on multi-controller clusters? What happens without it?
On clusters with multiple Ingress Controllers (e.g., nginx and Istio), ingressClassName specifies which controller should handle a given Ingress object. Without it, the cluster doesn’t know which controller should respond, causing ambiguous behavior: one controller might handle it, both might attempt to handle it, or neither might. This leads to unpredictable routing and configuration conflicts. Always specify ingressClassName to explicitly assign Ingress to the intended controller.