Kubernetes Services

The networking abstraction that provides stable, discoverable endpoints for ephemeral Pods. Services are the bridge between dynamic workloads and reliable client access — a core topic in the CKA “Services & Networking” domain (~20%). Synthesized from CKA Day 9 — Kubernetes Services Explained. Once Services open communication paths, NetworkPolicies are used to restrict them.

The Problem: Pods Are Ephemeral

Every Pod receives a unique internal IP address when it starts. But when a Pod restarts, crashes, or is rescheduled, it gets a new IP. If a front-end Pod tries to reach a back-end Pod at 10.244.1.2, that address becomes invalid the moment the back-end Pod restarts.

Services solve this by:

  • Providing a stable virtual IP (ClusterIP) that never changes
  • Maintaining an Endpoints list that automatically tracks which Pods are healthy backends
  • Offering DNS resolution so clients use names (my-service) instead of IPs
  • Enabling load balancing across multiple Pod replicas

Service YAML Anatomy

apiVersion: v1
kind: Service
metadata:
  name: my-service
  labels:
    env: demo
spec:
  type: ClusterIP        # NodePort | LoadBalancer | ExternalName
  selector:
    app: nginx           # Must match Pod labels
  ports:
    - port: 80           # Service port (cluster-internal)
      targetPort: 80     # Pod container port
      nodePort: 30001    # Only for NodePort/LoadBalancer (30000-32767)
FieldDescriptionRequired
apiVersionv1 for ServiceYes
kindService (case-sensitive)Yes
metadata.nameUnique name in namespaceYes
spec.typeClusterIP (default), NodePort, LoadBalancer, ExternalNameNo
spec.selectorLabel key-value pairs to match target PodsYes
spec.ports[].portPort exposed by the Service inside the clusterYes
spec.ports[].targetPortPort the container listens onNo (defaults to port)
spec.ports[].nodePortStatic external port (30,000–32,767)No (auto-allocated)

YAML Tip: selector uses plain key-value pairs, not matchLabels. This is a common mistake — Services use selector: {app: nginx}, while Deployments/ReplicaSets use selector: matchLabels: {app: nginx}. Source: CKA Day 9

The Three Port Concepts

Understanding the distinction between these three ports is critical for both the exam and real-world debugging:

PortRoleAudience
targetPortThe actual port the application container is listening onThe Service forwards traffic here
portThe port exposed by the Service within the clusterOther Pods and Services in the cluster
nodePortA static port opened on every node’s IP (30,000–32,767)External users and clients outside the cluster

Example: A Nginx container listens on targetPort: 80. The Service exposes port: 80 for internal clients. If type: NodePort, it also opens nodePort: 30001 on each node’s IP. External traffic hits NodeIP:30001 → Service forwards to Pod at targetPort: 80.

How Services Route Traffic

  1. The Service controller watches for Pods matching the selector
  2. Healthy matching Pods are added to the Endpoints object
  3. kube-proxy on each node programs iptables (or ipvs) rules to route traffic destined for the Service IP to one of the Endpoint IPs
  4. The selection algorithm is typically round-robin (with iptables mode using random distribution)
  5. Readiness probes on each backend Pod determine whether its IP remains in the Endpoints list; failing readiness removes the Pod from traffic routing without killing it
User/Client
    │
    ▼
┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│  NodePort   │────▶│   Service   │────▶│   Pod 1     │
│  30001      │     │   ClusterIP │     │ 10.244.1.2  │
└─────────────┘     └─────────────┘     ├─────────────┤
                                        │   Pod 2     │
                                        │ 10.244.1.3  │
                                        ├─────────────┤
                                        │   Pod 3     │
                                        │ 10.244.2.5  │
                                        └─────────────┘

Endpoints and EndpointSlices

Endpoints are the dynamic backend list maintained by Kubernetes:

kubectl get endpoints        # List all endpoints
kubectl get ep               # Short form
kubectl describe svc <name>  # View endpoints attached to a service

When a Pod is created, restarted, or deleted, its IP is automatically added or removed from the Endpoints object. This is why Services remain stable even as Pods churn — the virtual IP is constant, but the backend IPs are continuously refreshed.

In large clusters, Kubernetes uses EndpointSlices (introduced in v1.21, default in v1.22+) to split endpoints into smaller, more scalable chunks instead of one large Endpoints object.

Essential kubectl Commands

CommandPurpose
kubectl get svcList all Services
kubectl get serviceSame as above
kubectl describe svc <name>Detailed Service info including Endpoints
kubectl get endpointsShow backend Pod IPs for all Services
kubectl apply -f service.yamlCreate/Update from manifest
kubectl delete svc <name>Delete a Service
kubectl expose deployment <name> --port=80Imperative Service creation

Imperative Service Creation (CKA Speed Pattern)

Writing full YAML under exam pressure is slow. Use imperative commands:

# ClusterIP (default)
kubectl expose deployment nginx-deploy --port=80
 
# NodePort with specific port
kubectl expose deployment nginx-deploy --type=NodePort --port=80 --target-port=80 --node-port=30001
 
# LoadBalancer
kubectl expose deployment nginx-deploy --type=LoadBalancer --port=80

CKA Tip: Imperative commands are ~3x faster than writing YAML from scratch. Practice them for the exam. Source: CKA Day 9

Troubleshooting Services

SymptomLikely CauseFix
Service shows no EndpointsSelector labels don’t match Pod labelsCheck kubectl get pods --show-labels and align selectors
NodePort not accessible from hostKind cluster missing extraPortMappingsAdd port mapping in kind-config.yaml and recreate cluster
LoadBalancer pending external IPNo cloud provider integrationUse NodePort locally; on cloud, check CCM
Connection refused on targetPortApplication not listening on that portVerify container’s containerPort matches targetPort
Service name not resolvingCoreDNS issues or wrong namespaceUse FQDN: <service>.<namespace>.svc.cluster.local

Sources

Practical Practice

Exam-style hands-on tasks for this topic. Complete each task before reviewing the solution. Time yourself — CKA tasks average 5–7 minutes.

Task 1: Create a ClusterIP Service You are asked to create a Service named web-svc of type ClusterIP that targets Pods with label app=web on port 80. Requirements: Use imperative generation; do not write full YAML from scratch. Verification: kubectl get svc web-svc && kubectl get endpoints web-svc Solution:

kubectl create service clusterip web-svc --tcp=80:80 --dry-run=client -o yaml > web-svc.yaml
# Edit web-svc.yaml to add `selector: { app: web }` under spec, then apply
kubectl apply -f web-svc.yaml

Task 2: Expose a Deployment as NodePort You are asked to expose the existing Deployment frontend as a NodePort Service accessible on static port 30080. Requirements: NodePort must be exactly 30080; targetPort is 80. Verification: kubectl get svc frontend Solution:

kubectl expose deployment frontend --type=NodePort --port=80 --target-port=80 --node-port=30080 --dry-run=client -o yaml | kubectl apply -f -

Task 3: Debug Empty Endpoints A Service api-svc exists but kubectl get endpoints api-svc shows no backends. The backend Pods are running. Requirements: Identify the root cause and fix it without deleting the Service. Verification: kubectl get endpoints api-svc should list Pod IPs. Solution:

# 1. Compare labels
kubectl get pods --show-labels
kubectl get svc api-svc -o yaml | grep selector -A 2
# 2. If selector mismatch, patch the Service
kubectl patch svc api-svc --type='json' -p='[{"op": "replace", "path": "/spec/selector", "value":{"app":"api"}}]'

Task 4: Create a Headless Service for a StatefulSet You are asked to create a Headless Service named db-headless for a StatefulSet with Pods labeled app=db. Requirements: ClusterIP must be None; port is 3306. Verification: kubectl get svc db-headless shows CLUSTER-IP as None. Solution:

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
  name: db-headless
spec:
  type: ClusterIP
  clusterIP: None
  selector:
    app: db
  ports:
  - port: 3306
    targetPort: 3306
EOF

Tags: kubernetes services clusterip nodeport loadbalancer externalname networking cka devops endpoints kube-proxy