By Fatskills Exam Guides Team — the exam nerds behind 28,500+ quizzes and 2.1M practice questions across 500+ global exams.
You’re running a Kubernetes cluster with multiple microservices (e.g., frontend, backend, auth-service). Each service has its own ClusterIP or NodePort, but you need:
frontend
backend
auth-service
ClusterIP
NodePort
http://
/api
/login
app.example.com
api.example.com
Without Ingress, you’d have to: - Expose each service via NodePort (ugly, high port numbers, no HTTPS).- Use a cloud load balancer per service (expensive, hard to manage).- Manually configure TLS certificates (error-prone, no automation).
With Ingress, you get: ✅ One load balancer for all services (cost-efficient).✅ Automatic TLS (via cert-manager + Let’s Encrypt).✅ Flexible routing rules (path, host, headers).✅ Production-grade traffic management (rate limiting, rewrites, canary deployments).
Real-world scenario:You inherit a Kubernetes cluster where every service has its own LoadBalancer. Your cloud bill is $2,000/month just for load balancers. Fix: Replace them with a single Ingress Controller + Ingress Resources.
LoadBalancer
Deployment
backend-service
kubernetes.io/ingress.class
nginx
traefik
Secret
.crt
.key
kube-secret
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/...
nginx.ingress.kubernetes.io/limit-rpm: "100"
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/permanent-redirect: https://new.example.com
default-backend
gcr.io/google_containers/defaultbackend:1.4
/api → backend-service
example.com/api
example.com/login
api.example.com → backend-service
Production insight:- Host-based routing is cleaner for microservices (each team owns a subdomain).- Path-based routing is simpler for monoliths (one domain, many paths).
✅ A running Kubernetes cluster (Minikube, Kind, EKS, GKE, AKS).✅ kubectl configured to talk to the cluster.✅ Helm (for installing NGINX Ingress Controller).
kubectl
# Add the NGINX Helm repo helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx helm repo update # Install NGINX Ingress Controller (creates a LoadBalancer) helm install ingress-nginx ingress-nginx/ingress-nginx \ --namespace ingress-nginx \ --create-namespace \ --set controller.service.type=LoadBalancer
Verify:
kubectl get svc -n ingress-nginx
Expected output:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) ingress-nginx-controller LoadBalancer 10.96.123.45 123.45.67.89 80:32456/TCP,443:30012/TCP
EXTERNAL-IP
<pending>
# Deploy "hello-world" service (port 8080) kubectl create deployment hello-world --image=gcr.io/google-samples/hello-app:1.0 kubectl expose deployment hello-world --port=8080 --name=hello-world # Deploy "goodbye-world" service (port 8080) kubectl create deployment goodbye-world --image=gcr.io/google-samples/hello-app:2.0 kubectl expose deployment goodbye-world --port=8080 --name=goodbye-world
kubectl get pods,svc
NAME READY STATUS RESTARTS AGE pod/hello-world-xyz123 1/1 Running 0 1m pod/goodbye-world-abc456 1/1 Running 0 1m NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) service/hello-world ClusterIP 10.96.123.100 <none> 8080/TCP service/goodbye-world ClusterIP 10.96.123.101 <none> 8080/TCP
# ingress.yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress annotations: nginx.ingress.kubernetes.io/rewrite-target: /$1 # Removes /hello or /goodbye from the path spec: ingressClassName: nginx # Required for NGINX Ingress Controller rules: - http: paths: - path: /hello(/|$)(.*) # Matches /hello, /hello/, /hello/foo pathType: Prefix backend: service: name: hello-world port: number: 8080 - path: /goodbye(/|$)(.*) # Matches /goodbye, /goodbye/, /goodbye/bar pathType: Prefix backend: service: name: goodbye-world port: number: 8080
Apply:
kubectl apply -f ingress.yaml
kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE example-ingress nginx * 123.45.67.89 80 1m
Test:
# Get the external IP of the Ingress Controller INGRESS_IP=$(kubectl get svc -n ingress-nginx ingress-nginx-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip}') # Test /hello curl http://$INGRESS_IP/hello # Output: Hello, world! Version: 1.0.0 # Test /goodbye curl http://$INGRESS_IP/goodbye # Output: Hello, world! Version: 2.0.0
# Create a self-signed certificate (for testing) openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout tls.key -out tls.crt \ -subj "/CN=example.com/O=example.com" # Create a Kubernetes Secret kubectl create secret tls example-tls --key tls.key --cert tls.crt
Update ingress.yaml to include TLS:
ingress.yaml
spec: tls: - hosts: - example.com secretName: example-tls rules: - host: example.com # Now requires a Host header http: paths: - path: /hello pathType: Prefix backend: service: name: hello-world port: number: 8080 - path: /goodbye pathType: Prefix backend: service: name: goodbye-world port: number: 8080
Test with HTTPS:
# Test with curl (ignore self-signed cert warning) curl -k https://$INGRESS_IP/hello -H "Host: example.com" # Output: Hello, world! Version: 1.0.0
bash helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --set installCRDs=true
ClusterIssuer
yaml controller: service: annotations: service.beta.kubernetes.io/aws-load-balancer-internal: "true" # AWS
yaml annotations: nginx.ingress.kubernetes.io/limit-rpm: "100" nginx.ingress.kubernetes.io/limit-burst-multiplier: "5"
yaml controller: resources: requests: cpu: 100m memory: 256Mi limits: cpu: 1 memory: 1Gi
ingressClassName
yaml metadata: labels: app: my-app env: prod
yaml controller: metrics: enabled: true serviceMonitor: enabled: true
yaml controller: config: access-log-path: "/var/log/nginx/access.log" error-log-path: "/var/log/nginx/error.log"
nginx_ingress_controller_requests{status=~"5.."}
nginx_ingress_controller_request_duration_seconds
spec.ingressClassName: nginx
host
curl -v
✅ "Ingress Controller" (it’s the actual proxy).
"How do you expose multiple Services under a single domain?"
✅ "Use an Ingress Resource with path-based routing."
"What’s the difference between pathType: Prefix and pathType: Exact?"
pathType: Prefix
pathType: Exact
Prefix
/foo
/foo/bar
Exact: Only matches /foo.
Exact
"How do you enable HTTPS for an Ingress?"
spec.tls: true
spec.tls
traefik.ingress.kubernetes.io/...
Challenge:Deploy a Traefik Ingress Controller and route traffic to two services (app1 and app2) using host-based routing (app1.example.com → app1, app2.example.com → app2).
app1
app2
app1.example.com
app2.example.com
Solution:
# Install Traefik via Helm helm repo add traefik https://helm.traefik.io/traefik helm install traefik traefik/traefik -n traefik --create-namespace # Deploy two apps kubectl create deployment app1 --image=gcr.io/google-samples/hello-app:1.0 kubectl expose deployment app1 --port=8080 kubectl create deployment app2 --image=gcr.io/google-samples/hello-app:2.0 kubectl expose deployment app2 --port=8080 # Create Ingress (host-based) cat <<EOF | kubectl apply -f - apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: traefik-ingress annotations: traefik.ingress.kubernetes.io/router.entrypoints: web spec: rules: - host: app1.example.com http: paths: - path: / pathType: Prefix backend: service: name: app1 port: number: 8080 - host: app2.example.com http: paths: - path: / pathType: Prefix backend: service: name: app2 port: number: 8080 EOF
Why it works:- Traefik’s Helm chart automatically creates a LoadBalancer.- Host-based routing ensures app1.example.com and app2.example.com go to different services.
helm install ingress-nginx ingress-nginx/ingress-nginx
--set controller.service.type=LoadBalancer
path: /api
/api/foo
host: api.example.com
kubectl create secret tls my-tls --key tls.key --cert tls.crt
defaultBackend: { service: { name: default-backend, port: 8080 } }
nginx.ingress.kubernetes.io/rewrite-target: /$1
ingressClassName: nginx
traefik.ingress.kubernetes.io/router.entrypoints: web
Join 4M+ learners. Unlock unlimited quizzes, wrong-answer tracking, flashcards + reminders, study guides, and 1-on-1 challenges.