Every Pod has an IP address. But pods are mortal: they get recreated, rescheduled onto different nodes, and receive new IPs. How do clients reach them reliably? The answer is a Service.
Why You Need a Service
Imagine you have 3 backend replicas. Each Pod has its own IP. The frontend does not know those IPs in advance, and they change on every restart.
A Service is a stable abstraction over a set of pods. It has a permanent IP (ClusterIP) and a DNS name. Requests to the Service are automatically load-balanced across healthy pods.
Frontend → Service (stable IP: 10.96.45.12)
├── Pod 1 (10.244.1.5)
├── Pod 2 (10.244.2.8)
└── Pod 3 (10.244.3.2)
A Service finds its pods by selector (labels). Pods that carry the matching labels are automatically included in the load-balancing pool.
Service Types
ClusterIP (default)
Accessible only inside the cluster. Ideal for internal service-to-service communication.
# service-clusterip.yaml
apiVersion: v1
kind: Service
metadata:
name: backend-service
spec:
type: ClusterIP
selector:
app: backend
ports:
- port: 80 # port on the Service itself
targetPort: 8000 # port on the container
kubectl apply -f service-clusterip.yaml
kubectl get services
# NAME TYPE CLUSTER-IP PORT(S) AGE
# backend-service ClusterIP 10.96.45.12 80/TCP 1m
From any pod in the cluster: curl http://backend-service or curl http://10.96.45.12.
NodePort
Opens a port on every node in the cluster. Accessible from outside via <NodeIP>:<NodePort>.
apiVersion: v1
kind: Service
metadata:
name: frontend-service
spec:
type: NodePort
selector:
app: frontend
ports:
- port: 80
targetPort: 3000
nodePort: 30080 # range 30000–32767 (optional, can be omitted)
# Access from outside (for example in minikube)
minikube service frontend-service --url
# http://192.168.49.2:30080
Use NodePort for quick debugging and dev environments. Not recommended for production.
LoadBalancer
Provisions an external load balancer through the cloud provider (AWS ELB, GCP Load Balancer). Automatically receives an external IP.
apiVersion: v1
kind: Service
metadata:
name: api-service
spec:
type: LoadBalancer
selector:
app: api
ports:
- port: 80
targetPort: 8080
kubectl get services api-service
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# api-service LoadBalancer 10.96.12.34 34.123.45.67 80:31234/TCP 3m
The primary type for production in the cloud. But each LoadBalancer costs money. For many services it is better to use Ingress (one load balancer for all).
ExternalName
An alias for an external DNS name. Useful for migrations or connecting external services.
apiVersion: v1
kind: Service
metadata:
name: external-db
spec:
type: ExternalName
externalName: db.example.com
DNS Inside the Cluster
K8s automatically creates DNS records for every Service. Format:
<service-name>.<namespace>.svc.cluster.local
Examples of addressing services:
# From the same namespace
curl http://backend-service
# From another namespace
curl http://backend-service.production.svc.cluster.local
# Short form from another namespace
curl http://backend-service.production
DNS is provided by CoreDNS — a Control Plane component. Pods are automatically configured to use the internal DNS.
kubectl expose: Quick Service Creation
# Create a Service for a Deployment
kubectl expose deployment my-app --port=80 --target-port=8000
# Create a NodePort
kubectl expose deployment my-app --type=NodePort --port=80
# Create a LoadBalancer
kubectl expose deployment my-app --type=LoadBalancer --port=80
Comparing Service Types
| Type | Access | When to Use |
|---|---|---|
| ClusterIP | Inside the cluster only | Service-to-service communication |
| NodePort | External, via node IP | Dev / testing |
| LoadBalancer | External, via cloud LB | Production API in the cloud |
| ExternalName | DNS alias | External services |
Verifying a Service
# Service details
kubectl describe service backend-service
# Endpoints (live pods behind the Service)
kubectl get endpoints backend-service
# NAME ENDPOINTS AGE
# backend-service 10.244.1.5:8000,10.244.2.8:8000,... 5m
# Temporary debug pod for network troubleshooting
kubectl run debug --image=busybox -it --rm -- wget -qO- http://backend-service
💬 Comments (0)
No comments yet
Be the first to share your opinion about this article!