Kubernetes Service Tasks¶
- Hands-on Kubernetes exercises covering Services, Networking, and Service Discovery.
- Each task includes a description and a detailed solution with step-by-step instructions.
- Practice these tasks to master how Kubernetes exposes applications and manages traffic.
Table of Contents¶
- 01. Basic Service Exposure (ClusterIP)
- 02. NodePort & LoadBalancer
- 03. Service Discovery with DNS (FQDN)
- 04. Headless Services
- 05. ExternalName Service
- 06. Manual Endpoints
- 07. Session Affinity
- 08. Multi-Port Service
01. Basic Service Exposure (ClusterIP)¶
Run an nginx pod and expose it via a Service (ClusterIP) to access it from within the cluster.
Scenario:¶
◦ You have an application running in a pod, but it needs to be accessible by other pods. ◦ ClusterIP is the default service type, providing internal stable IP.
Hint: kubectl expose, kubectl get services, kubectl port-forward
Solution
# 1. Run an nginx pod
kubectl run nginx-web --image=nginx:alpine --port=80
# 2. Expose the pod as a Service (ClusterIP by default)
kubectl expose pod nginx-web --name=nginx-svc --port=80 --target-port=80
# 3. Verify the service
kubectl get svc
# 4. Access it (using port-forward for local access)
kubectl port-forward svc/nginx-svc 8080:80
# (Open localhost:8080 in browser)
# Cleanup
kubectl delete pod nginx-web
kubectl delete svc nginx-svc
02. NodePort & LoadBalancer¶
Expose a deployment using NodePort to access it via the node’s IP, and then switch it to LoadBalancer (simulated or real).
Scenario:¶
◦ You need to make your application accessible from outside the Kubernetes cluster. ◦ NodePort opens a specific port on all nodes, while LoadBalancer provisions an external IP (cloud provider dependent).
Hint: type: NodePort, type: LoadBalancer
Solution
# 1. Create a deployment
kubectl create deployment web-server --image=nginx:alpine --replicas=2
# 2. Expose as NodePort
kubectl expose deployment web-server --type=NodePort --name=web-nodeport --port=80
# 3. Get the allocated NodePort (e.g., 30xxx)
kubectl get svc web-nodeport
# 4. (Optional) Patch it to be a LoadBalancer
kubectl patch svc web-nodeport -p '{"spec": {"type": "LoadBalancer"}}'
# 5. Verify external IP (it might stay <pending> on Minikube/Kind without addons)
kubectl get svc web-nodeport
# Cleanup
kubectl delete deployment web-server
kubectl delete svc web-nodeport
03. Service Discovery with DNS (FQDN)¶
Create two pods in different namespaces and verify they can communicate using the Fully Qualified Domain Name (FQDN).
Scenario:¶
◦ Microservices often live in different namespaces (e.g., frontend vs backend). ◦ You need to ensure they can talk to each other using Kubernetes internal DNS.
Hint: nslookup, <service>.<namespace>.svc.cluster.local
Solution
# 1. Create two namespaces
kubectl create ns app-a
kubectl create ns app-b
# 2. Run a target pod and service in app-b
kubectl run backend --image=nginx:alpine -n app-b
kubectl expose pod backend --name=backend-svc --port=80 -n app-b
# 3. Run a client pod in app-a
kubectl run client --image=busybox -n app-a -- sleep 3600
# 4. Test DNS resolution from client to backend
# FQDN format: service-name.namespace.svc.cluster.local
kubectl exec -it client -n app-a -- nslookup backend-svc.app-b.svc.cluster.local
# 5. Test connectivity
kubectl exec -it client -n app-a -- wget -O- backend-svc.app-b.svc.cluster.local
# Cleanup
kubectl delete ns app-a app-b
04. Headless Services¶
Create a Headless Service (ClusterIP: None) and verify that DNS returns the IPs of the individual pods instead of a single Service IP.
Scenario:¶
◦ You are deploying a distributed stateful application (like Cassandra, MongoDB, or Kafka) that needs to discover all peer nodes directly. ◦ Headless services allow direct pod-to-pod communication without load balancing.
Hint: clusterIP: None
Solution
# 1. Create a Headless Service
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: headless-svc
spec:
clusterIP: None
selector:
app: headless-app
ports:
- port: 80
EOF
# 2. Create pods matching the selector
kubectl run pod-1 --image=nginx:alpine --labels=app=headless-app
kubectl run pod-2 --image=nginx:alpine --labels=app=headless-app
# 3. Verify DNS resolution (should return multiple IPs)
kubectl run dns-test --image=busybox --restart=Never -- nslookup headless-svc
# 4. Check logs to see the IPs
kubectl logs dns-test
# Cleanup
kubectl delete pod pod-1 pod-2 dns-test
kubectl delete svc headless-svc
05. ExternalName Service¶
Create a Service that maps to an external DNS name (e.g., google.com) instead of a pod selector.
Scenario:¶
◦ You want to refer to an external database or API (e.g., AWS RDS, external API) using a local Kubernetes service name. ◦ This allows you to change the external endpoint later without changing your application code.
Hint: type: ExternalName, externalName: example.com
Solution
# 1. Create ExternalName service
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: my-external-service
spec:
type: ExternalName
externalName: google.com
EOF
# 2. Test resolution (it should be a CNAME to google.com)
kubectl run ext-test --image=busybox --restart=Never -- nslookup my-external-service
# 3. Check logs
kubectl logs ext-test
# Cleanup
kubectl delete svc my-external-service
kubectl delete pod ext-test
06. Manual Endpoints¶
Create a Service without a selector, and manually create an Endpoints object to point to an external IP (or a specific pod IP).
Scenario:¶
◦ You want to use a Kubernetes Service to point to a specific IP address that isn’t managed by a Kubernetes Pod selector (e.g., a legacy server or a database outside the cluster).
Hint: kind: Endpoints, same name as Service
Solution
# 1. Create a Service without a selector
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: manual-svc
spec:
ports:
- port: 80
targetPort: 80
EOF
# 2. Create Endpoints manually (Use an IP you know, e.g., 1.1.1.1 or a pod IP)
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Endpoints
metadata:
name: manual-svc
subsets:
- addresses:
- ip: 1.1.1.1
ports:
- port: 80
EOF
# 3. Describe service to see endpoints
kubectl describe svc manual-svc
# Cleanup
kubectl delete svc manual-svc
kubectl delete endpoints manual-svc
07. Session Affinity¶
Create a Service with sessionAffinity: ClientIP and verify that requests from the same client pod go to the same backend pod (if possible to observe).
Scenario:¶
◦ Your application stores session state locally in the container (not recommended, but happens). ◦ You need to ensure a user always hits the same pod during their session.
Hint: sessionAffinity: ClientIP
Solution
# 1. Create a deployment with 3 replicas
kubectl create deployment session-app --image=nginx:alpine --replicas=3
# 2. Expose with ClientIP affinity
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: session-svc
spec:
selector:
app: session-app
ports:
- port: 80
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800
EOF
# 3. Verify affinity setting
kubectl describe svc session-svc
# Cleanup
kubectl delete deployment session-app
kubectl delete svc session-svc
08. Multi-Port Service¶
Create a Service that exposes both port 80 (HTTP) and 443 (HTTPS) for the same set of pods.
Scenario:¶
◦ Your application serves both HTTP and HTTPS traffic. ◦ You need a single Service to handle both ports.
Hint: ports array in Service spec
Solution
# 1. Create a pod that exposes port 80
kubectl run web-multi --image=nginx:alpine --port=80
# 2. Create a multi-port service
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: multi-port-svc
spec:
selector:
run: web-multi
ports:
- name: http
port: 80
targetPort: 80
- name: https
port: 443
targetPort: 80 # Mapping 443 to 80 just for demo since nginx listens on 80
EOF
# 3. Verify ports
kubectl get svc multi-port-svc
# Cleanup
kubectl delete pod web-multi
kubectl delete svc multi-port-svc