The Service Object via API
In the previous chapter, we explored Pods – their network identity (podIP
), their readiness state, and how to group them using labels. However, we also noted a key limitation: Pod IPs are ephemeral. If a Pod dies and is replaced by a Deployment, the new Pod gets a new IP address. Relying directly on Pod IPs for communication between different parts of your application (e.g., frontend to backend) would be fragile and require constant updates.
This is the primary problem solved by Kubernetes Services. A Service provides a stable, virtual IP address (called the ClusterIP
) and DNS name within the cluster. When traffic hits the Service's IP and port, Kubernetes automatically load balances it across the set of healthy Pods that match the Service's selector. This provides a reliable abstraction layer for accessing groups of Pods.
In this chapter, we'll explore how Services work under the hood, focusing on their API representation and how client-go
allows us to manage them programmatically. We'll also look at the crucial Endpoints
object, which dynamically tracks the actual ready Pod IPs backing a Service.
The Service
Object via API (v1.Service
)
Service
Object via API (v1.Service
)Just like Pods, Services are standard Kubernetes API resources. When you interact with them using client-go
, you'll be working with the v1.Service
struct from the k8s.io/api/core/v1
package. The core definition and behavior of a Service are primarily defined within its spec
field.
Let's break down the most important fields within ServiceSpec
:
spec.type
(string)
This field defines how the Service is exposed. The main types are:
ClusterIP
(Default): Exposes the Service on a cluster-internal IP address (spec.clusterIP
). This IP is only reachable from within the cluster. This is the most common type for internal communication between different microservices.NodePort
: Exposes the Service on each Node's IP address at a static port (thespec.ports[].nodePort
). AClusterIP
Service is automatically created as well, to which theNodePort
traffic is routed. This allows external traffic to reach the Service via<NodeIP>:<NodePort>
, often used for development, testing, or when you manage your own external load balancers. ThenodePort
is typically chosen from a configured range (e.g., 30000-32767).LoadBalancer
: Exposes the Service externally using a cloud provider's load balancer (e.g., an AWS ELB, GCP Load Balancer). ANodePort
andClusterIP
Service are automatically created, and the external load balancer routes traffic to theNodePort
. This is the standard way to expose services directly to the internet in cloud environments. The cloud provider asynchronously provisions the load balancer and populates its external IP/hostname into the Service'sstatus.loadBalancer.ingress
.ExternalName
: This is a special type that maps the Service to an external DNS name (specified inspec.externalName
) instead of using selectors and Pods. It acts as a CNAME record within the cluster's DNS. We won't focus heavily on this type in this networking book.
The type
field dictates the fundamental accessibility of the Service.
spec.selector
(map[string]string)
This is arguably the most critical field for ClusterIP
, NodePort
, and LoadBalancer
Services. It's a map of key-value pairs – exactly like the Pod labels we discussed. The Service continuously monitors the cluster for Pods whose labels match this selector exactly.
Link: Only Pods matching this selector and passing their readiness probes become endpoints for this Service.
Format:
map[string]string{"app": "my-backend", "tier": "database"}
Absence: If the selector is omitted or empty, the Service doesn't automatically target Pods. The corresponding
Endpoints
object must be managed manually (an advanced use case).
# Example Service Spec Snippet
apiVersion: v1
kind: Service
metadata:
name: my-backend-service
spec:
selector:
app: my-backend # Service targets Pods with label 'app=my-backend'
ports:
# ... ports defined below ...
type: ClusterIP # Service only reachable within the cluster
spec.ports
([]v1.ServicePort)
This is a slice defining one or more ports the Service will expose. Each ServicePort
object within the slice has several fields:
port
(int32, required): The port number that the Service itself will listen on (on itsclusterIP
).targetPort
(intstr.IntOrString): The port on the target Pods (matching the selector) to which traffic arriving at the Service'sport
should be forwarded. This can be:A specific port number (e.g.,
8080
).The name of a port defined in the Pod's
spec.containers[*].ports[*].name
(e.g.,"http-api"
). Using a named port is often preferred as it decouples the Service from specific container port numbers. If omitted, defaults to the same value as theport
field.
protocol
(string): The network protocol. Defaults toTCP
. Can beUDP
,SCTP
.name
(string, optional): A name for this specific service port definition. Useful if a Service exposes multiple ports. Required by some tools like Istio.nodePort
(int32, optional): Only relevant ifspec.type
isNodePort
orLoadBalancer
. This specifies the static port number on the Node where the Service will be exposed. If not specified forNodePort
, Kubernetes allocates one from the configured range.
# Example Service Spec Snippet - Ports
spec:
selector:
app: my-app
ports:
- name: http # Name for this port mapping
protocol: TCP
port: 80 # Service listens on port 80 (on its ClusterIP)
targetPort: 8080 # Forwards traffic to container port 8080 on matching Pods
- name: metrics
protocol: TCP
port: 9090 # Service listens on port 9090
targetPort: metrics-port # Forwards traffic to the *named port* 'metrics-port' on Pods
type: ClusterIP
Status Field
While spec
defines the desired state, status
reflects the runtime state, mainly relevant for LoadBalancer
types:
status.loadBalancer.ingress
: Fortype: LoadBalancer
, this field is populated by the cloud provider with the external IP address or hostname of the provisioned load balancer once it's ready.
Understanding these fields within the v1.Service
struct is essential for creating and managing Services programmatically. In the next sections, we'll use client-go
to create Services and explore the Endpoints
object that bridges the gap between the stable Service IP and the dynamic Pod IPs.
Last updated
Was this helpful?