Qingular

Service Types

·CKAk8sPractice

CKA Domain 3 — Kubernetes Service types in detail (ClusterIP, NodePort, LoadBalancer, Headless, ExternalName)

← Back to CKA Practice Index

Overview

Service is an abstraction layer in Kubernetes used to expose network access to Pods. It provides a stable access endpoint (DNS name or IP address) for a dynamically changing set of Pods.

1. ClusterIP (Default Type)

Accessible only within the cluster; this is the default Service type.

apiVersion: v1
kind: Service
metadata:
  name: my-clusterip-svc
spec:
  type: ClusterIP
  selector:
    app: my-app
  ports:
    - port: 80
      targetPort: 8080
# Create a ClusterIP Service
kubectl create service clusterip my-svc --tcp=80:8080

# Or via the expose command
kubectl expose deployment my-deploy --port=80 --target-port=8080 --name=my-svc

2. NodePort

Opens a static port (30000-32767) on every node to forward traffic to the Service.

apiVersion: v1
kind: Service
metadata:
  name: my-nodeport-svc
spec:
  type: NodePort
  selector:
    app: my-app
  ports:
    - port: 80
      targetPort: 8080
      nodePort: 30080  # Optional; randomly assigned if not specified
# Create a NodePort Service
kubectl create service nodeport my-svc --tcp=80:8080 --node-port=30080

# Or specify the type when using expose
kubectl expose deployment my-deploy --type=NodePort --port=80 --target-port=8080 --name=my-nodeport-svc

Access method: <NodeIP>:<NodePort>

3. LoadBalancer

Builds on NodePort and automatically creates a cloud load balancer (only effective in cloud environments).

apiVersion: v1
kind: Service
metadata:
  name: my-lb-svc
spec:
  type: LoadBalancer
  selector:
    app: my-app
  ports:
    - port: 80
      targetPort: 8080
# Create a LoadBalancer Service
kubectl create service loadbalancer my-svc --tcp=80:8080
# Or via expose
kubectl expose deployment my-deploy --type=LoadBalancer --port=80 --target-port=8080

View External IP (usually assigned after a few minutes in cloud environments):

kubectl get svc my-lb-svc
# Note the EXTERNAL-IP column

4. Headless Service

Set clusterIP: None -- no ClusterIP is assigned; DNS queries return the IP addresses of all backend Pods instead. Commonly used with StatefulSets.

apiVersion: v1
kind: Service
metadata:
  name: my-headless-svc
spec:
  clusterIP: None
  selector:
    app: my-app
  ports:
    - port: 80
      targetPort: 8080

Used with StatefulSet:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "my-headless-svc"
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx

Pod DNS format: <pod-name>.<service-name>.<namespace>.svc.cluster.local

# Verify Headless Service DNS resolution
kubectl run dns-test --image=busybox:1.28 --rm -it --restart=Never -- nslookup my-headless-svc

5. ExternalName

Maps a Service to an external domain name via DNS CNAME. No Selector and no port definitions.

apiVersion: v1
kind: Service
metadata:
  name: my-external-svc
spec:
  type: ExternalName
  externalName: api.example.com
# Verify ExternalName resolution
kubectl run dns-test --image=busybox:1.28 --rm -it --restart=Never -- nslookup my-external-svc

6. Endpoints and EndpointSlice

When a Service matches Pods via Selector, an Endpoints resource is automatically created.

# View Endpoints
kubectl get endpoints my-svc
kubectl describe endpoints my-svc

# View EndpointSlice (K8s v1.21+)
kubectl get endpointslices

Manually create Endpoints pointing to an external service example (Service without Selector):

apiVersion: v1
kind: Service
metadata:
  name: external-db
spec:
  ports:
    - port: 3306
---
apiVersion: v1
kind: Endpoints
metadata:
  name: external-db
subsets:
  - addresses:
      - ip: 192.168.1.100
    ports:
      - port: 3306

Common Command Summary

# Create Service
kubectl create service clusterip my-svc --tcp=80:8080
kubectl create service nodeport my-svc --tcp=80:8080
kubectl create service loadbalancer my-svc --tcp=80:8080

# expose command (most frequently used)
kubectl expose deployment my-deploy --type=ClusterIP --port=80 --target-port=8080
kubectl expose pod my-pod --port=80 --target-port=8080 --name=my-svc

# View Services
kubectl get svc
kubectl get svc -o wide
kubectl describe svc my-svc

# Port forwarding (for temporary debugging)
kubectl port-forward svc/my-svc 8080:80
kubectl port-forward pod/my-pod 8080:80

🧪 Complete Hands-on Example: Exposing an Application via Different Service Types

Scenario

Create an Nginx Deployment and expose it via ClusterIP, NodePort, and Headless Service, verifying the access method for each type.

Prerequisites

  • Cluster is running normally with at least one available node
  • kubectl is configured with cluster access

Steps

Step 1: Create a Deployment

kubectl create deployment nginx --image=nginx:1.25 --replicas=2
# Expected output: deployment.apps/nginx created

kubectl get pods -l app=nginx -o wide
# Expected output: two Pods in Running state, each with an IP

Step 2: Create a ClusterIP Service

kubectl expose deployment nginx --name=nginx-clusterip --port=80 --target-port=80 --type=ClusterIP
# Expected output: service/nginx-clusterip exposed

kubectl get svc nginx-clusterip
# NAME               TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
# nginx-clusterip    ClusterIP   10.96.123.45    <none>        80/TCP    5s

Step 3: Test ClusterIP from Inside the Cluster

# Start a temporary test Pod to access the ClusterIP
kubectl run test-pod --image=busybox:1.28 --rm -it --restart=Never -- wget -qO- http://nginx-clusterip
# Expected output: nginx welcome page HTML (<!DOCTYPE html>...)

# Or use nslookup to verify DNS resolution
kubectl run dns-test --image=busybox:1.28 --rm -it --restart=Never -- nslookup nginx-clusterip
# Expected output: Server:    10.96.0.10
#           Name:      nginx-clusterip.default.svc.cluster.local
#           Address:   10.96.123.45

Step 4: Change the Service to NodePort

# Delete the original Service and recreate as NodePort type
kubectl delete svc nginx-clusterip

kubectl expose deployment nginx --name=nginx-nodeport --port=80 --target-port=80 --type=NodePort
# Expected output: service/nginx-nodeport exposed

kubectl get svc nginx-nodeport
# NAME             TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
# nginx-nodeport   NodePort   10.96.67.89     <none>        80:30080/TCP   5s

# Get node IPs (can access from any node)
kubectl get nodes -o wide
# Note the Internal-IP or External-IP of any node, e.g., 192.168.1.10

Step 5: Test NodePort from Outside the Cluster

# Access from outside the cluster or from a node
curl http://192.168.1.10:30080
# Expected output: nginx welcome page HTML

# If accessing a minikube cluster from the local machine
minikube service nginx-nodeport --url
# Expected output: http://127.0.0.1:xxxxx

Step 6: Create a Headless Service

# Delete the previous NodePort Service
kubectl delete svc nginx-nodeport

# Create a Headless Service (via YAML, since kubectl expose cannot directly set clusterIP: None)
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
  name: nginx-headless
spec:
  clusterIP: None
  selector:
    app: nginx
  ports:
    - port: 80
      targetPort: 80
EOF
# Expected output: service/nginx-headless created

kubectl get svc nginx-headless
# NAME              TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
# nginx-headless    ClusterIP   None         <none>        80/TCP    5s

Step 7: Test Headless Service DNS Resolution

# Verify that DNS returns all backend Pod IPs
kubectl run dns-test --image=busybox:1.28 --rm -it --restart=Never -- nslookup nginx-headless
# Expected output:
# Server:    10.96.0.10
# Address:   10.96.0.10:53
# Name:      nginx-headless.default.svc.cluster.local
# Address:   10.244.1.5    # Pod-1 IP
# Name:      nginx-headless.default.svc.cluster.local
# Address:   10.244.2.7    # Pod-2 IP

# Use dig for detailed records (requires the netshoot image)
kubectl run dig-pod --image=nicolaka/netshoot --rm -it --restart=Never -- dig nginx-headless A +short
# Expected output:
# 10.244.1.5
# 10.244.2.7

Verification

# Compare the three Service types
kubectl get svc
# NAME              TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
# kubernetes        ClusterIP   10.96.0.1       <none>        443/TCP        1h
# nginx-headless    ClusterIP   None            <none>        80/TCP         5m

# Check Endpoints to confirm backend Pods
kubectl get endpoints nginx-headless
# NAME              ENDPOINTS                       AGE
# nginx-headless    10.244.1.5:80,10.244.2.7:80     5m

# Clean up resources
kubectl delete deployment nginx
kubectl delete svc nginx-headless

Exam Tips