LearnKubernetes
Kubernetes9. Networking·EXPERT·6 min read

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
  • ingressClassName disambiguates between multiple Ingress Controllers; omitting it causes unpredictable routing behavior in multi-controller clusters
  • pathType: Exact matches single path, Prefix matches /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/v1 does not match
  • Prefix: Match /api and /api/v1 and /api/v1/users; /apiextra does 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 ingressClassName on 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: HTTPS annotation) 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 /api should NOT match /apiextra. If this happens, controller may use naive string prefix matching.
  • Fix: Use pathType: Exact if 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.

🔒 Locked
Sign in and unlock the full syllabus to keep reading.
Sign in to continue
Knowledge check
5 scenario questions on this topic
Take the quiz →
Related in 9. Networking
DNS Fundamentals
INTERMEDIATE
CoreDNS
EXPERT
Kubernetes Networking
EXPERT
Gateway API
ADVANCED