Skip to main content

Lesson: Services & Ingress

What you'll learn

  • Why Pod IPs are not enough, and how labels/selectors connect things.
  • The Service types: ClusterIP, NodePort, and LoadBalancer.
  • What an Ingress does and how it routes by hostname/path.
  • How the lab exposes apps through its Kong gateway.

Skill gained: you can give your app a stable address inside the cluster and expose it to the outside.

The lesson

Your Deployment creates Pods, and each Pod gets an IP. But Pod IPs are useless to rely on: Pods die and get replaced with new IPs all the time. You need a stable address that always points at the current, healthy Pods. That is a Service. And to let traffic in from outside the cluster on a real hostname, you use an Ingress.

1. Labels and selectors — the glue

Kubernetes connects objects using labels (key/value tags on objects) and selectors (a filter that matches those labels). A Service does not list Pod IPs; it says "send traffic to any Pod with label app: web," and the set updates automatically as Pods come and go.

Service (selector app=web)
        |
        |  matches by label, not by IP
        v
  Pod(app=web)  Pod(app=web)  Pod(app=web)

So your Pods carry labels: { app: web }, and your Service carries selector: { app: web }. Get those two to match and traffic flows.

2. ClusterIP — the default, internal address

A ClusterIP Service gives your app a single virtual IP and DNS name reachable only inside the cluster. This is how one app calls another (frontend -> backend).

apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  type: ClusterIP        # the default; you can omit it
  selector:
    app: web
  ports:
    - port: 80           # the Service's port
      targetPort: 80     # the container's port

Now any Pod in the cluster can reach it at http://web (same namespace) or http://web.<namespace>.svc.cluster.local. Kubernetes runs an internal DNS that turns the Service name into its ClusterIP. This is the type you will use most.

kubectl apply -f web-svc.yaml
kubectl get svc web                 # see its CLUSTER-IP
kubectl get endpoints web           # the actual Pod IPs it currently targets

3. NodePort and LoadBalancer — reaching from outside

ClusterIP is internal only. Two types open the app up:

  • NodePort opens the same high port (30000–32767) on every node. Traffic to nodeIP:nodePort reaches the Service. So you could hit 10.100.100.8:31000. Simple, but ugly ports and you must know node IPs.

  • LoadBalancer asks the infrastructure for an external load balancer with its own IP. On cloud providers this is automatic; in a bare lab there may be nothing to provide one, so it can stay <pending>.

apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  type: NodePort
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 80
      nodePort: 31000     # optional; otherwise auto-assigned
                ClusterIP        NodePort            LoadBalancer
reach from       inside only     inside + nodeIP:port  external IP
typical use      app-to-app      quick external test   cloud public service

NodePort is fine for a quick test, but for real exposure you do not want callers juggling node IPs and odd ports. That is what Ingress is for.

4. Ingress — HTTP routing by hostname and path

An Ingress is a set of HTTP routing rules: "requests for app.example.com go to Service web; requests for api.example.com go to Service api." It lets many apps share one entry point and one external address, routed by hostname or URL path.

An Ingress object is just rules — it does nothing without an ingress controller, the actual proxy that reads those rules and routes traffic. In the lab, that controller is the Kong gateway, whose proxy listens at 10.100.100.100. So you write Ingress rules and Kong does the routing.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web
spec:
  ingressClassName: kong       # tell the lab's Kong to handle this
  rules:
    - host: web.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web
                port:
                  number: 80
   Internet / lab network
            |
            v
   Kong gateway (10.100.100.100)   <- reads Ingress rules
        | web.example.com -> Service web
        | api.example.com -> Service api
        v
   ClusterIP Services -> Pods
kubectl apply -f web-ingress.yaml
kubectl get ingress
kubectl describe ingress web        # check the rules and that Kong picked it up

In this lab, TLS (HTTPS) is terminated upstream at the edge, so your Ingress and Services serve plain HTTP — you do not configure certificates on the Service or Ingress here.

5. Putting it together

The normal exposure chain for a web app in the lab is:

Deployment (Pods, labels app=web)
      ^ selector app=web
Service (ClusterIP web)         <- stable internal address
      ^ backend
Ingress (host web.example.com)  <- HTTP routing rules
      ^ handled by
Kong gateway 10.100.100.100     <- the actual proxy / entry point

You will build exactly this in Assignment 1.

6. Quick debugging

kubectl get svc,endpoints           # does the Service have endpoints? empty = selector mismatch
kubectl run tmp --image=busybox:1.36 -it --rm -- sh   # a throwaway Pod to test from inside
#   inside it:  wget -qO- http://web    (hits your ClusterIP by DNS name)
kubectl describe ingress web        # are the rules right and the class set to kong?

The most common mistake is a label/selector mismatch: the Service's selector does not match the Pods' labels, so kubectl get endpoints shows none and nothing connects. Always check endpoints first.

Dig deeper

Search terms

  • kubernetes clusterip nodeport loadbalancer difference
  • kubernetes service selector labels
  • kubernetes ingress explained for beginners
  • kubernetes service no endpoints debug
  • kong ingress controller kubernetes

Check yourself

  1. Why can't you just hardcode a Pod's IP in another app?
  2. What connects a Service to the right set of Pods?
  3. Which Service type is internal-only, and what is it used for?
  4. What does an Ingress do that a Service does not, and what actually performs the routing in this lab?
  5. If kubectl get endpoints shows none for your Service, what is the likely cause?