Pods are temporary by design, but the rest of your system still needs a stable way to reach them. A Kubernetes Service gives that stable network identity, handles basic load balancing, and lets applications talk to each other without tracking individual pod IPs.

What is a Kubernetes Service?

A Service is an abstraction that defines a logical set of Pods and a policy by which to access them. Services enable loose coupling between microservices and provide stable network endpoints regardless of pod lifecycle changes.

Service vs Deployment

Deployment (Application Management):
┌─────────────────────────────────────┐
│  Deployment Controller              │
│  ┌─────────────────────────────┐    │
│  │  ReplicaSet                 │    │
│  │  ┌─────┐ ┌─────┐ ┌─────┐   │    │
│  │  │Pod1 │ │Pod2 │ │Pod3 │   │    │
│  │  └─────┘ └─────┘ └─────┘   │    │
│  └─────────────────────────────┘    │
└─────────────────────────────────────┘
Manages application lifecycle

Service (Network Abstraction):
┌─────────────────────────────────────┐
│  Service: webapp-service            │
│  ┌─────────────────────────────┐    │
│  │  Stable IP: 10.96.0.1       │    │
│  │  Port: 80                   │    │
│  │  Load Balancer              │    │
│  └─────────────────────────────┘    │
│           │                         │
│  ┌─────┐ ┌─────┐ ┌─────┐            │
│  │Pod1 │ │Pod2 │ │Pod3 │            │
│  └─────┘ └─────┘ └─────┘            │
└─────────────────────────────────────┘
Provides stable networking

What Services Actually Provide

  • Stable network identity: consistent IP address and DNS name
  • Load balancing: traffic distribution across matching, ready pods
  • Service discovery: DNS records and environment variables inside the cluster
  • Loose coupling: clients depend on a Service name, not pod internals
  • Multiple access patterns: ClusterIP, NodePort, LoadBalancer, and ExternalName

Service Types Explained

ClusterIP (Default)

Exposes service on an internal IP address accessible only within the cluster:

# clusterip-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: webapp-clusterip
spec:
  type: ClusterIP
  selector:
    app: webapp
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP

NodePort

Exposes service on each node’s IP at a static port:

# nodeport-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: webapp-nodeport
spec:
  type: NodePort
  selector:
    app: webapp
  ports:
  - port: 80
    targetPort: 8080
    nodePort: 30080  # Optional: specify node port (30000-32767)
    protocol: TCP

LoadBalancer

Exposes service externally using a cloud provider’s load balancer:

# loadbalancer-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: webapp-loadbalancer
spec:
  type: LoadBalancer
  selector:
    app: webapp
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP

Creating and Managing Services

Basic Service Creation

# Create service imperatively
kubectl expose deployment webapp-deployment --port=80 --target-port=8080

# Create specific service types
kubectl expose deployment webapp-deployment --port=80 --target-port=8080 --type=NodePort
kubectl expose deployment webapp-deployment --port=80 --target-port=8080 --type=LoadBalancer

# Get services
kubectl get services
kubectl get service webapp-clusterip

# Describe service
kubectl describe service webapp-clusterip

Service with Deployment Example

# complete-example.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: webapp
  template:
    metadata:
      labels:
        app: webapp
    spec:
      containers:
      - name: webapp
        image: myapp:v1.0
        ports:
        - containerPort: 8080
        env:
        - name: SERVICE_NAME
          value: "webapp"
---
apiVersion: v1
kind: Service
metadata:
  name: webapp-service
spec:
  type: ClusterIP
  selector:
    app: webapp
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP
# Apply both resources
kubectl apply -f complete-example.yaml

# Test service connectivity
kubectl run test-pod --image=busybox --rm -it -- /bin/sh
# Inside the pod:
wget -qO- http://webapp-service

Service Discovery Mechanisms

Environment Variables

Kubernetes automatically injects service information as environment variables:

# Check environment variables in a pod
kubectl exec -it webapp-deployment-xxxxx -- env | grep WEBAPP

# Expected output:
WEBAPP_SERVICE_HOST=10.96.0.1
WEBAPP_SERVICE_PORT=80

DNS Discovery

Services are automatically registered in cluster DNS:

# Test DNS resolution
kubectl run dns-test --image=busybox --rm -it -- /bin/sh

# Inside the pod:
nslookup webapp-service
nslookup webapp-service.default.svc.cluster.local

Service Discovery in Action

# service-discovery-demo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
      - name: frontend
        image: frontend:v1.0
        ports:
        - containerPort: 3000
        env:
        - name: BACKEND_URL
          value: "http://backend-service:8080"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
      - name: backend
        image: backend:v1.0
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: backend-service
spec:
  type: ClusterIP
  selector:
    app: backend
  ports:
  - port: 8080
    targetPort: 8080
    protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
  name: frontend-service
spec:
  type: NodePort
  selector:
    app: frontend
  ports:
  - port: 80
    targetPort: 3000
    nodePort: 30080
    protocol: TCP

Advanced Service Features

Headless Services

For direct pod access without load balancing:

# headless-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: webapp-headless
spec:
  clusterIP: None  # This makes it headless
  selector:
    app: webapp
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP

Services with Session Affinity

For sticky sessions:

# session-affinity-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: webapp-sticky
spec:
  type: ClusterIP
  sessionAffinity: ClientIP
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 10800  # 3 hours
  selector:
    app: webapp
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP

ExternalName Services

For external service references:

# externalname-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: external-database
spec:
  type: ExternalName
  externalName: database.external-service.com

Load Balancing Algorithms

Round Robin (Default)

Distributes requests evenly across pods:

# round-robin-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: webapp-roundrobin
spec:
  type: ClusterIP
  selector:
    app: webapp
  ports:
  - port: 80
    targetPort: 8080

Session Affinity

Routes requests from same client to same pod:

# session-affinity-lb.yaml
apiVersion: v1
kind: Service
metadata:
  name: webapp-affinity
spec:
  type: ClusterIP
  sessionAffinity: ClientIP
  selector:
    app: webapp
  ports:
  - port: 80
    targetPort: 8080

Service Troubleshooting

First Checks

# Check service status
kubectl get services
kubectl get endpoints

# Describe service for details
kubectl describe service webapp-service

# Check if service has endpoints
kubectl get endpoints webapp-service

# Test service connectivity
kubectl run test-pod --image=busybox --rm -it -- /bin/sh
# Inside pod:
wget -qO- http://webapp-service

# Check DNS resolution
kubectl run dns-test --image=busybox --rm -it -- /bin/sh
# Inside pod:
nslookup webapp-service

# Check service events
kubectl get events --field-selector involvedObject.name=webapp-service

Debugging Service Issues

# Check if pods have correct labels
kubectl get pods --show-labels | grep webapp

# Verify service selector matches pod labels
kubectl describe service webapp-service | grep Selector

# Check if pods are ready
kubectl get pods -l app=webapp

# Check pod logs
kubectl logs -l app=webapp --tail=50

# Test direct pod connectivity
kubectl exec -it webapp-pod-xxxx -- wget -qO- localhost:8080

Service Best Practices

Naming Conventions

# good-naming-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: webapp-backend-service  # Descriptive name
  labels:
    app: webapp
    tier: backend
    environment: production
spec:
  type: ClusterIP
  selector:
    app: webapp
    tier: backend
  ports:
  - name: http
    port: 80
    targetPort: 8080
    protocol: TCP

Health Checks Integration

# service-with-health-checks.yaml
apiVersion: v1
kind: Service
metadata:
  name: webapp-service
spec:
  type: ClusterIP
  selector:
    app: webapp
  ports:
  - port: 80
    targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: webapp
  template:
    metadata:
      labels:
        app: webapp
    spec:
      containers:
      - name: webapp
        image: myapp:v1.0
        ports:
        - containerPort: 8080
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5

Commands I Reach For

# Service Management
kubectl create service clusterip webapp --tcp=80:8080
kubectl expose deployment webapp-deployment --port=80 --target-port=8080
kubectl get services
kubectl describe service webapp-service

# Service Types
kubectl expose deployment webapp --port=80 --target-port=8080 --type=NodePort
kubectl expose deployment webapp --port=80 --target-port=8080 --type=LoadBalancer
kubectl expose deployment webapp --port=80 --target-port=8080 --type=ClusterIP

# Service Discovery
kubectl exec -it pod-name -- nslookup webapp-service
kubectl exec -it pod-name -- env | grep WEBAPP
kubectl get endpoints webapp-service

# Port Forwarding (for testing)
kubectl port-forward service/webapp-service 8080:80

# Delete service
kubectl delete service webapp-service

Real-World Service Architecture

Microservices Communication Pattern

# microservices-architecture.yaml
# Frontend Service
apiVersion: v1
kind: Service
metadata:
  name: frontend-service
spec:
  type: NodePort
  selector:
    app: frontend
  ports:
  - port: 80
    targetPort: 3000
    nodePort: 30080
---
# API Gateway Service
apiVersion: v1
kind: Service
metadata:
  name: api-gateway-service
spec:
  type: ClusterIP
  selector:
    app: api-gateway
  ports:
  - port: 8080
    targetPort: 8080
---
# User Service
apiVersion: v1
kind: Service
metadata:
  name: user-service
spec:
  type: ClusterIP
  selector:
    app: user-service
  ports:
  - port: 8081
    targetPort: 8081
---
# Database Service (External)
apiVersion: v1
kind: Service
metadata:
  name: database-service
spec:
  type: ExternalName
  externalName: postgres.database.internal

What to Remember

  • Services provide stable network endpoints for dynamic pods
  • Service types (ClusterIP, NodePort, LoadBalancer) serve different access patterns
  • Service discovery works via environment variables and DNS
  • Load balancing distributes traffic across healthy pods
  • Health probes ensure only ready pods receive traffic
  • Session affinity can maintain client-to-pod relationships
  • ExternalName services can reference external resources

Command Reference

# Service Creation and Management
kubectl expose deployment webapp --port=80 --target-port=8080
kubectl expose deployment webapp --port=80 --target-port=8080 --type=NodePort
kubectl expose deployment webapp --port=80 --target-port=8080 --type=LoadBalancer
kubectl get services
kubectl describe service webapp-service

# Service Discovery Testing
kubectl exec -it pod-name -- nslookup webapp-service
kubectl exec -it pod-name -- wget -qO- http://webapp-service
kubectl get endpoints webapp-service

# Port Forwarding for Testing
kubectl port-forward service/webapp-service 8080:80

# Service Troubleshooting
kubectl get events --field-selector involvedObject.name=webapp-service
kubectl describe service webapp-service
kubectl get pods --show-labels | grep webapp

After Services

Services sit between workload management and real application traffic. Once selectors, endpoints, DNS, and readiness probes make sense, the next Kubernetes networking problems become much easier to debug.

Resources for Further Learning