Services
Pods are ephemeral — they come and go, and their IP addresses change. Services provide a stable network endpoint to access a set of Pods, with built-in load balancing and service discovery.
The Problem Services Solve
Consider this scenario:
- You have 3 nginx Pods behind a Deployment
- Each Pod has its own IP (e.g., 10.244.0.5, 10.244.0.6, 10.244.0.7)
- If a Pod dies, the replacement gets a new IP
- Other applications can’t hardcode Pod IPs
Services solve this by providing a stable virtual IP (ClusterIP) that automatically routes traffic to healthy Pods.
┌─────────────┐
│ Service │
│ ClusterIP: │
│ 10.96.0.100 │
└──────┬───────┘
│
┌─────────┼─────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Pod 1 │ │ Pod 2 │ │ Pod 3 │
│10.244. │ │10.244. │ │10.244. │
│ 0.5 │ │ 0.6 │ │ 0.7 │
└─────────┘ └─────────┘ └─────────┘
Service Types
Kubernetes offers four types of Services:
| Type | Accessible From | Use Case |
|---|---|---|
| ClusterIP | Inside the cluster only | Internal communication between microservices |
| NodePort | External via <NodeIP>:<NodePort> |
Development, testing |
| LoadBalancer | External via cloud load balancer | Production on cloud providers |
| ExternalName | DNS alias to external service | Integrating external services |
ClusterIP (Default)
The default Service type. Creates an internal-only virtual IP.
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
type: ClusterIP # Default, can be omitted
selector:
app: nginx # Routes to Pods with this label
ports:
- protocol: TCP
port: 80 # Service port (what clients connect to)
targetPort: 80 # Container port (where traffic is sent)
# Create the Service
kubectl apply -f service.yaml
# Check it
kubectl get services
# or: kubectl get svc
Output:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-service ClusterIP 10.96.45.123 <none> 80/TCP 5s
Accessing a ClusterIP Service
From inside the cluster, you can access it by:
# By Service name (same namespace)
curl http://nginx-service
# By fully qualified domain name
curl http://nginx-service.default.svc.cluster.local
# Format: <service-name>.<namespace>.svc.cluster.local
NodePort
Exposes the Service on a static port on each node’s IP. Useful for development.
apiVersion: v1
kind: Service
metadata:
name: nginx-nodeport
spec:
type: NodePort
selector:
app: nginx
ports:
- protocol: TCP
port: 80 # Internal Service port
targetPort: 80 # Container port
nodePort: 30080 # External port (30000-32767)
# Access from outside the cluster
curl http://<node-ip>:30080
# In Minikube, get the URL:
minikube service nginx-nodeport --url
LoadBalancer
Creates an external load balancer (on cloud providers like AWS, GCP, Azure).
apiVersion: v1
kind: Service
metadata:
name: nginx-loadbalancer
spec:
type: LoadBalancer
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
kubectl get svc nginx-loadbalancer
Output (on a cloud provider):
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-loadbalancer LoadBalancer 10.96.78.210 203.0.113.50 80:31234/TCP 30s
Note: In Minikube, LoadBalancer type won’t get a real external IP. Use minikube tunnel to simulate it, or use NodePort instead.
ExternalName
Maps a Service to an external DNS name. No proxying — just a DNS CNAME record.
apiVersion: v1
kind: Service
metadata:
name: external-db
spec:
type: ExternalName
externalName: db.example.com
Now Pods can connect to external-db and it resolves to db.example.com.
Service Discovery
Kubernetes provides two mechanisms for service discovery:
1. DNS (Recommended)
Every Service gets a DNS entry automatically:
<service-name>.<namespace>.svc.cluster.local
# From any Pod in the "default" namespace:
curl http://nginx-service # Same namespace
curl http://nginx-service.default # With namespace
curl http://nginx-service.default.svc.cluster.local # FQDN
2. Environment Variables
Kubernetes injects Service details as environment variables into Pods (only for Services that existed before the Pod was created):
# Inside a Pod:
echo $NGINX_SERVICE_SERVICE_HOST # 10.96.45.123
echo $NGINX_SERVICE_SERVICE_PORT # 80
Endpoints
Behind every Service is an Endpoints object that lists the IPs of all matching Pods:
kubectl get endpoints nginx-service
Output:
NAME ENDPOINTS AGE
nginx-service 10.244.0.5:80,10.244.0.6:80,10.244.0.7:80 5m
When Pods are added or removed, the Endpoints are updated automatically.
Multi-Port Services
A Service can expose multiple ports:
apiVersion: v1
kind: Service
metadata:
name: web-app
spec:
selector:
app: web-app
ports:
- name: http
protocol: TCP
port: 80
targetPort: 8080
- name: https
protocol: TCP
port: 443
targetPort: 8443
Note: When you have multiple ports, each port must have a name.
Headless Services
A headless Service (with clusterIP: None) doesn’t get a virtual IP. Instead, DNS returns the individual Pod IPs directly. Useful for StatefulSets.
apiVersion: v1
kind: Service
metadata:
name: db-headless
spec:
clusterIP: None
selector:
app: database
ports:
- port: 5432
# DNS returns individual Pod IPs instead of a single ClusterIP
nslookup db-headless.default.svc.cluster.local
# Returns: 10.244.0.5, 10.244.0.6, 10.244.0.7
Hands-On: Complete Example
Let’s create a Deployment and expose it with a Service:
# Create a Deployment
kubectl create deployment web --image=nginx:1.27 --replicas=3
# Expose it as a NodePort Service
kubectl expose deployment web --type=NodePort --port=80
# Check the Service
kubectl get svc web
# In Minikube, open it in a browser
minikube service web
Summary
| Service Type | Scope | Best For |
|---|---|---|
| ClusterIP | Internal only | Microservice-to-microservice communication |
| NodePort | External via node port | Development, testing |
| LoadBalancer | External via cloud LB | Production on cloud |
| ExternalName | DNS alias | External service integration |
- Services provide stable endpoints for ephemeral Pods
- DNS-based discovery is the recommended approach
- Use labels and selectors to connect Services to Pods
- Headless Services return individual Pod IPs (for StatefulSets)
What’s Next?
As your cluster grows, you’ll need to organize resources and isolate workloads. In the next chapter, we’ll learn about Namespaces — Kubernetes’ way of creating virtual clusters within a physical cluster.