Services

Kubernetes Services provide a stable network endpoint for accessing pods, even as pods are created, deleted, or moved between nodes. Think of a Service as a phone number that stays the same, while the actual phones (pods) behind it can change. Services solve the fundamental problem that pod IP addresses are temporary—when a pod restarts or is rescheduled, it gets a new IP address, but your Service IP and DNS name remain constant.

The Problem Services Solve

Pods in Kubernetes are ephemeral. They can be created, deleted, rescheduled to different nodes, or scaled up and down. Each pod gets its own IP address, but that IP address is temporary:

graph LR A[Pod Created] --> B[Gets IP: 10.244.1.5] B --> C[Pod Deleted] C --> D[New Pod Created] D --> E[Gets Different IP: 10.244.2.8] style A fill:#e1f5ff style B fill:#fff4e1 style E fill:#ffe1e1

If your application tried to connect directly to pod IPs, it would break every time pods restart or scale. Services provide a stable abstraction layer:

graph TB A[Client Application] --> B[Service: my-app:80] B --> C[Pod 1: 10.244.1.5] B --> D[Pod 2: 10.244.1.6] B --> E[Pod 3: 10.244.2.3] F[Pod 1 Deleted] --> G[New Pod 4: 10.244.2.7] B --> G style A fill:#e1f5ff style B fill:#e8f5e9 style C fill:#fff4e1 style D fill:#fff4e1 style E fill:#fff4e1 style G fill:#e8f5e9

How Services Work

A Service selects pods using labels and creates a stable IP address and DNS name. The Service continuously watches for pods matching its selector and updates its internal routing table (Endpoints) as pods come and go.

graph TB subgraph service[Service: my-app] A[Service IP: 10.96.0.1] B[DNS: my-app.default.svc.cluster.local] end subgraph pods[Selected Pods] C[Pod 1<br/>Labels: app=my-app] D[Pod 2<br/>Labels: app=my-app] E[Pod 3<br/>Labels: app=my-app] end A --> C A --> D A --> E B --> A style A fill:#e8f5e9 style B fill:#e8f5e9 style C fill:#fff4e1 style D fill:#fff4e1 style E fill:#fff4e1

Service Types

Kubernetes provides four types of Services, each suited for different access patterns:

ClusterIP (Default)

The default Service type. Provides a stable IP address accessible only within the cluster. Used for internal service-to-service communication.

graph LR A[Pod in Cluster] --> B[ClusterIP Service] B --> C[Backend Pods] D[External Client] -.->|Cannot Access| B style A fill:#e1f5ff style B fill:#e8f5e9 style D fill:#ffe1e1

NodePort

Exposes the Service on each node’s IP at a static port. External clients can access the Service using <NodeIP>:<NodePort>. Useful for development or bare-metal clusters.

graph TB A[External Client] --> B[Node 1: 192.168.1.10:30080] A --> C[Node 2: 192.168.1.11:30080] B --> D[NodePort Service] C --> D D --> E[Backend Pods] style A fill:#e1f5ff style D fill:#e8f5e9 style E fill:#fff4e1

LoadBalancer

Automatically provisions an external load balancer (cloud provider specific). Provides the most straightforward way to expose Services externally in cloud environments.

graph TB A[Internet] --> B[Cloud Load Balancer<br/>External IP: 203.0.113.1] B --> C[LoadBalancer Service] C --> D[Node 1] C --> E[Node 2] D --> F[Backend Pods] E --> F style A fill:#e1f5ff style B fill:#fff4e1 style C fill:#e8f5e9 style F fill:#fff4e1

Headless (clusterIP: None)

A Service without a cluster IP. Used when you need direct pod access or custom service discovery. Returns individual pod IPs via DNS instead of a single Service IP.

graph TB A[Client] --> B[Headless Service<br/>clusterIP: None] B --> C[Pod 1 IP: 10.244.1.5] B --> D[Pod 2 IP: 10.244.1.6] B --> E[Pod 3 IP: 10.244.2.3] style A fill:#e1f5ff style B fill:#e8f5e9 style C fill:#fff4e1 style D fill:#fff4e1 style E fill:#fff4e1

Service Discovery

Services are automatically discoverable via DNS. Kubernetes creates DNS records for Services that allow pods to find Services by name:

  • Fully Qualified Domain Name (FQDN): <service-name>.<namespace>.svc.cluster.local
  • Short name: <service-name> (within the same namespace)
  • Cross-namespace: <service-name>.<namespace>
graph LR A[Pod] --> B[DNS Query: my-app] B --> C[CoreDNS] C --> D[Service IP: 10.96.0.1] D --> E[Service Routes to Pods] style A fill:#e1f5ff style C fill:#fff4e1 style D fill:#e8f5e9 style E fill:#fff4e1

Basic Service Example

Here’s a simple Service that routes traffic to pods labeled app: nginx:

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  namespace: default
spec:
  selector:
    app: nginx
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP
  type: ClusterIP

Key fields:

  • selector: Labels that identify which pods this Service targets
  • ports: Port mapping (Service port → pod port)
  • type: Service type (ClusterIP is default)

The Service will automatically find all pods with app: nginx labels and route traffic to them.

Service Ports

Services can expose multiple ports and map them to different pod ports:

apiVersion: v1
kind: Service
metadata:
  name: multi-port-service
spec:
  selector:
    app: my-app
  ports:
  - name: http
    port: 80
    targetPort: 8080
    protocol: TCP
  - name: https
    port: 443
    targetPort: 8443
    protocol: TCP
  - name: metrics
    port: 9090
    targetPort: 9090
    protocol: TCP

Service Lifecycle

graph TD A[Service Created] --> B[Selector Evaluated] B --> C[Pods Matching Selector Found] C --> D[Endpoints Resource Created] D --> E[Service IP Allocated] E --> F[DNS Record Created] F --> G[Service Ready] H[New Pod Created] --> I{Matches Selector?} I -->|Yes| J[Added to Endpoints] I -->|No| K[Ignored] J --> L[Traffic Routed to New Pod] M[Pod Deleted] --> N[Removed from Endpoints] N --> O[Traffic No Longer Routed] style A fill:#e1f5ff style G fill:#e8f5e9 style L fill:#e8f5e9 style O fill:#ffe1e1

When to Use Services

Use Services when you need:

Stable endpoints - Access to pods that may restart or move
Load balancing - Distribute traffic across multiple pod instances
Service discovery - Find services by DNS name instead of IP addresses
Abstraction - Decouple client applications from pod details
Internal communication - Connect services within the cluster
External access - Expose applications outside the cluster (NodePort/LoadBalancer)

Service vs Direct Pod Access

Using a Service (recommended):

  • Stable IP and DNS name
  • Automatic load balancing
  • Works across pod restarts
  • Handles pod scaling automatically

Direct pod access (not recommended):

  • Pod IPs change on restart
  • No load balancing
  • Breaks when pods are rescheduled
  • Requires manual IP management

Best Practices

  1. Always use Services - Never connect directly to pod IPs in production
  2. Use descriptive names - Service names should clearly indicate their purpose
  3. Match selectors carefully - Ensure Service selectors match your pod labels exactly
  4. Use port names - Name your ports for clarity, especially with multiple ports
  5. Choose the right type - Use ClusterIP for internal, LoadBalancer for external cloud access
  6. Monitor Endpoints - Check that Services are finding and routing to the correct pods
  7. Use namespaces - Organize Services with namespaces for better isolation
  8. Document port mappings - Clearly document which ports map to which pod ports

Troubleshooting

Service Not Routing Traffic

  1. Check selector matches pods: kubectl get pods -l app=my-app
  2. Verify Endpoints exist: kubectl get endpoints <service-name>
  3. Check Service selector: kubectl describe service <service-name>
  4. Verify pod labels: kubectl get pods --show-labels

DNS Not Resolving

  1. Check CoreDNS is running: kubectl get pods -n kube-system -l k8s-app=kube-dns
  2. Try FQDN: Use <service>.<namespace>.svc.cluster.local instead of short name
  3. Check namespace: Ensure client and Service are in the same namespace (or use FQDN)

Port Connection Issues

  1. Verify targetPort matches pod container port
  2. Check pod is listening on the correct port
  3. Test connectivity: kubectl run -it --rm debug --image=busybox --restart=Never -- wget -O- <service-name>:<port>

Topics

See Also