> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Demo Traffic Flow with Services and Ingress

> Demonstrates Kubernetes traffic flow using Deployments, Services, NodePort, and NGINX Ingress to provide stable access, load balancing, and path based routing to multiple backends

Here's a common problem every Kubernetes platform engineer encounters: Pods are ephemeral. They are created and destroyed frequently, and each pod gets a new IP address when recreated. If clients try to communicate using pod IPs directly, connectivity breaks when pods restart. Services solve this by providing a stable network endpoint and load-balancing to pods.

![A presentation slide with the word "Demo" on the left and a blue curved panel on the right reading "Traffic Flow with Services and Ingress." The slide also shows a small "© Copyright KodeKloud" at the bottom left.](https://mintcdn.com/kodekloud-c4ac6d9a/jFwNGqGMK4QeJs-g/images/Prep-Course-Certified-Cloud-Native-Platform-Engineering-CNPE/Platform-Architecture-and-Infrastructure/Demo-Traffic-Flow-with-Services-and-Ingress/demo-traffic-flow-services-ingress.jpg?fit=max\&auto=format\&n=jFwNGqGMK4QeJs-g\&q=85\&s=4fd086afc6db8057e81f6c900ddae92d)

In this demo we will:

* Reproduce pod churn and observe changing pod IPs.
* Expose the app inside the cluster using a ClusterIP Service.
* Expose it externally with a NodePort.
* Add an Ingress (with an NGINX ingress controller) to demonstrate path-based routing to multiple backends.

Prerequisites and references:

* Kubernetes cluster with sufficient permissions.
* NGINX Ingress Controller installed (see the NGINX Ingress Controller docs: [https://kubernetes.github.io/ingress-nginx/](https://kubernetes.github.io/ingress-nginx/)).
* Docker images: NGINX ([https://hub.docker.com/\_/nginx](https://hub.docker.com/_/nginx)) and HashiCorp http-echo ([https://hub.docker.com/r/hashicorp/http-echo](https://hub.docker.com/r/hashicorp/http-echo)).

## Create a deployment and observe pod churn

Create a Deployment named `web` using the NGINX image with three replicas, then inspect the pods:

```bash theme={null}
controlplane ~ ➜ kubectl create deployment web --image=nginx --replicas=3
deployment.apps/web created

controlplane ~ ➜ kubectl get pods -o wide
NAME                        READY   STATUS    RESTARTS   AGE     IP          NODE         NOMINATED NODE   READINESS GATES
web-68d995574f-j4hpv        1/1     Running   0          15s     172.17.0.8  controlplane  <none>            <none>
web-68d995574f-njhqz        1/1     Running   0          15s     172.17.0.7  controlplane  <none>            <none>
web-68d995574f-q6hjj        1/1     Running   0          15s     172.17.0.6  controlplane  <none>            <none>
```

Now delete one pod and watch a new pod get created with a different IP:

```bash theme={null}
controlplane ~ ➜ kubectl delete pod web-68d995574f-j4hpv
pod "web-68d995574f-j4hpv" deleted

controlplane ~ ➜ kubectl get pods -o wide
NAME                        READY   STATUS    RESTARTS   AGE     IP          NODE         NOMINATED NODE   READINESS GATES
web-68d995574f-4vgnb        1/1     Running   0          3s      172.17.0.9  controlplane  <none>            <none>
web-68d995574f-njhqz        1/1     Running   0          2m39s   172.17.0.7  controlplane  <none>            <none>
web-68d995574f-q6hjj        1/1     Running   0          2m39s   172.17.0.6  controlplane  <none>            <none>
```

Because the Deployment controller replaces deleted pods, the new pod has a different IP (172.17.0.9). Any client addressing the old pod IP would lose connectivity — which is why we use Services as stable access points to pods.

## Expose the Deployment as a ClusterIP Service

Expose the `web` deployment as a ClusterIP Service (the default type) named `web-svc`:

```bash theme={null}
controlplane ~ ➜ kubectl expose deploy web --port=80 --target-port=80 --name=web-svc
service/web-svc exposed
```

Describe the service to see how it maps to pods:

```bash theme={null}
controlplane ~ ➜ kubectl describe service web-svc
Name:                     web-svc
Namespace:                default
Labels:                   <none>
Annotations:              <none>
Selector:                 app=web
Type:                     ClusterIP
IP:                       172.20.212.114
Port:                     <unset>  80/TCP
TargetPort:               80/TCP
Endpoints:                172.17.0.7:80,172.17.0.6:80,172.17.0.9:80
Session Affinity:         None
Events:                   <none>
```

Verify the pods have the label used by the Service selector:

```bash theme={null}
controlplane ~ ➜ kubectl get pods --show-labels
NAME                      READY   STATUS    RESTARTS   AGE     LABELS
web-68d995574f-4vqnb      1/1     Running   0          117s    app=web,pod-template-hash=68d995574f
web-68d995574f-njhqz      1/1     Running   0          2m39s   app=web,pod-template-hash=68d995574f
web-68d995574f-q6hjj      1/1     Running   0          2m39s   app=web,pod-template-hash=68d995574f
```

ClusterIP services are reachable only from inside the cluster. To access the app externally, use NodePort or an Ingress.

## Expose the app externally with NodePort

Create a NodePort Service named `web-nodeport`:

```bash theme={null}
controlplane ~ ➜ kubectl expose deploy web --port=80 --target-port=80 --type=NodePort --name=web-nodeport
service/web-nodeport exposed

controlplane ~ ➜ kubectl get svc
NAME           TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)           AGE
kubernetes     ClusterIP   172.20.0.1       <none>        443/TCP           150m
web-nodeport   NodePort    172.20.11.172    <none>        80:30309/TCP      21s
web-svc        ClusterIP   172.20.212.114   <none>        80/TCP            2m1s
```

On a single-node cluster you can curl `localhost` with the assigned NodePort:

```bash theme={null}
controlplane ~ ➜ curl localhost:30309
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
<h1>Welcome to nginx!</h1>
...
</html>
```

\<NodePort has limitations — use Ingress for richer routing and TLS.>

NodePort opens high-numbered ports (typically in the 30000–32767 range) on every node. It's simple, but not user-friendly for end users, and it doesn't provide host- or path-based routing or TLS termination. For host/path routing and easier external access, use an Ingress with an ingress controller.

## Use Ingress for host/path-based routing (NGINX ingress controller)

Ingress is a cluster resource that defines routing rules (host/path) and optionally TLS termination. An ingress controller (e.g., NGINX) watches Ingress objects and programs a reverse proxy accordingly.

Check that an NGINX ingress controller is running in the `ingress-nginx` namespace and that its Service is present:

```bash theme={null}
controlplane ~ ➜ kubectl get pods -n ingress-nginx
NAME                                     READY   STATUS    RESTARTS   AGE
ingress-nginx-controller-c4d7b845b-bpzvx 1/1     Running   0          10m

controlplane ~ ➜ kubectl get svc -n ingress-nginx
NAME                        TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)                         AGE
ingress-nginx-controller    NodePort   172.20.253.85   <none>        80:30080/TCP,443:30443/TCP      10m
```

Because the ingress controller Service maps node port 30080 → 80, hitting node:30080 reaches the ingress controller.

## Add a second backend (api) for path-based routing

Create a second backend deployment `api` using HashiCorp's `http-echo` image so it returns a simple response, then expose it as `api-svc`:

```bash theme={null}
controlplane ~ ➜ kubectl create deploy api --image=hashicorp/http-echo --replicas=2 -- /http-echo --text="hello from kodekloud"
deployment.apps/api created

controlplane ~ ➜ kubectl expose deployment api --port=5678 --target-port=5678 --name=api-svc
service/api-svc exposed
```

## Create an Ingress resource with path-based rules

Save the following as `ingress.yaml` (it rewrites paths to `/` and routes `/api` to `api-svc:5678` and `/` to `web-svc:80`):

```yaml theme={null}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: platform-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
  - http:
      paths:
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: api-svc
            port:
              number: 5678
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-svc
            port:
              number: 80
```

Apply the Ingress and inspect it:

```bash theme={null}
controlplane ~ ➜ kubectl apply -f ingress.yaml
ingress.networking.k8s.io/platform-ingress created

controlplane ~ ➜ kubectl describe ingress platform-ingress
Name:             platform-ingress
Namespace:        default
Address:
Ingress Class:    nginx
Default backend:  <default>
Rules:
  Host  Path   Backends
  ----  ----   --------
  *     /api   api-svc:5678 (172.17.0.11:5678,172.17.0.10:5678)
        /      web-svc:80 (172.17.0.7:80,172.17.0.6:80,172.17.0.9:80)
Annotations:     nginx.ingress.kubernetes.io/rewrite-target: /
Events:
  Type    Reason    Age   From                       Message
  ----    ------    ----  ----                       -------
  Normal  Sync      14s   nginx-ingress-controller  Scheduled for sync
```

Test the routing via the ingress controller NodePort (30080):

```bash theme={null}
controlplane ~ ➜ curl localhost:30080/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
<h1>Welcome to nginx!</h1>
...
</html>

controlplane ~ ➜ curl localhost:30080/api
hello from kodekloud
```

The same NodePort (30080) and cluster IP are now used by the ingress controller to route requests to different backends based on the paths defined in the Ingress resource.

## Summary: the three routing layers

Ingress routes to Services, and Services route to Pods. Remember these three layers:

| Layer | Purpose | Example / Notes |
| - | - | - |
| Ingress | External routing (host/path), TLS termination | `Ingress` resource handled by an ingress controller (e.g., NGINX). |
| Service | Stable internal discovery and load balancing to pods | `ClusterIP`, `NodePort`, `LoadBalancer`. `web-svc`, `api-svc`. |
| Pod | Actual application workload | Containers running NGINX or http-echo; pod IPs are ephemeral. |

## Debugging checklist (outside → inside)

1. Can't reach the app externally?
   * Verify the ingress controller pod(s) and Service are running.
   * Confirm the ingress controller Service exposes ports (NodePort or LoadBalancer).
2. Ingress reachable but wrong backend?
   * Inspect the Ingress resource: `kubectl describe ingress <name>`.
   * Check annotations (e.g., rewrite rules) and ingressClassName.
3. Service has no endpoints?
   * Check Service selectors and Pod labels: `kubectl describe svc <svc>` and `kubectl get pods --show-labels`.
   * Confirm pods are listening on the expected `targetPort`.

When troubleshooting, always follow the chain: Ingress → Service → Pod. Verify the ingress-controller is running, the Ingress rules point to the correct services, and the Services select the appropriate Pods (labels and ports).

## Further reading and references

* Kubernetes Services: [https://kubernetes.io/docs/concepts/services-networking/service/](https://kubernetes.io/docs/concepts/services-networking/service/)
* Kubernetes Ingress: [https://kubernetes.io/docs/concepts/services-networking/ingress/](https://kubernetes.io/docs/concepts/services-networking/ingress/)
* NGINX Ingress Controller: [https://kubernetes.github.io/ingress-nginx/](https://kubernetes.github.io/ingress-nginx/)
* NGINX image on Docker Hub: [https://hub.docker.com/\_/nginx](https://hub.docker.com/_/nginx)
* HashiCorp http-echo image: [https://hub.docker.com/r/hashicorp/http-echo](https://hub.docker.com/r/hashicorp/http-echo)

> **[Watch Video](https://learn.kodekloud.com/user/courses/prep-course-certified-cloud-native-platform-engineer-cnpe/module/989346de-0207-4837-af11-bf456d188972/lesson/1ed2dc1a-4ce9-4793-b1f3-a2c15035cfd8)**


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.