Kubernetes Services
Introduction
When working with containerized applications in Kubernetes, one of the key challenges is enabling reliable network communication. Pods in Kubernetes are ephemeral - they can be created, destroyed, and rescheduled based on scaling needs or node failures. This dynamic nature makes it difficult to rely on pod IP addresses for stable communication.
This is where Kubernetes Services come in. A Service is an abstraction that defines a logical set of pods and a policy to access them. Services provide a stable network endpoint for pods, allowing other components to reliably communicate with your application regardless of what happens to the individual pods underneath.
Why We Need Services
Let's consider a scenario to understand why Services are essential:
- You deploy a web application with 3 replicas (pods)
- Each pod gets its own IP address
- These pods might be terminated, recreated, or scaled up/down over time
- You need a reliable way for other applications to connect to your web application
Without Services, clients would need to keep track of all pod IP addresses and update their configurations whenever pods change. Services solve this problem by providing a single, stable IP address and DNS name that routes traffic to the appropriate pods.
Service Types
Kubernetes offers several types of Services to meet different networking requirements:
ClusterIP (Default)
A ClusterIP Service exposes the Service on an internal IP address accessible only within the cluster. This is the default Service type and is ideal for internal communication between applications.
apiVersion: v1
kind: Service
metadata:
name: my-backend-service
spec:
selector:
app: my-backend
ports:
- port: 80
targetPort: 8080
type: ClusterIP
NodePort
A NodePort Service exposes the Service on each Node's IP at a static port. This makes the Service accessible from outside the cluster using <NodeIP>:<NodePort>
.
apiVersion: v1
kind: Service
metadata:
name: my-web-service
spec:
selector:
app: my-web
ports:
- port: 80
targetPort: 8080
nodePort: 30080 # Optional: Kubernetes assigns one if not specified (range 30000-32767)
type: NodePort
LoadBalancer
A LoadBalancer Service creates an external load balancer in cloud environments (like AWS, GCP, Azure) and assigns a fixed, external IP to the Service.
apiVersion: v1
kind: Service
metadata:
name: my-public-service
spec:
selector:
app: my-public-app
ports:
- port: 80
targetPort: 8080
type: LoadBalancer
ExternalName
An ExternalName Service maps the Service to an external DNS name, acting as a CNAME record. This is useful for providing access to external services.
apiVersion: v1
kind: Service
metadata:
name: my-database
spec:
type: ExternalName
externalName: db.example.com
Service Discovery in Kubernetes
Kubernetes provides two primary mechanisms for service discovery:
- Environment Variables: When a Pod is created, Kubernetes adds environment variables for each active Service.
- DNS: Kubernetes clusters typically have a DNS service (CoreDNS) that allows pods to resolve service names to their cluster IPs.
The DNS approach is the recommended method as it's more flexible and intuitive:
# Access a service using its name within the same namespace
curl http://my-service:80
# Access a service in a different namespace
curl http://my-service.other-namespace:80
# Access a service in a different namespace with the cluster domain suffix
curl http://my-service.other-namespace.svc.cluster.local:80
Service Selectors
Services use label selectors to determine which pods should receive traffic. This is a key component of Kubernetes' loose coupling approach:
apiVersion: v1
kind: Service
metadata:
name: my-app-service
spec:
selector:
app: my-app
tier: frontend
ports:
- port: 80
targetPort: 8080
In this example, the Service will route traffic to all pods with labels app: my-app
and tier: frontend
.
Headless Services
Sometimes you don't need load balancing or a single Service IP. In these cases, you can create a "headless" Service by setting .spec.clusterIP
to None
:
apiVersion: v1
kind: Service
metadata:
name: my-headless-service
spec:
clusterIP: None
selector:
app: my-stateful-app
ports:
- port: 80
targetPort: 8080
A headless Service returns the IP addresses of the pods directly in DNS queries, which is useful for:
- StatefulSets where each pod needs a stable identity
- Applications that want to connect to all pods (not just one randomly)
- Applications that handle their own load balancing
EndpointSlices
Behind the scenes, Kubernetes automatically creates and manages EndpointSlices for Services. These contain the network endpoints (IP addresses, ports) of pods matching the Service selector.
You rarely need to interact with EndpointSlices directly, but understanding that they exist helps explain how Services actually route traffic to pods.
Port Configurations
Services allow different port configurations:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: my-app
ports:
- name: http
port: 80 # Port exposed by the service
targetPort: 8080 # Port of the application in the pod
protocol: TCP # Protocol (TCP, UDP, or SCTP)
For more complex scenarios, you can map to named ports in your pod specifications:
apiVersion: v1
kind: Pod
metadata:
name: my-pod
labels:
app: my-app
spec:
containers:
- name: my-container
image: nginx
ports:
- name: http-web
containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: my-app
ports:
- port: 80
targetPort: http-web # Refers to the named port in the pod
Visualizing Services
Let's visualize how a Service works in Kubernetes:
Practical Example: Multi-Tier Application
Let's walk through a complete example of setting up Services for a three-tier application:
Step 1: Create the Frontend Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
spec:
replicas: 3
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: frontend
image: my-frontend:latest
ports:
- containerPort: 80
Step 2: Create the Frontend Service (LoadBalancer)
apiVersion: v1
kind: Service
metadata:
name: frontend-service
spec:
selector:
app: frontend
ports:
- port: 80
targetPort: 80
type: LoadBalancer
Step 3: Create the Backend Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
spec:
replicas: 2
selector:
matchLabels:
app: backend
template:
metadata:
labels:
app: backend
spec:
containers:
- name: backend
image: my-backend:latest
ports:
- containerPort: 8080
Step 4: Create the Backend Service (ClusterIP)
apiVersion: v1
kind: Service
metadata:
name: backend-service
spec:
selector:
app: backend
ports:
- port: 80
targetPort: 8080
type: ClusterIP
Step 5: Create the Database Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: database
spec:
replicas: 1
selector:
matchLabels:
app: database
template:
metadata:
labels:
app: database
spec:
containers:
- name: database
image: postgres:13
ports:
- containerPort: 5432
env:
- name: POSTGRES_PASSWORD
value: "mysecretpassword"
- name: POSTGRES_DB
value: "myapp"
Step 6: Create the Database Service (ClusterIP)
apiVersion: v1
kind: Service
metadata:
name: database-service
spec:
selector:
app: database
ports:
- port: 5432
targetPort: 5432
type: ClusterIP
In this architecture:
- External users access the application through the
frontend-service
LoadBalancer - The frontend pods communicate with backend pods through
backend-service
- The backend pods communicate with the database pod through
database-service
- All internal communication is secured within the cluster
Service Network Traffic Flow
Service Networking Advanced Concepts
SessionAffinity
Services support "session affinity" which can route all traffic from a client to the same pod:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: my-app
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800 # 3 hours
ports:
- port: 80
targetPort: 8080
ExternalTrafficPolicy
For NodePort and LoadBalancer services, you can configure how external traffic is handled:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
type: LoadBalancer
externalTrafficPolicy: Local # Only routes to pods on the same node
Options:
Cluster
(default): Traffic can be routed to pods on any nodeLocal
: Traffic is only routed to pods on the node that received the request (preserves client source IP and avoids extra hops)
Troubleshooting Services
When your Services aren't working as expected, follow these troubleshooting steps:
-
Verify Service exists:
bashkubectl get service my-service
-
Check Service details:
bashkubectl describe service my-service
-
Verify Endpoints are created:
bashkubectl get endpoints my-service
-
Test connectivity from a pod:
bashkubectl run test-pod --image=busybox -it --rm -- wget -O- my-service:80
-
Verify labels match between Service selector and Pod labels:
bashkubectl get pods --selector=app=my-app
Summary
Kubernetes Services are a fundamental networking concept that:
- Provide stable network identities to pods
- Enable service discovery within your cluster
- Support different access patterns (internal-only, node-level, external)
- Abstract away the complexity of pod changes and failures
- Allow your applications to reliably communicate regardless of pod lifecycle events
Understanding Services is essential for building reliable networked applications in Kubernetes. They form the foundation for more advanced concepts like Ingress controllers and Service Meshes.
Additional Resources
Exercises
-
Create a simple deployment and expose it using each of the different Service types. Observe how accessibility changes with each type.
-
Create a multi-tier application (similar to our example) with frontend, backend, and database services.
-
Experiment with headless Services for a StatefulSet and observe how DNS resolution works compared to regular Services.
-
Test different service discovery methods (environment variables vs. DNS) and compare their effectiveness.
-
Implement session affinity and observe how traffic is routed consistently to the same pods.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)