Ingress Controllers

Medium 20 min read

Understanding Ingress Controllers

Ingress Controllers are the front door to your Kubernetes cluster

They act as a Layer 7 load balancer, routing external HTTP/HTTPS traffic to the right services based on hostnames, paths, and other rules.

Internet

External traffic from users and clients hits your cluster's public endpoint.

Ingress Controller

L7 load balancer that inspects HTTP headers, paths, and hostnames to make routing decisions.

Ingress Rules

Declarative routing configuration that maps hosts and paths to backend services.

Services

Backend applications that receive the routed traffic from the Ingress Controller.

Why Ingress Controllers?

SSL/TLS Termination

Centralized HTTPS handling with automatic certificate management and secure connections.

Path-based Routing

Route requests based on URL paths, enabling microservice architectures with single entry point.

Virtual Hosting

Host multiple domains on the same cluster with intelligent host-based routing.

Load Balancing

Distribute traffic across backend services with health checks and failover capabilities.

URL Rewriting

Transform and manipulate URLs before forwarding to backend services.

Rate Limiting

Protect against DDoS attacks with configurable rate limiting and throttling.

Core Ingress Concepts

Basic Ingress Resource

simple-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: simple-ingress
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  rules:
  - host: app.example.com
    http:
      paths:
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 8080
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend-service
            port:
              number: 80

Path Types Explained

path-types.yaml
paths:
- path: /api/v1/users
  pathType: Exact          # Only /api/v1/users, nothing else
- path: /api/
  pathType: Prefix         # /api/anything matches
- path: /legacy
  pathType: ImplementationSpecific  # Controller decides

NGINX Ingress Controller

Installation

install-nginx.sh
# Add NGINX Ingress repository
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

# Install NGINX Ingress Controller
helm install nginx-ingress ingress-nginx/ingress-nginx \
  --namespace ingress-nginx \
  --create-namespace \
  --set controller.service.type=LoadBalancer \
  --set controller.metrics.enabled=true

# Verify installation
kubectl get pods -n ingress-nginx
kubectl get svc -n ingress-nginx

Rate Limiting and Security

secure-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: secure-ingress
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/limit-rps: "100"
    nginx.ingress.kubernetes.io/limit-connections: "10"
    nginx.ingress.kubernetes.io/enable-cors: "true"
    nginx.ingress.kubernetes.io/configuration-snippet: |
      more_set_headers "X-Frame-Options: SAMEORIGIN";
      more_set_headers "X-Content-Type-Options: nosniff";
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - secure.example.com
    secretName: secure-tls
  rules:
  - host: secure.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: secure-service
            port:
              number: 80

Canary Deployments

canary-ingress.yaml
# Main Production Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: production-ingress
spec:
  rules:
  - host: app.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: app-stable
            port:
              number: 80

---
# Canary Ingress (10% traffic)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: canary-ingress
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "10"
spec:
  rules:
  - host: app.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: app-canary
            port:
              number: 80

Traefik Ingress Controller

Installation and Configuration

install-traefik.sh
# Add Traefik repository
helm repo add traefik https://helm.traefik.io/traefik
helm repo update

# Install Traefik
helm install traefik traefik/traefik \
  --namespace traefik \
  --create-namespace \
  --set dashboard.enabled=true \
  --set service.type=LoadBalancer

# Access Dashboard (port-forward)
kubectl port-forward -n traefik \
  $(kubectl get pods -n traefik --selector "app.kubernetes.io/name=traefik" -o name) \
  9000:9000

Traefik IngressRoute

ingressroute.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: app-ingressroute
spec:
  entryPoints:
    - web
    - websecure
  routes:
  - match: Host(`app.example.com`) && PathPrefix(`/api`)
    kind: Rule
    services:
    - name: api-service
      port: 8080
    middlewares:
    - name: api-stripprefix
    - name: rate-limit
  - match: Host(`app.example.com`)
    kind: Rule
    services:
    - name: frontend-service
      port: 80
  tls:
    certResolver: letsencrypt

Traefik Middlewares

middlewares.yaml
# Strip Prefix Middleware
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: api-stripprefix
spec:
  stripPrefix:
    prefixes:
      - /api

---
# Rate Limiting Middleware
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: rate-limit
spec:
  rateLimit:
    average: 100
    burst: 50

---
# Security Headers
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: security-headers
spec:
  headers:
    frameDeny: true
    sslRedirect: true
    browserXssFilter: true
    contentTypeNosniff: true

SSL/TLS Configuration

Automatic HTTPS with cert-manager

cert-manager automates certificate lifecycle

It watches for Ingress resources with specific annotations and automatically provisions and renews TLS certificates from Let's Encrypt or other ACME-compatible CAs.

cert-manager-setup.yaml
# Install cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.12.0/cert-manager.yaml

# ClusterIssuer for Let's Encrypt
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: admin@example.com
    privateKeySecretRef:
      name: letsencrypt-prod
    solvers:
    - http01:
        ingress:
          class: nginx

---
# Ingress with automatic TLS
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: tls-ingress
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - app.example.com
    secretName: app-tls
  rules:
  - host: app.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: app-service
            port:
              number: 80

Advanced Patterns

Blue-Green Deployments

blue-green.yaml
# Blue Environment (Current)
apiVersion: v1
kind: Service
metadata:
  name: app-blue-service
spec:
  selector:
    app: myapp
    version: blue
  ports:
  - port: 80

---
# Green Environment (New)
apiVersion: v1
kind: Service
metadata:
  name: app-green-service
spec:
  selector:
    app: myapp
    version: green
  ports:
  - port: 80

---
# Ingress (Switch service name for deployment)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
spec:
  rules:
  - host: app.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: app-blue-service  # Change to app-green-service
            port:
              number: 80

API Gateway Pattern

api-gateway.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api-gateway
  annotations:
    nginx.ingress.kubernetes.io/enable-cors: "true"
    nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, PUT, DELETE, OPTIONS"
    nginx.ingress.kubernetes.io/rate-limit: "100"
    nginx.ingress.kubernetes.io/auth-url: "http://auth-service.default.svc.cluster.local/verify"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - api.example.com
    secretName: api-tls
  rules:
  - host: api.example.com
    http:
      paths:
      - path: /api/v1/users
        pathType: Prefix
        backend:
          service:
            name: user-service
            port:
              number: 8080
      - path: /api/v1/products
        pathType: Prefix
        backend:
          service:
            name: product-service
            port:
              number: 8080
      - path: /api/v1/orders
        pathType: Prefix
        backend:
          service:
            name: order-service
            port:
              number: 8080

Controller Comparison

Feature NGINX Ingress Traefik HAProxy Ingress
Performance Excellent Good Excellent
Configuration Annotations CRDs/Annotations Annotations
Dashboard None (3rd party) Built-in Basic stats
Let's Encrypt Via cert-manager Built-in Via cert-manager
Canary Deployments Native support Weighted routing Limited support
Best For High-performance, mature Dynamic config, modern Enterprise, complex LB

Security Best Practices

  • Always use TLS/SSL in production environments
  • Implement rate limiting to prevent abuse and DDoS attacks
  • Use Web Application Firewall (WAF) for additional protection
  • Regular security updates for Ingress Controllers
  • Implement proper authentication and authorization
  • Monitor and log all ingress traffic for security analysis

Practice Exercises

Easy Create a Basic Ingress Resource

Write an Ingress manifest that routes myapp.example.com to a service called myapp-svc on port 80.

You need an Ingress with a single host rule and one path entry using pathType Prefix.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myapp-ingress
spec:
  ingressClassName: nginx
  rules:
  - host: myapp.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: myapp-svc
            port:
              number: 80

Easy Add TLS to an Existing Ingress

Modify an Ingress resource to enable HTTPS using a TLS secret called myapp-tls and redirect HTTP to HTTPS.

Add a tls block to the spec and use the ssl-redirect annotation.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myapp-ingress
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - myapp.example.com
    secretName: myapp-tls
  rules:
  - host: myapp.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: myapp-svc
            port:
              number: 80

Medium Multi-Service Path Routing

Create an Ingress that routes /api to api-svc:8080, /admin to admin-svc:3000, and / to frontend-svc:80 under the same host.

Order matters: more specific paths should come first. Use Prefix pathType for all three.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: multi-service-ingress
spec:
  ingressClassName: nginx
  rules:
  - host: myapp.example.com
    http:
      paths:
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: api-svc
            port:
              number: 8080
      - path: /admin
        pathType: Prefix
        backend:
          service:
            name: admin-svc
            port:
              number: 3000
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend-svc
            port:
              number: 80

Medium Set Up a Canary Deployment

Configure NGINX Ingress canary annotations to send 20% of traffic to a new version of your service while keeping 80% on the stable version.

You need two Ingress resources: one for the stable version and one with canary: "true" and canary-weight: "20" annotations.

# Stable Ingress (80% traffic)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: stable-ingress
spec:
  ingressClassName: nginx
  rules:
  - host: myapp.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: app-stable
            port:
              number: 80
---
# Canary Ingress (20% traffic)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: canary-ingress
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "20"
spec:
  ingressClassName: nginx
  rules:
  - host: myapp.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: app-canary
            port:
              number: 80

Hard Design a Production API Gateway

Create a complete Ingress setup for a microservices API gateway with TLS, rate limiting, CORS, external auth, and routing to three services (users, products, orders).

Combine TLS configuration, NGINX annotations for rate limiting, CORS headers, and external auth-url, with multiple path rules.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api-gateway
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/enable-cors: "true"
    nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, PUT, DELETE"
    nginx.ingress.kubernetes.io/limit-rps: "50"
    nginx.ingress.kubernetes.io/auth-url: "http://auth-svc.default.svc.cluster.local/verify"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - api.example.com
    secretName: api-tls
  rules:
  - host: api.example.com
    http:
      paths:
      - path: /api/v1/users
        pathType: Prefix
        backend:
          service:
            name: user-service
            port:
              number: 8080
      - path: /api/v1/products
        pathType: Prefix
        backend:
          service:
            name: product-service
            port:
              number: 8080
      - path: /api/v1/orders
        pathType: Prefix
        backend:
          service:
            name: order-service
            port:
              number: 8080

Pro Tip

Start with a simple single-service Ingress and build up complexity. Test each feature (TLS, path routing, annotations) independently before combining them into a production configuration.

Quick Reference

Common NGINX Annotations

Annotation Purpose Example Value
ssl-redirect Force HTTPS redirect "true"
limit-rps Rate limit per second "100"
enable-cors Enable CORS headers "true"
canary Enable canary routing "true"
canary-weight Traffic percentage to canary "10"
auth-url External auth endpoint "http://auth-svc/verify"

Essential kubectl Commands

ingress-commands.sh
# List all Ingress resources
kubectl get ingress --all-namespaces

# Describe an Ingress for debugging
kubectl describe ingress my-ingress

# Check Ingress Controller logs
kubectl logs -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx

# Verify TLS certificate
kubectl get secret my-tls-secret -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -text

# Test Ingress routing
curl -H "Host: myapp.example.com" http://<ingress-ip>/api

Remember

An Ingress resource by itself does nothing. You need an Ingress Controller (NGINX, Traefik, etc.) running in your cluster to watch for Ingress resources and configure routing accordingly.