Skip to content

Kubernetes Services – Complete Guide

Services provide stable networking for Pods. This covers all types for CKA.


1. Why Services?

  • Pods are ephemeral (IPs change on restart)
  • Services provide stable DNS name and IP
  • Load balancing across multiple Pods
  • Service discovery within cluster

2. Service Types

TypeScopeUse Case
ClusterIPInternal onlyDefault, pod-to-pod communication
NodePortExternal via node IPDev/testing, direct node access
LoadBalancerExternal via cloud LBProduction external traffic
ExternalNameDNS aliasAccess external services
HeadlessNo ClusterIPStatefulSets, direct pod access

3. ClusterIP (Default)

Internal-only service. Only accessible within cluster.

yaml
apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: ClusterIP  # optional, default
  selector:
    app: myapp
  ports:
  - port: 80        # Service port
    targetPort: 8080  # Pod port

Access: curl my-service.default.svc.cluster.local:80


4. NodePort

Exposes service on each node's IP at a static port (30000-32767).

yaml
apiVersion: v1
kind: Service
metadata:
  name: my-nodeport
spec:
  type: NodePort
  selector:
    app: myapp
  ports:
  - port: 80
    targetPort: 8080
    nodePort: 30080  # optional, auto-assigned if omitted

Access: curl <node-ip>:30080

Port mapping:

External → NodePort (30080) → Service Port (80) → Pod Port (8080)

5. LoadBalancer

Creates external load balancer (cloud providers only).

yaml
apiVersion: v1
kind: Service
metadata:
  name: my-lb
spec:
  type: LoadBalancer
  selector:
    app: myapp
  ports:
  - port: 80
    targetPort: 8080

On-prem: Use MetalLB or stays in Pending state


6. ExternalName

Maps service to external DNS name (no proxying).

yaml
apiVersion: v1
kind: Service
metadata:
  name: external-db
spec:
  type: ExternalName
  externalName: db.example.com

Access: curl external-db → resolves to db.example.com


7. Headless Service

No ClusterIP assigned. Returns Pod IPs directly.

yaml
apiVersion: v1
kind: Service
metadata:
  name: headless-svc
spec:
  clusterIP: None  # This makes it headless
  selector:
    app: myapp
  ports:
  - port: 80

Use cases:

  • StatefulSets (stable network identity)
  • Direct pod-to-pod communication
  • Client-side load balancing

DNS returns: All Pod IPs instead of single ClusterIP


8. Service Selectors

Services route to Pods matching the selector labels.

Service:

yaml
spec:
  selector:
    app: web
    tier: frontend

Matching Pod:

yaml
metadata:
  labels:
    app: web
    tier: frontend
    version: v1  # Extra labels OK

Pod must have ALL labels in selector to receive traffic.


9. Ports Explained

yaml
ports:
- name: http        # Optional, required if multiple ports
  port: 80          # Service port (what clients use)
  targetPort: 8080  # Pod port (where app listens)
  nodePort: 30080   # Node port (NodePort type only)
  protocol: TCP     # TCP (default) or UDP

targetPort can be name:

yaml
# In Service
targetPort: http-port

# In Pod
ports:
- name: http-port
  containerPort: 8080

10. Multi-Port Services

yaml
apiVersion: v1
kind: Service
metadata:
  name: multi-port
spec:
  selector:
    app: myapp
  ports:
  - name: http
    port: 80
    targetPort: 8080
  - name: https
    port: 443
    targetPort: 8443
  - name: metrics
    port: 9090
    targetPort: 9090

11. Service Without Selector

Manually control endpoints (external service integration).

Service:

yaml
apiVersion: v1
kind: Service
metadata:
  name: external-svc
spec:
  ports:
  - port: 80
# No selector!

Endpoints:

yaml
apiVersion: v1
kind: Endpoints
metadata:
  name: external-svc  # Must match service name
subsets:
- addresses:
  - ip: 192.168.1.100
  - ip: 192.168.1.101
  ports:
  - port: 80

12. DNS Names

TypeDNS Format
Service<service>.<namespace>.svc.cluster.local
Pod (headless)<pod-name>.<service>.<namespace>.svc.cluster.local
Short form<service> (same namespace)
Cross-namespace<service>.<namespace>

Examples:

bash
curl my-service                    # Same namespace
curl my-service.default            # Explicit namespace  
curl my-service.default.svc.cluster.local  # Full FQDN

13. Session Affinity

Stick client to same Pod.

yaml
spec:
  sessionAffinity: ClientIP
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 10800  # 3 hours default

14. Service CIDR

Services get IPs from service CIDR (default: 10.96.0.0/12).

Check: kubectl cluster-info dump | grep service-cluster-ip-range


15. Common Commands

bash
# Create service imperatively
kubectl expose deployment nginx --port=80 --target-port=8080 --type=NodePort

# List services
kubectl get svc

# Describe service (shows endpoints)
kubectl describe svc my-service

# Get endpoints
kubectl get endpoints my-service

# Test service from within cluster
kubectl run test --rm -it --image=busybox -- wget -qO- my-service:80

# Get service YAML
kubectl get svc my-service -o yaml

16. Imperative Commands

bash
# ClusterIP
kubectl expose deployment nginx --port=80

# NodePort
kubectl expose deployment nginx --port=80 --type=NodePort

# With specific node port
kubectl expose deployment nginx --port=80 --type=NodePort --overrides='{"spec":{"ports":[{"port":80,"nodePort":30080}]}}'

# LoadBalancer
kubectl expose deployment nginx --port=80 --type=LoadBalancer

# From pod
kubectl expose pod nginx --port=80 --name=nginx-svc

17. Troubleshooting

Service has no endpoints

bash
kubectl get endpoints <service>

Causes:

  • Selector doesn't match any pods
  • Pods not ready (failing readiness probe)
  • Pods in wrong namespace

Can't access service

bash
# Check if service exists
kubectl get svc

# Check endpoints
kubectl describe svc <name>

# Test from inside cluster
kubectl run test --rm -it --image=busybox -- wget -qO- <service>:<port>

# Check pod logs
kubectl logs <pod>

18. CKA Exam Tips

  1. Default type is ClusterIP – don't need to specify
  2. kubectl expose is fastest for creating services
  3. NodePort range: 30000-32767
  4. Headless: set clusterIP: None
  5. Check endpoints when service doesn't route traffic
  6. Selectors must match Pod labels exactly

Appendix: Niche / Edge-case Scenarios

Session Affinity (moved from questions)

Scenario: The marketing team needs an internal web application that maintains session state based on the client's IP address.

Notes & Example: Session Affinity (ClientIP) sticks a client to the same pod.

yaml
spec:
  sessionAffinity: ClientIP
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 10800

Use this sparingly in the exam — it's an edge case: prefer stateless apps.


Cross-Namespace DNS Discovery (moved from questions)

Scenario: A secure-vault service runs in a separate namespace (hiddensecrets) and must be reachable from default.

Notes & Example: Use FQDN to access services across namespaces: <service>.<namespace>.svc.cluster.local.

From a pod in default you can curl secure-vault.hiddensecrets.svc.cluster.local:80 to reach the service.

These cases are important conceptually but are niche; focus your practice on selectors, ports, and endpoints for CKA.


NodePort Traffic Policy (moved from questions)

Scenario: Exposing a front-end application externally using a NodePort while preserving the source IP is an advanced traffic-policy case.

Notes & Example: To preserve client source IP you can use externalTrafficPolicy: Local. This is an advanced option — good to know, but rarely required for core CKA tasks.

yaml
spec:
  type: NodePort
  externalTrafficPolicy: Local
  ports:
  - port: 80
    nodePort: 31050

Headless Service for StatefulSets (moved from questions)

Scenario: Stateful applications that require stable network identity and direct pod-to-pod DNS resolution.

Notes & Example: A headless Service is created by setting clusterIP: None, which makes DNS return individual Pod A records.

yaml
spec:
  clusterIP: None
  selector:
    app: db
  ports:
  - port: 5432

Verify by doing nslookup <service> inside the cluster: you should see multiple A records.


ExternalName Service (moved from questions)

Scenario: Map an in-cluster service name to an external DNS name without proxying traffic.

Notes & Example: An ExternalName Service points to an external DNS name.

yaml
apiVersion: v1
kind: Service
metadata:
  name: access-google
spec:
  type: ExternalName
  externalName: google.com

Test resolution from a pod to confirm access-google.<namespace>.svc.cluster.local resolves to google.com.


Released under the MIT License.