Services and Networking

intermediate kubernetes services ingress networking

Pods are ephemeral. They get created, destroyed, and rescheduled all the time. Every time a Pod restarts, it gets a new IP address. So how do other Pods find and talk to our app? That’s what Services solve — they give us a stable endpoint that doesn’t change even when the Pods behind it do.

Service Types

Service Types — From Internal to External
ClusterIP
Internal only. Other Pods can reach it, but nothing outside the cluster can. Default type.
NodePort
Opens a port (30000–32767) on every node. External traffic hits <NodeIP>:<Port>. Good for dev/testing.
LoadBalancer
Provisions a cloud load balancer (AWS ELB, GCP LB). The real way to expose services in production on cloud.
ExternalName
Maps a Service to an external DNS name (like an RDS endpoint). No proxying, just a CNAME alias.

ClusterIP — The Default

Most Services are ClusterIP. They give us a virtual IP inside the cluster that load-balances traffic across matching Pods.

apiVersion: v1
kind: Service
metadata:
  name: my-app-service
spec:
  type: ClusterIP              # default, can omit this line
  selector:
    app: my-app                # routes to Pods with this label
  ports:
    - port: 80                 # port the Service listens on
      targetPort: 8080         # port on the Pod

The selector is the glue. The Service watches for all Pods with app: my-app label and routes traffic to them.

NodePort

Builds on ClusterIP but also opens a static port on every node in the cluster.

spec:
  type: NodePort
  selector:
    app: my-app
  ports:
    - port: 80
      targetPort: 8080
      nodePort: 30080          # optional, K8s picks one if omitted

Now we can hit http://<any-node-ip>:30080 from outside. Not great for production (ugly ports, no SSL), but handy for quick access.

LoadBalancer

The production choice on cloud providers. It creates a ClusterIP + NodePort + a cloud load balancer that routes external traffic in.

spec:
  type: LoadBalancer
  selector:
    app: my-app
  ports:
    - port: 80
      targetPort: 8080

The cloud provider assigns an external IP. One downside: each LoadBalancer Service gets its own cloud LB, which can get expensive. That’s where Ingress comes in.

Service Discovery via DNS

Kubernetes runs a DNS server (CoreDNS) inside the cluster. Every Service gets a DNS entry automatically:

  • my-app-service — works within the same namespace
  • my-app-service.default.svc.cluster.local — fully qualified (namespace = default)

So our app code just uses the Service name as the hostname. No hardcoded IPs.

# From inside any Pod in the same namespace
curl http://my-app-service:80

Ingress — Smart HTTP Routing

Instead of giving every Service its own LoadBalancer, we can use a single Ingress controller to route traffic based on hostnames or URL paths.

An Ingress Controller (like nginx-ingress or Traefik) is the actual reverse proxy. An Ingress resource is the routing config.

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

This routes api.example.com to one Service and app.example.com to another — all through a single load balancer. Way cheaper and cleaner.

In simple language, Services give Pods a stable address, and Ingress lets us route external HTTP traffic to the right Service based on domain names or paths. For internal communication we use ClusterIP, for external access we use Ingress or LoadBalancer.