Skip to main content
Kubernetes Essentials·Lesson 3 of 5

Services & Networking

Pods are ephemeral — they can be created, destroyed, and rescheduled at any time. Their IP addresses change constantly. Services provide a stable network endpoint so that clients can always reach your application, regardless of which pods are running behind it.

The Networking Problem

When you scale a deployment to 3 replicas, you get 3 pods with 3 different IP addresses. Which one should clients connect to? What happens when a pod dies and gets replaced with a new IP?

Services solve this by providing a single, stable IP and DNS name that automatically routes traffic to healthy pods.

Service Types

Kubernetes offers four service types:

TypeDescriptionUse Case
ClusterIPInternal cluster IP onlyService-to-service communication
NodePortExposes on each node's IP at a static portDevelopment, testing
LoadBalancerProvisions an external load balancerProduction traffic from the internet
ExternalNameMaps to an external DNS nameConnecting to external services

ClusterIP Service (Default)

A ClusterIP service is only accessible from within the cluster:

apiVersion: v1
kind: Service
metadata:
  name: api-service
spec:
  type: ClusterIP
  selector:
    app: api
  ports:
    - port: 80          # Service port (what clients connect to)
      targetPort: 3000   # Container port (where the app listens)
      protocol: TCP
kubectl apply -f service.yaml

# Other pods can now reach the API at:
# http://api-service (within the same namespace)
# http://api-service.default.svc.cluster.local (fully qualified)

NodePort Service

A NodePort service exposes the application on a port on every node:

apiVersion: v1
kind: Service
metadata:
  name: web-service
spec:
  type: NodePort
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 3000
      nodePort: 30080    # Port on every node (30000-32767)

Access it at http://<any-node-ip>:30080.

LoadBalancer Service

In cloud environments, a LoadBalancer service provisions an external load balancer:

apiVersion: v1
kind: Service
metadata:
  name: web-service
spec:
  type: LoadBalancer
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 3000
kubectl apply -f service.yaml

# Get the external IP
kubectl get service web-service
# NAME          TYPE           EXTERNAL-IP     PORT(S)
# web-service   LoadBalancer   203.0.113.10    80:31234/TCP

The cloud provider (AWS, GCP, Azure) creates and manages the load balancer automatically.

Service Discovery with DNS

Kubernetes has a built-in DNS service. Every Service gets a DNS entry:

<service-name>.<namespace>.svc.cluster.local

Within the same namespace, you can use just the service name:

# From a pod in the default namespace
curl http://api-service          # Same namespace
curl http://api-service.default  # Explicit namespace
curl http://api-service.default.svc.cluster.local  # Fully qualified

This means your application code uses service names as hostnames:

// In your application code
const API_URL = "http://api-service:80";
const DB_URL = "postgres://db-service:5432/mydb";
const CACHE_URL = "redis://cache-service:6379";

Ingress

An Ingress routes external HTTP/HTTPS traffic to services based on hostnames and paths. It is the standard way to expose web applications:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
    - host: myapp.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: frontend-service
                port:
                  number: 80

          - path: /api
            pathType: Prefix
            backend:
              service:
                name: api-service
                port:
                  number: 80

    - host: admin.myapp.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: admin-service
                port:
                  number: 80

Ingress requires an Ingress Controller (like nginx-ingress or traefik) to be installed in the cluster:

# Install nginx ingress controller
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.9.0/deploy/static/provider/cloud/deploy.yaml

TLS with Ingress

Secure your services with HTTPS:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: secure-ingress
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - myapp.com
      secretName: myapp-tls
  rules:
    - host: myapp.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web-service
                port:
                  number: 80

With cert-manager installed, TLS certificates are automatically provisioned and renewed from Let's Encrypt.

Network Policies

Network Policies control which pods can communicate with each other. By default, all pods can talk to all other pods. Network Policies add firewall rules:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-network-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: api
  policyTypes:
    - Ingress
    - Egress
  ingress:
    # Only allow traffic from the frontend pods
    - from:
        - podSelector:
            matchLabels:
              app: frontend
      ports:
        - protocol: TCP
          port: 3000
  egress:
    # Allow traffic to the database
    - to:
        - podSelector:
            matchLabels:
              app: database
      ports:
        - protocol: TCP
          port: 5432
    # Allow DNS
    - to:
        - namespaceSelector: {}
      ports:
        - protocol: UDP
          port: 53

This policy ensures the API can only receive traffic from the frontend and can only connect to the database and DNS.

Debugging Networking

# Check service endpoints
kubectl get endpoints api-service

# Test DNS resolution from inside a pod
kubectl run dns-test --image=busybox --rm -it -- nslookup api-service

# Test connectivity from inside a pod
kubectl run curl-test --image=curlimages/curl --rm -it -- curl http://api-service

# View service details
kubectl describe service api-service

# Check if an Ingress has an IP assigned
kubectl get ingress

# View Ingress controller logs
kubectl logs -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx

Complete Networking Example

Here is a full-stack application with proper networking:

# Frontend Deployment + Service
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 2
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
        - name: frontend
          image: my-frontend:1.0
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: frontend-service
spec:
  selector:
    app: frontend
  ports:
    - port: 80
      targetPort: 80
---
# API Deployment + Service
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api
  template:
    metadata:
      labels:
        app: api
    spec:
      containers:
        - name: api
          image: my-api:1.0
          ports:
            - containerPort: 3000
          env:
            - name: DATABASE_URL
              value: "postgres://db-service:5432/myapp"
---
apiVersion: v1
kind: Service
metadata:
  name: api-service
spec:
  selector:
    app: api
  ports:
    - port: 80
      targetPort: 3000

Summary

Services provide stable networking for ephemeral pods. ClusterIP handles internal communication, LoadBalancer exposes services externally, and Ingress routes HTTP traffic by hostname and path. Network Policies add security by controlling which pods can communicate. In the next lesson, you will learn about ConfigMaps and Secrets for managing application configuration.