Ravindra BagaleCourses & study guides मराठी

Chapter 65: Kubernetes Domain Names and HTTPS: Ingress, DNS, ExternalDNS, cert-manager and Gateway API

65.1 Ingress and Ingress controllers: one entry point for many Services

Why: the problem

In Chapter 62 every app got its own Service. When Swiggy runs a menu service, a cart service, an orders service and a partner portal, giving each one a LoadBalancer Service means four cloud load balancers, four public IP addresses, four bills and four places to install a certificate. Users do not want four addresses either. They type one name, such as swiggy.com, and expect the right page to come back whether the URL ends in /menu or /cart. Ingress solves this. An Ingress object is a list of HTTP rules: when the host is shop.example.com and the path starts with /api, send the request to the api Service on port 80; everything else goes to the web Service. The Ingress object itself does nothing. A separate program called an Ingress controller watches all Ingress objects in the cluster and configures a real reverse proxy, such as NGINX, Traefik, HAProxy, Envoy or a cloud load balancer, to follow those rules. Only the controller needs a public address; it then fans traffic out to many ClusterIP Services inside the cluster. Which controller you install matters. The community ingress-nginx project was the most common choice for years, but Kubernetes SIG Network and the Security Response Committee announced its retirement in November 2025 and retired it on 24 March 2026. Existing installs still work, and its manifests stay available, so it is fine for learning on kind, but it no longer receives security fixes. New production clusters choose a maintained controller (Traefik, HAProxy Ingress, the F5 NGINX Ingress Controller, the AWS Load Balancer Controller, Contour) or move to Gateway API, which we cover in 65.5. The good news: the Ingress object you write is the same for all of them; only ingressClassName changes. In this lesson we create a kind cluster that maps ports 80 and 443 to your laptop, install a controller, expose two apps behind one Ingress, read how the controller turned rules into proxy config, and see the default backend and IngressClass in action.

What: every term in plain English

  • Ingress — A Kubernetes object holding HTTP and HTTPS routing rules: host, path and the backend Service.
  • Ingress controller — The program that reads Ingress objects and configures a real proxy to follow them.
  • IngressClass — A cluster object naming a controller; ingressClassName in an Ingress picks which controller handles it.
  • Backend — The Service name and port that receives matching requests.
  • Default backend — Where requests go when no rule matches; usually a 404 page.
  • pathType — Prefix matches by path segment, Exact matches the whole path, ImplementationSpecific is controller-specific.
  • Reverse proxy — A server that receives client requests and forwards them to backend servers.
  • extraPortMappings — kind setting that forwards ports on your laptop to a node container.
  • Annotation — Controller-specific key-value setting on an Ingress, such as a rewrite or timeout.
  • Layer 7 — HTTP level routing using host, path and headers, unlike layer 4 which sees only IP and port.
Create a kind cluster with ports 80 and 443 mapped. Install an Ingress controller from a pinned manifest. Check the IngressClass. Deploy two small apps with ClusterIP Services. Write one Ingress with two paths. Test with curl and a Host header. Read the generated proxy config.
Ingress: one entry point, many Services

How: step by step

  1. Create a kind cluster with ports 80 and 443 mapped. curl localhost reaches the node.
  2. Install an Ingress controller from a pinned manifest. Controller pod Running.
  3. Check the IngressClass. nginx class exists.
  4. Deploy two small apps with ClusterIP Services. web and api ready.
  5. Write one Ingress with two paths. Rules accepted.
  6. Test with curl and a Host header. Each path reaches its app.
  7. Read the generated proxy config. Rules became NGINX locations.
  8. Clean up or keep for 65.2. Cluster stays.

Example 1: Create a kind cluster ready for Ingress and install a controller

Goal. Create a kind cluster ready for Ingress and install a controller.

kind-ch65.yaml

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: ch65
nodes:
  - role: control-plane
    kubeadmConfigPatches:
      - |
        kind: InitConfiguration
        nodeRegistration:
          kubeletExtraArgs:
            node-labels: "ingress-ready=true"
    extraPortMappings:
      - containerPort: 80
        hostPort: 80
        protocol: TCP
      - containerPort: 443
        hostPort: 443
        protocol: TCP
  - role: worker
  - role: worker

install-controller.sh

kind create cluster --config kind-ch65.yaml
# ingress-nginx-kind-v1.11.3.yaml: the kind provider manifest saved from the official docs, pinned and reviewed.
# The project is retired (24 March 2026); we use it only as a learning lab on kind.
kubectl apply -f ingress-nginx-kind-v1.11.3.yaml
kubectl wait -n ingress-nginx --for=condition=ready pod -l app.kubernetes.io/component=controller --timeout=180s
kubectl get ingressclass

What each line does

  • node-labels ingress-ready=true — The kind manifest uses a nodeSelector on this label, so the controller lands on the node with port mappings.
  • extraPortMappings — Port 80 and 443 on your laptop go into the control plane container, where the controller listens with hostPort.
  • Pinned manifest — A saved, reviewed file with a fixed version, so the lab gives the same result next month.
  • kubectl wait — Blocks until the controller pod is Ready instead of guessing with sleep.
  • get ingressclass — Shows the class name you will put in ingressClassName.

Green output

$ bash install-controller.sh
Creating cluster "ch65" ...
 ✓ Ensuring node image (kindest/node:v1.34.0)
 ✓ Preparing nodes
Set kubectl context to "kind-ch65"
namespace/ingress-nginx created
deployment.apps/ingress-nginx-controller created
ingressclass.networking.k8s.io/nginx created
pod/ingress-nginx-controller-7c9d8b6f5-x4k2p condition met
NAME    CONTROLLER                     PARAMETERS   AGE
nginx   k8s.io/ingress-nginx           none         40s
$ curl -s -o /dev/null -w "%{http_code}\n" http://localhost/
404

Expected result. A 404 from localhost is good news: the request reached the controller, and its default backend answered because no Ingress exists yet.

Example 2: Expose two apps behind one Ingress by path

Goal. Expose two apps behind one Ingress by path.

apps.sh

kubectl create ns shop
kubectl create deployment web -n shop --image=hashicorp/http-echo:1.0 -- /http-echo -listen=:8080 -text="web home"
kubectl create deployment api -n shop --image=hashicorp/http-echo:1.0 -- /http-echo -listen=:8080 -text="api v1"
kubectl expose deployment web -n shop --port=80 --target-port=8080
kubectl expose deployment api -n shop --port=80 --target-port=8080
kubectl rollout status deploy/web -n shop && kubectl rollout status deploy/api -n shop

shop-ingress.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: shop
  namespace: shop
spec:
  ingressClassName: nginx
  rules:
    - host: shop.local
      http:
        paths:
          - path: /api
            pathType: Prefix
            backend:
              service:
                name: api
                port:
                  number: 80
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web
                port:
                  number: 80

What each line does

  • ClusterIP Services — web and api are not public; only the controller is.
  • ingressClassName: nginx — Tells which controller owns this Ingress; without it a default class may or may not pick it up.
  • host: shop.local — Rules apply only when the HTTP Host header is shop.local.
  • /api before / — The controller sorts by the longest match, so /api/orders goes to api and everything else to web.
  • pathType Prefix — /api matches /api and /api/orders but not /apix, because Prefix matches whole segments.

Green output

$ bash apps.sh
deployment "web" successfully rolled out
deployment "api" successfully rolled out
$ kubectl apply -f shop-ingress.yaml
ingress.networking.k8s.io/shop created
$ kubectl get ingress -n shop
NAME   CLASS   HOSTS        ADDRESS     PORTS   AGE
shop   nginx   shop.local   localhost   80      20s
$ curl -s -H "Host: shop.local" http://localhost/
web home
$ curl -s -H "Host: shop.local" http://localhost/api/orders
api v1
$ curl -s -H "Host: shop.local" http://localhost/apix
web home
$ curl -s -o /dev/null -w "%{http_code}\n" -H "Host: other.local" http://localhost/
404

Expected result. One entry point on port 80 now serves two Services. The Host header picks the rule set, the path picks the Service, and a host with no rule gets the default 404.

Example 3: See what the controller built and use IngressClass well

Goal. See what the controller built and use IngressClass well.

inspect.sh

POD=$(kubectl get pod -n ingress-nginx -l app.kubernetes.io/component=controller -o name)
kubectl exec -n ingress-nginx $POD -- cat /etc/nginx/nginx.conf | grep -n "server_name shop.local" -A3
kubectl describe ingress shop -n shop
kubectl get endpointslices -n shop
kubectl annotate ingressclass nginx ingressclass.kubernetes.io/is-default-class=true

no-class.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: docs
  namespace: shop
spec:
  rules:
    - host: docs.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web
                port:
                  number: 80

What each line does

  • nginx.conf — The controller wrote a server block per host and a location per path.
  • describe ingress — Shows each rule and the pod IPs behind each backend.
  • EndpointSlices — The controller sends traffic straight to pod IPs from these, skipping kube-proxy.
  • is-default-class — Ingresses without ingressClassName now go to this class.
  • Only one default — Two default classes make new Ingresses ambiguous; keep exactly one.

Green output

$ bash inspect.sh
212:    server {
213:        server_name shop.local ;
214:        listen 80  ;
Rules:
  Host        Path  Backends
  ----        ----  --------
  shop.local
              /api   api:80 (10.244.1.5:8080)
              /      web:80 (10.244.2.4:8080)
NAME        ADDRESSTYPE   PORTS   ENDPOINTS
api-8xk2q   IPv4          8080    10.244.1.5
web-p9v7d   IPv4          8080    10.244.2.4
ingressclass.networking.k8s.io/nginx annotated
$ kubectl apply -f no-class.yaml
$ curl -s -H "Host: docs.local" http://localhost/
web home

Expected result. The rules became plain NGINX config, traffic goes directly to pod IPs, and marking one IngressClass as default lets teams skip ingressClassName safely.

Every command, step by step (kind lab with kubectl)

  1. Cluster

    kind create cluster --config kind-ch65.yaml
    

    What it does: Ports 80 and 443 mapped.

    Set kubectl context to "kind-ch65"
    
  2. Controller

    kubectl apply -f ingress-nginx-kind-v1.11.3.yaml
    

    What it does: Installed.

    deployment.apps/ingress-nginx-controller created
    
  3. Wait

    kubectl wait -n ingress-nginx --for=condition=ready pod -l app.kubernetes.io/component=controller --timeout=180s
    

    What it does: Ready.

    condition met
    
  4. Class

    kubectl get ingressclass
    

    What it does: nginx.

    nginx   k8s.io/ingress-nginx
    
  5. Default 404

    curl -s -o /dev/null -w "%{http_code}" http://localhost/
    

    What it does: No rules yet.

    404
    
  6. Apps

    bash apps.sh
    

    What it does: Two Services.

    deployment "api" successfully rolled out
    
  7. Ingress

    kubectl apply -f shop-ingress.yaml
    

    What it does: Rules.

    ingress.networking.k8s.io/shop created
    
  8. Web

    curl -s -H "Host: shop.local" http://localhost/
    

    What it does: Root path.

    web home
    
  9. API

    curl -s -H "Host: shop.local" http://localhost/api/orders
    

    What it does: Prefix match.

    api v1
    
  10. Describe

    kubectl describe ingress shop -n shop
    

    What it does: Backends and pod IPs.

    /api   api:80 (10.244.1.5:8080)
    
  11. Default class

    kubectl annotate ingressclass nginx ingressclass.kubernetes.io/is-default-class=true
    

    What it does: One default.

    ingressclass.networking.k8s.io/nginx annotated
    

When it is red 1

curl: (7) Failed to connect to localhost port 80

Cause. The cluster was created without extraPortMappings, or another program uses port 80.

Fix, step by step

  1. Recreate with kind-ch65.yaml.
  2. Stop the local web server using port 80.

When it is red 2

404 Not Found from nginx for a host you defined

Cause. Host header does not match, or ingressClassName points to a class no controller owns.

Fix, step by step

  1. curl -H "Host: shop.local".
  2. kubectl get ingressclass and compare.

When it is red 3

503 Service Temporarily Unavailable

Cause. Backend Service has no ready endpoints.

Fix, step by step

  1. kubectl get endpointslices -n shop.
  2. Fix the selector or readiness probe.

When it is red 4

ADDRESS column stays empty

Cause. Controller has not processed the Ingress, often a wrong class.

Fix, step by step

  1. kubectl logs of the controller pod.
  2. Set ingressClassName.

When it is red 5

Controller pod Pending

Cause. No node has ingress-ready=true.

Fix, step by step

  1. Use the kind config with node-labels.
  2. kubectl get nodes --show-labels.

Practice tasks

Easy Task 1: Cluster

Why: Create.

What to do: Create kind with ports 80 and 443 mapped.

How:
  1. kind-ch65.yaml.
Solution
docker ps --filter name=ch65

Easy Task 2: Controller

Why: Install.

What to do: Apply the pinned controller manifest.

How:
  1. Wait for Ready.
Solution
kubectl get pods -n ingress-nginx

Easy Task 3: Apps

Why: Deploy.

What to do: Create web and api with ClusterIP Services.

How:
  1. http-echo.
Solution
kubectl get svc -n shop

Medium Task 4: Paths

Why: Route.

What to do: Apply shop-ingress.yaml.

How:
  1. /api and /.
Solution
curl -s -H "Host: shop.local" http://localhost/api

Medium Task 5: Host

Why: Test.

What to do: Send a wrong Host header.

How:
  1. Expect 404.
Solution
curl -s -o /dev/null -w "%{http_code}" -H "Host: x.local" http://localhost/

Medium Task 6: Config

Why: Inspect.

What to do: Find the server block in nginx.conf.

How:
  1. kubectl exec.
Solution
kubectl exec -n ingress-nginx deploy/ingress-nginx-controller -- nginx -T

Hard Task 7: Default

Why: Class.

What to do: Make nginx the default class and apply no-class.yaml.

How:
  1. One default only.
Solution
kubectl get ingressclass -o yaml

Hard Task 8: Swiggy

Why: Main task.

What to do: Three apps behind partner.local.

How:
  1. Three paths.
Solution
kubectl get ingress -A

Homework: try at home

For Swiggy's partner portal, run three apps (portal, menu, payouts) behind one Ingress on partner.local with paths /, /menu and /payouts, prove each path with curl and a Host header, show the 404 for an unknown host, and mark one IngressClass as default.

Recap

  • Ingress holds HTTP rules; a controller makes them real.
  • One controller address can serve many hosts and paths.
  • ingressClassName picks the controller; mark exactly one class as default.
  • pathType Prefix matches whole segments and longest match wins.
  • ingress-nginx was retired on 24 March 2026; use it only for learning and pick a maintained controller or Gateway API for production.

FAQ

Q. Ingress or LoadBalancer Service?

Use a LoadBalancer Service for non-HTTP traffic such as databases or MQTT. Use Ingress or Gateway API for HTTP and HTTPS so many apps share one load balancer and one certificate setup.

Q. Is NGINX Ingress from F5 the same as ingress-nginx?

No. They are two different projects that both use NGINX. The F5 controller is still maintained; the community ingress-nginx is retired.

Q. Can two controllers run in one cluster?

Yes. Give each its own IngressClass, for example internal and public, and set ingressClassName per Ingress.

Q. Does Ingress handle TCP or UDP?

Not in the standard API. Some controllers have their own extras, but Gateway API has TCPRoute and UDPRoute for that.

Interview questions

  1. What is the difference between Ingress and an Ingress controller?

    Model answer: Ingress is a rules object; the controller is the running proxy that reads those rules and serves traffic.

  2. Why not one LoadBalancer per Service?

    Model answer: Cost, more public IPs, more certificates and more places to secure. Ingress shares one entry point.

  3. How does the controller reach pods?

    Model answer: It reads EndpointSlices and proxies straight to pod IPs.

  4. What changed for ingress-nginx in 2026?

    Model answer: It was retired on 24 March 2026; no new releases or security patches, so plan migration.

Quick quiz

Q1. What does an Ingress object do on its own?

Nothing; a controller must act on it.

Q2. Which field picks the controller?

ingressClassName.

Q3. Which match type respects path segments?

Prefix.

Q4. What answers when no rule matches?

The default backend, usually 404.

Q5. Why was ingress-nginx retired?

Not enough maintainers; no more security fixes after 24 March 2026.

Samajla ka?