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.