Learnitweb

Kubernetes Services – An Introduction

Introduction

In Kubernetes, Pods are the fundamental units of deployment that run your application containers. However, when Pods fail, scale, or are updated, they often get replaced with new ones having different IP addresses. This dynamic behavior creates challenges in managing stable network communication between different Pods.

In this tutorial, we will understand why you should avoid directly connecting to any specific Pod and how Kubernetes Services solve this problem by providing a stable networking solution.

Why Shouldn’t You Connect to a Pod Directly?

In a Kubernetes cluster, Pods are constantly created and destroyed due to various reasons:

  1. Pod Failure: When a Pod fails, a new one is created to replace it, which comes with a new IP address.
  2. Scaling Up/Down: When you scale your application, new Pods with different IP addresses are introduced, or existing Pods are removed.
  3. Rolling Updates: During deployment updates, existing Pods are replaced with new ones, resulting in different IP addresses.

This continuous change of IP addresses is called IP churn, and it makes it extremely unreliable to directly connect to any Pod using its IP address. Therefore, you should never rely on direct Pod-to-Pod communication based on their IPs.

How Kubernetes Services Solve This Problem?

A Service (with a capital “S”) is a Kubernetes REST API object, similar to Pods, Deployments, or ReplicaSets.

  • You define a Service in a YAML or JSON manifest file.
  • The manifest is then deployed to the Kubernetes API server, which creates the Service.
  • This Service abstracts the underlying Pods and exposes them via a single stable endpoint.

The primary goal of a Service is to ensure stable network communication to dynamically changing Pods.

When you create a Service in Kubernetes, it automatically gets:

  • Stable IP Address: Unlike Pods, the Service always retains the same IP address, even if Pods behind it change.
  • Stable DNS Name: Kubernetes assigns a DNS name to the Service, allowing other Pods to communicate using the Service name instead of an IP.
  • Stable Port: The port exposed by the Service remains constant, regardless of how many Pods are scaled or replaced.

This guarantees consistent network access to your application even when Pods are frequently changing.

A Service uses labels and selectors to dynamically route traffic to the correct group of Pods.

  • Labels: Key-value pairs attached to Pods to identify them.
  • Selectors: Rules defined in the Service to filter which Pods receive traffic based on their labels.

A Service ensures that clients can consistently access Pods without disruption, even when the Pods scale up or down, encounter failures, or undergo updates and rollbacks. This is possible because the Service continuously monitors the state of the Pods and dynamically adjusts its routing to direct traffic only to healthy Pods. However, the Service itself maintains a fixed IP address, DNS name, and port, providing a stable point of access regardless of changes in the underlying Pods.

Labels and loose coupling

Services are loosely coupled with Pods via labels and selectors. This is the same technology that loosely couples Deployments to Pods and is key to the flexibility of Kubernetes.

Figure an example where 3 Pods are labelled as zone=prod and version=1, and the Service has a selector that matches.

The Service ensures stable networking for all three Pods by receiving incoming requests and forwarding them to the Pods, while also offering basic load balancing.

For the Service to route traffic to a Pod, the Pod must have all the labels that the Service uses for selection. However, the Pod may also have additional labels that the Service does not consider.

The following excerpts, from a Service YAML and Deployment YAML, show how selectors and labels work.

svc.yml

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
apiVersion: v1
kind: Service
metadata:
name: hello-svc
spec:
ports:
- port: 8080
selector:
app: hello-world # Send to Pods with these labels
env: kube # Send to Pods with these labels
apiVersion: v1 kind: Service metadata: name: hello-svc spec: ports: - port: 8080 selector: app: hello-world # Send to Pods with these labels env: kube # Send to Pods with these labels
apiVersion: v1
kind: Service
metadata:
  name: hello-svc
spec:
  ports:
    - port: 8080
  selector:
    app: hello-world  # Send to Pods with these labels
    env: kube          # Send to Pods with these labels

deploy.yml

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-deploy
spec:
replicas: 10
<Snip>
template:
metadata:
labels:
app: hello-world # Pod labels
env: kube # Pod labels
spec:
containers:
<Snip>
apiVersion: apps/v1 kind: Deployment metadata: name: hello-deploy spec: replicas: 10 <Snip> template: metadata: labels: app: hello-world # Pod labels env: kube # Pod labels spec: containers: <Snip>
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-deploy
spec:
  replicas: 10
  <Snip>
  template:
    metadata:
      labels:
        app: hello-world  # Pod labels
        env: kube          # Pod labels
    spec:
      containers:
<Snip>

In the example, the Service is selecting on Pods with the app=hello-world and book=kube labels. The Deployment has a Pod template with the same two labels. This means any Pods it deploys will match the Service’s selector
and receive traffic from it. This loose coupling is how Services know which Pods to send traffic to.

Services and Endpoint objects

To keep track of healthy Pods associated with a Service, Kubernetes uses a combination of label selectors and a resource called the Endpoints object.

What is an Endpoints Object?

Whenever you create a Service in Kubernetes, an Endpoints object is automatically created with it. This object maintains a dynamic list of healthy Pods that match the Service’s label selector. It acts as a bridge between the Service and the actual running Pods.

How Does It Work?

Kubernetes continuously monitors the cluster for Pods that match the labels defined in the Service’s selector. If a new Pod with the matching labels is created, it is automatically added to the Endpoints object. Conversely, if a Pod is terminated or becomes unhealthy, it is removed from the Endpoints object. This ensures that the Service always has an up-to-date list of healthy Pods.

The Endpoints object allows the Service to forward incoming traffic only to healthy Pods. It dynamically adjusts the target Pods without requiring any manual intervention, making the system resilient and highly available.

Traffic Flow Through a Service

When a request is sent to the Service’s IP address (or DNS name), Kubernetes routes the traffic to one of the healthy Pods listed in the Endpoints object. This allows the application to remain accessible regardless of changes in Pod instances.

However, some Kubernetes-native applications (applications designed to integrate with Kubernetes API) can directly query the Endpoints API instead of relying on the Service’s DNS. This approach bypasses the DNS resolution and directly retrieves the list of healthy Pods from the Endpoints object.

Transition to endpoint slices

In recent versions of Kubernetes, the Endpoints object is being gradually replaced by a more efficient construct called Endpoint Slices. Endpoint Slices offer better scalability and performance, especially in large clusters with thousands of Pods. Although the underlying functionality remains the same, Endpoint Slices provide improved efficiency in tracking and updating healthy Pods.

Service types

Kubernetes supports several types of Service. The default type is ClusterIP.

Kubernetes has two types of Service for requests originating from outside the cluster.

  • NodePort
  • LoadBalancer