Service Types
CKA Domain 3 — Kubernetes Service types in detail (ClusterIP, NodePort, LoadBalancer, Headless, ExternalName)
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
-
ClusterIP is the default type; if
--typeis not specified,kubectl exposedefaults to ClusterIP -
NodePort port range is fixed at 30000-32767; if
--node-portis not specified, a random port is assigned -
Headless Service is commonly used with StatefulSets; DNS returns all Pod IPs rather than a virtual IP
-
CKA key commands:
kubectl expose deployment,kubectl create service, andkubectl run ... -- nslookupare high-frequency exam topics -
If the DNS test Pod fails to start, check whether
busybox:1.28is available; version1.28is recommended as newer versions may lack the nslookup command -
https://kubernetes.io/docs/concepts/services-networking/service/
-
https://kubernetes.io/docs/tasks/debug/debug-application/debug-service/
-
https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/
-
https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/