LFCE 140 ๐ง Kubernetes โ Services
A Kubernetes Service is an abstraction that defines a logical set of Pods and a policy for accessing them. Pods are ephemeral: they are created, deleted, and replaced with new IP addresses. A Service provides a stable virtual IP address and DNS name that front-end clients can use, while the Service’s selector determines which Pods receive the traffic . The Service is the layer that decouples the consumer from the Pod’s lifecycle, and the type field determines how the Service is exposed: internally to the cluster, on a node port, through a cloud load balancer, or as an alias for an external DNS name.
This chapter covers the four Service typesโClusterIP, NodePort, LoadBalancer, and ExternalNameโthe selector mechanism that binds a Service to Pods, the port mapping between the Service and the container, and the headless Service that returns Pod IPs directly instead of a virtual IP.
Key point: A Service selects Pods by label. If the selector does not match the Pods’ labels, the Service has no endpoints and all traffic fails. The kubectl get endpointslices command is the first diagnostic step when a Service is not working.
Why Services exist
The Pod IP problem. Every Pod gets its own IP address, but that address changes when the Pod is recreated. A Deployment that replaces a failed Pod creates a new Pod with a new IP. A client that hardcodes a Pod IP breaks on the next restart. A Service provides a stable ClusterIP and DNS name that survive Pod churn .
The discovery problem. A front-end needs to reach a back-end without knowing which Pods exist or where they are running. The Service’s selector matches the back-end Pods by label, and the Service’s DNS name (my-service.default.svc.cluster.local) resolves to the Service’s virtual IP. The client uses the name; Kubernetes handles the routing .
The exposure problem. Not every service should be reachable from outside the cluster. A database should only be reachable from the application tier. A web front-end should be reachable from the internet. The Service type field determines the exposure level, from internal-only (ClusterIP) to cloud load balancer (LoadBalancer) .
a. ClusterIP: the default internal Service
ClusterIP is the default type. It allocates a virtual IP address that is reachable only from within the cluster. The Service’s DNS name resolves to this IP, and kube-proxy on each node routes traffic from the virtual IP to one of the selected Pods .
apiVersion: v1
kind: Service
metadata:
name: api-backend
spec:
type: ClusterIP
selector:
app: api-backend
ports:
- protocol: TCP
port: 80
targetPort: 8080
The port is the port the Service exposes. The targetPort is the port the container listens on. A client calls the Service at api-backend:80, and the request is forwarded to a selected Pod at port 8080 . The targetPort can also be a named port that matches the container’s ports field.
A ClusterIP Service is appropriate for internal communication: the front-end calls the back-end, the back-end calls the database, and none of these are reachable from outside the cluster.
b. NodePort: exposing a port on every node
NodePort is a superset of ClusterIP. It allocates a ClusterIP for internal traffic and additionally opens a port on every node in the cluster. External clients call NodeIP:NodePort, and the request is forwarded to the Service’s selected Pods .
apiVersion: v1
kind: Service
metadata:
name: web-nodeport
spec:
type: NodePort
selector:
app: web
ports:
- port: 80
targetPort: 8080
nodePort: 30080
The nodePort value is in the range 30000โ32767. If it is omitted, Kubernetes allocates one automatically. The Service is reachable in two ways: internally at ClusterIP:port, and externally at NodeIP:nodePort .
NodePort is useful when the cluster does not have a cloud load balancer, or when a specific port must be reachable on the nodes for an external load balancer to use. The documentation notes that some cluster configurations use a NodePort Service behind an external application load balancer .
c. LoadBalancer: a cloud load balancer
LoadBalancer is a superset of NodePort. It allocates a ClusterIP, a NodePort, and additionally provisions an external load balancer through the cloud provider’s integration. The load balancer gets a stable external IP address, and traffic from the internet reaches the Service through it .
apiVersion: v1
kind: Service
metadata:
name: web-lb
spec:
type: LoadBalancer
selector:
app: web
ports:
- port: 80
targetPort: 8080
The external IP appears in the Service’s status.loadBalancer.ingress field after the cloud controller provisions the load balancer. In GKE, a regional external passthrough Network Load Balancer is created, and the IP is visible in kubectl get service . The loadBalancerClass field can select a non-default load balancer implementation .
LoadBalancer is the standard way to expose a service to the internet on a cloud-managed cluster. On a self-hosted cluster without a cloud provider, the external IP remains <pending> because there is no controller to provision the load balancer.
d. ExternalName and headless Services
ExternalName is fundamentally different from the other three. It does not select Pods and does not allocate an IP address. It maps the Service’s DNS name to an external DNS name via a CNAME record. A client inside the cluster calls the Service’s name, and the DNS resolver returns the external name .
apiVersion: v1
kind: Service
metadata:
name: external-db
spec:
type: ExternalName
externalName: db.example.com
A client calls external-db.default.svc.cluster.local, and DNS resolves it to db.example.com. No proxying is involved; the client connects directly to the external address. This is useful for migrating a service out of the cluster without changing the clients’ configuration .
A headless Service is created by setting clusterIP: None on a ClusterIP Service. It does not allocate a virtual IP. Instead, DNS returns the IP addresses of the individual Pods that match the selector. This allows a client to connect to a specific Pod rather than a load-balanced virtual IP, which is useful for StatefulSets and for clients that need to manage their own connection pooling .
Complete Example Session
# ============================================
# PART 1: A DEPLOYMENT TO EXPOSE
# ============================================
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
# ============================================
# PART 2: CLUSTERIP SERVICE
# ============================================
apiVersion: v1
kind: Service
metadata:
name: web-clusterip
spec:
type: ClusterIP
selector:
app: web
ports:
- port: 80
targetPort: 80
# ============================================
# PART 3: VERIFY THE SERVICE
# ============================================
kubectl apply -f deployment.yaml -f service.yaml
kubectl get service web-clusterip
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
# web-clusterip ClusterIP 10.96.123.45 <none> 80/TCP
# ============================================
# PART 4: CHECK THE ENDPOINTS
# ============================================
kubectl get endpointslices -l kubernetes.io/service-name=web-clusterip
# NAME ADDRESSTYPE PORTS ENDPOINTS
# web-clusterip-xx IPv4 80 10.244.1.5,10.244.2.3,10.244.3.7
# ============================================
# PART 5: NODEPORT SERVICE
# ============================================
apiVersion: v1
kind: Service
metadata:
name: web-nodeport
spec:
type: NodePort
selector:
app: web
ports:
- port: 80
targetPort: 80
nodePort: 30080
# ============================================
# PART 6: ACCESS VIA NODEPORT
# ============================================
# Get a node IP
kubectl get nodes -o wide
# curl http://<node-ip>:30080
# ============================================
# PART 7: LOADBALANCER SERVICE
# ============================================
apiVersion: v1
kind: Service
metadata:
name: web-lb
spec:
type: LoadBalancer
selector:
app: web
ports:
- port: 80
targetPort: 80
# ============================================
# PART 8: CHECK THE EXTERNAL IP
# ============================================
kubectl get service web-lb
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
# web-lb LoadBalancer 10.96.123.46 203.0.113.10 80:31234/TCP
# ============================================
# PART 9: EXTERNALNAME SERVICE
# ============================================
apiVersion: v1
kind: Service
metadata:
name: external-db
spec:
type: ExternalName
externalName: db.example.com
# ============================================
# PART 10: HEADLESS SERVICE
# ============================================
apiVersion: v1
kind: Service
metadata:
name: web-headless
spec:
clusterIP: None
selector:
app: web
ports:
- port: 80
targetPort: 80
The ten parts covered the Deployment, the ClusterIP Service, verification, endpoints, the NodePort Service, NodePort access, the LoadBalancer Service, the external IP, the ExternalName Service, and the headless Service.
Quick Reference
Service types
| Type | Scope | External access | Use case |
|---|---|---|---|
ClusterIP | Internal | No | Internal service communication |
NodePort | Cluster nodes | Yes (NodeIP:NodePort) | No cloud LB, specific port |
LoadBalancer | Cloud LB | Yes (external IP) | Internet-facing on cloud |
ExternalName | DNS alias | No (CNAME) | External service alias |
Headless (clusterIP: None) | DNS returns Pod IPs | No | StatefulSets, direct Pod access |
Port mapping
| Field | Meaning |
|---|---|
port | Port the Service exposes |
targetPort | Port the container listens on |
nodePort | Port on each node (30000โ32767) |
Selector and endpoints
| Item | Detail |
|---|---|
| Selector | spec.selector matches Pod labels |
| Endpoints | Created automatically when selector matches |
| No selector | Manual Endpoints or ExternalName |
| Check endpoints | kubectl get endpointslices -l kubernetes.io/service-name=<name> |
DNS names
| Format | Example |
|---|---|
| ClusterIP | <service>.<namespace>.svc.cluster.local |
| Headless | Returns Pod IPs directly |
| ExternalName | CNAME to externalName |
Best Practices
โ Do This:
# Use ClusterIP for internal services
type: ClusterIP # โ
# Always set targetPort explicitly
ports:
- port: 80
targetPort: 8080 # โ
# Verify endpoints after creating a Service
kubectl get endpointslices -l kubernetes.io/service-name=<name> # โ
# Use ExternalName for external DNS aliases
type: ExternalName
externalName: db.example.com # โ
โ Don’t Do This:
# Use LoadBalancer for an internal-only service
type: LoadBalancer # provisions a cloud LB unnecessarily # โ
# Mismatch selector and Pod labels
selector: { app: web } # Pods have app: nginx # โ
# Hardcode nodePort without checking for conflicts
nodePort: 30080 # may already be in use # โ
# Use a headless Service for a load-balanced front-end
clusterIP: None # no load balancing # โ
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Service has no endpoints | Selector does not match Pod labels | Fix the selector or the Pod labels |
External IP is <pending> | No cloud provider or load balancer controller | Use NodePort or install a load balancer |
| NodePort not reachable | Firewall blocks the port on the node | Open the node port in the firewall |
targetPort connection refused | Container not listening on that port | Check the container’s ports and the application |
| ExternalName does not resolve | CoreDNS not running or wrong externalName | Check CoreDNS and the CNAME value |
Real-World Examples
1. ClusterIP Service
spec:
type: ClusterIP
selector: { app: api }
ports: [{ port: 80, targetPort: 8080 }]
2. NodePort Service
spec:
type: NodePort
selector: { app: web }
ports: [{ port: 80, targetPort: 80, nodePort: 30080 }]
3. LoadBalancer Service
spec:
type: LoadBalancer
selector: { app: web }
ports: [{ port: 80, targetPort: 80 }]
4. ExternalName Service
spec:
type: ExternalName
externalName: db.example.com
5. Headless Service
spec:
clusterIP: None
selector: { app: db }
6. Check endpoints
kubectl get endpointslices -l kubernetes.io/service-name=web
7. Expose a Deployment imperatively
kubectl expose deployment web --type=ClusterIP --port=80 --target-port=80
8. Get Service details
kubectl describe service web-clusterip
9. DNS name
web-clusterip.default.svc.cluster.local
10. NodePort range
30000โ32767
Visual
The Service abstraction
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ SERVICE: web-clusterip (10.96.123.45) โ
โ โ
โ selector: app=web โ
โ โ โ
โ โโโ Pod 1 (10.244.1.5) โ
โ โโโ Pod 2 (10.244.2.3) โ
โ โโโ Pod 3 (10.244.3.7) โ
โ โ
โ Client calls web-clusterip:80 โ
โ kube-proxy routes to one of the Pods at port 80 โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Service type hierarchy
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ClusterIP โ
โ โโโ NodePort (adds node port) โ
โ โโโ LoadBalancer (adds cloud LB) โ
โ โ
โ ExternalName (separate, DNS alias) โ
โ Headless (ClusterIP with clusterIP: None) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Port mapping
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Client โโโบ Service:port (80) โโโบ Pod:targetPort (8080) โ
โ โ
โ port: the Service's port โ
โ targetPort: the container's port โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
NodePort access
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ External client โ
โ โ โ
โ โผ โ
โ NodeIP:30080 โโโบ Service โโโบ Pod:targetPort โ
โ โ
โ The same port is open on every node. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
| Item | Value |
|---|---|
| Default type | ClusterIP |
| Internal DNS | <service>.<namespace>.svc.cluster.local |
| Selector | Matches Pod labels |
port | Service port |
targetPort | Container port |
| NodePort range | 30000โ32767 |
| LoadBalancer | Cloud-provisioned external IP |
| ExternalName | CNAME to external DNS |
| Headless | clusterIP: None, returns Pod IPs |
| Endpoints check | kubectl get endpointslices -l kubernetes.io/service-name=<name> |
Key takeaways:
- A Service provides a stable virtual IP and DNS name for a set of Pods. The Pods are selected by label, and the Service’s ClusterIP is what clients use. The Pod IPs change; the Service IP does not .
- ClusterIP is the default and the foundation. It is reachable only from inside the cluster. NodePort and LoadBalancer are supersets that add external access .
- The selector must match the Pod labels. If
spec.selectordoes not match the Pods’metadata.labels, the Service has no endpoints and traffic fails.kubectl get endpointslicesshows whether the Service found its Pods . - NodePort opens a port on every node. External clients use
NodeIP:nodePort. The port is in the 30000โ32767 range. NodePort is useful when no cloud load balancer is available . - LoadBalancer provisions a cloud load balancer. The external IP appears in
status.loadBalancer.ingressafter the controller creates the load balancer. On a self-hosted cluster without a cloud provider, the IP remains<pending>. - ExternalName is a DNS alias, not a proxy. It maps the Service’s name to an external DNS name via a CNAME. No Pods are selected, and no traffic is proxied .
- A headless Service returns Pod IPs instead of a virtual IP. It is used for StatefulSets and for clients that need to connect to a specific Pod rather than a load-balanced Service .
Remember: A Service is the stable front door to a set of Pods. The selector determines which Pods are behind the door. The type determines who can reach the door: ClusterIP for internal clients, NodePort for node-level access, LoadBalancer for the internet, and ExternalName for a DNS alias. The port and targetPort determine how traffic is forwarded. When a Service is not working, check the endpoints first: if the selector does not match the Pod labels, there is nothing behind the door.
Stop using slow, ad-bloated tool sites! ๐คฎ
๐ Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
โ
Finance (Mortgage, Interest, Inflation)
โ
Tech (Base64, JSON, Dev Suite, IP)
โ
Health (BMI, BMR, TDEE)
โ
Productivity (Timer, Workspace, QR)
โก๏ธ Fast & Private
๐ No data leaves your device
๐ 100% Free
๐ Use it now: https://tools.kandz.me
๐ Bookmark itโyouโll need it later!