Intro
Kubernetes Services are fundamental to connecting applications running in pods. They provide stable network identities, load balancing, and service discovery, but their behavior can be subtle, especially when you are troubleshooting DNS, endpoint selection, or network policies. Setting up a dedicated local lab lets you experiment safely, break things deliberately, and build muscle memory for production troubleshooting. This guide walks through a repeatable process for creating a minimal Kubernetes environment on your laptop, deploying example workloads, and exploring Service behavior with concrete commands and expected outputs. By the end, you will have a lab that you can tear down and recreate in minutes, and a set of exercises that reinforce how Services work under the hood.
A clear Kubernetes Service setup reduces rework by separating research, brief, article generation, evaluation, approval, and publishing. In this context, that means separating the learning stages: environment provisioning, workload deployment, Service creation, and verification. For Kubernetes Service local lab, the first useful pilot should be narrow, measurable, and easy to inspect locally before deployment. We will follow that principle by starting with a single-node cluster and a simple application.
Version and Environment Inventory
Before creating any resources, document the versions and topology. This avoids confusion when commands behave differently across Kubernetes releases. The lab described here uses the following components:
| Component | Version | Purpose |
|---|---|---|
| Kubernetes | 1.28 (or latest stable) | Core orchestration |
| kubectl | 1.28 | CLI client |
| minikube | 1.32 | Local single-node cluster |
| Container runtime | Docker 24.x or containerd | Runs pods |
| Example app | nginx:1.25 | Simple web server |
These are constructed example versions and can be adjusted. The topology is a single-node cluster with one control plane and no worker nodes (minikube default). This is sufficient for Service experiments because all pods can be scheduled on the same node.
Prerequisites
- A machine with at least 2 CPUs, 4GB RAM, and 20GB free disk space.
- A hypervisor such as VirtualBox, Hyper-V, or KVM, or a container runtime like Docker.
- kubectl installed and in your PATH. Verify with
kubectl version --client. - minikube installed. Verify with
minikube version.
Install minikube and kubectl using your package manager or from the official documentation. For this guide, we assume both are installed.
Safe Configuration Path
We will proceed in small, reversible steps. Each step is independent and can be rolled back by deleting the created resource.
Step 1: Start the Cluster
Start minikube with a specific Kubernetes version to match your documentation. This creates a fresh cluster:
minikube start --kubernetes-version=v1.28.0
Expected output includes lines like:
* minikube v1.32.0 on Ubuntu 22.04
* Using the docker driver based on existing profile
* Starting control plane node minikube in cluster minikube
* Pulling base image ...
* Creating docker container (CPUs=2, Memory=4000MB) ...
* Preparing Kubernetes v1.28.0 on Docker 24.0.7 ...
* Verifying Kubernetes components...
- Using image gcr.io/k8s-minikube/storage-provisioner: v5
* Enabled addons: storage-provisioner, default-storageclass
* kubectl not found. If you need it, try: 'minikube kubectl -- get pods -A'
* Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default
Verify the cluster is accessible:
kubectl cluster-info
Expected output:
Kubernetes control plane is running at https://192.168.49.2:8443
CoreDNS is running at https://192.168.49.2:8443/api/v1/namespaces/kube-system/services/kube-dns: dns/proxy
Step 2: Create a Namespace
Use a dedicated namespace to isolate your lab resources and simplify cleanup.
kubectl create namespace service-lab
Expected output: namespace/service-lab created
Step 3: Deploy a Backend Application
Create a Deployment with two nginx replicas using an imperative command. The pods will serve as the backend for our Service.
kubectl create deployment nginx-deployment --image=nginx:1.25 --replicas=2 --namespace=service-lab
Expected output: deployment.apps/nginx-deployment created
Check pod status:
kubectl get pods -n service-lab -o wide
Expected output (names and IPs will vary):
NAME READY STATUS RESTARTS AGE IP NODE
nginx-deployment-7c79c4bf97-9x6k2 1/1 Running 0 10s 10.244.0.5 minikube
nginx-deployment-7c79c4bf97-j2p8t 1/1 Running 0 10s 10.244.0.6 minikube
Step 4: Create a ClusterIP Service
Expose the Deployment as a ClusterIP Service using kubectl expose. This is the default Service type and provides internal load balancing. The Service automatically selects the pods based on the Deployment's labels.
kubectl expose deployment nginx-deployment --name=nginx-service --namespace=service-lab --type=ClusterIP --port=80 --target-port=80
Expected output: service/nginx-service exposed
Inspect the Service:
kubectl get service nginx-service -n service-lab -o wide
Expected output:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
nginx-service ClusterIP 10.96.100.200 <none> 80/TCP 5s app=nginx-deployment
Note the CLUSTER-IP is an example; your cluster will assign a different one.
Step 5: Create a NodePort Service (Optional)
To access the Service from outside the cluster, expose the Deployment as a NodePort Service. Kubernetes will assign a random port in the NodePort range. If you need a specific port, you can use kubectl create service nodeport with additional configuration, but for this lab, a random port is sufficient.
kubectl expose deployment nginx-deployment --name=nginx-nodeport --namespace=service-lab --type=NodePort --port=80 --target-port=80
Get the service:
kubectl get service nginx-nodeport -n service-lab
Expected output (the node port is dynamically assigned):
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-nodeport NodePort 10.96.150.77 <none> 80:3xxxx/TCP 10s
You can access the nginx welcome page by opening http://<minikube-ip>:<nodePort> in a browser, or use minikube service nginx-nodeport -n service-lab to open a tunnel.
Verification and Diagnostics
Now that the Services are running, we verify their behavior using kubectl commands and direct pod-to-service communication.
Check Endpoints
Endpoints are the actual pod IPs backing the Service. Verify that both pod IPs are listed:
kubectl get endpoints nginx-service -n service-lab
Expected output:
NAME ENDPOINTS AGE
nginx-service 10.244.0.5:80,10.244.0.6:80 2m
Test Service from a Pod
We can run a temporary pod and use curl to access the Service by its DNS name. This tests DNS resolution and load balancing.
kubectl run -it --rm --restart=Never --image=curlimages/curl curl-test -n service-lab -- sh
Inside the pod, run:
curl http://nginx-service
Expected output: The HTML content of the nginx welcome page. Press Ctrl-D to exit; the pod will be deleted.
Test load balancing by running curl multiple times and observing the pod logs. In separate terminals, watch logs:
kubectl logs -f -l app=nginx-deployment -n service-lab
As you issue curls, you should see log entries alternating between the two nginx pods, demonstrating round-robin distribution.
Inspect Service Details
Describe the Service to see its configuration and events:
kubectl describe service nginx-service -n service-lab
Expected output includes:
Name: nginx-service
Namespace: service-lab
Labels: <none>
Annotations: <none>
Selector: app=nginx-deployment
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.96.100.200
IPs: 10.96.100.200
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.0.5:80,10.244.0.6:80
Session Affinity: None
Events: <none>
Check DNS Resolution
Kubernetes creates a DNS entry for each Service in the form <service-name>.<namespace>.svc.cluster.local. Test it from the curl pod:
nslookup nginx-service.service-lab.svc.cluster.local
Expected output includes the ClusterIP:
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: nginx-service.service-lab.svc.cluster.local
Address: 10.96.100.200
Common Pitfalls and How to Avoid Them
Working with Kubernetes Services locally often reveals mistakes that also happen in production. Here are the most frequent ones, why they occur, and how to recover.
Pitfall 1: Selector Mismatch Leads to Empty Endpoints
Why it happens: The Service selector does not match any pod labels. This can be due to a typo, using different label keys, or applying the Service before the Deployment is ready.
How to detect: Run kubectl get endpoints nginx-service -n service-lab. If the ENDPOINTS column shows <none>, the Service has no backing pods.
How to recover: Compare the Service selector with pod labels:
kubectl get pods -n service-lab --show-labels
kubectl get service nginx-service -n service-lab -o yaml | grep -A2 selector
Correct the mismatch by updating either the Service selector or the pod template labels, then reapply. A quick fix is to ensure both use app: nginx-deployment.
Pitfall 2: DNS Resolution Fails Inside Pods
Why it happens: CoreDNS pods may be down, or the client pod's DNS policy is set to Default instead of ClusterFirst, causing it to bypass cluster DNS.
How to detect: From inside a pod, run nslookup nginx-service.service-lab.svc.cluster.local. If the lookup times out or returns NXDOMAIN, DNS is not functioning correctly.
How to recover:
kubectl get pods -n kube-system -l k8s-app=kube-dns
If CoreDNS pods are missing or not ready, restart them:
kubectl rollout restart deployment coredns -n kube-system
Also verify the client pod's DNS config:
kubectl exec -n service-lab <pod-name> -- cat /etc/resolv.conf
Ensure the nameserver points to the cluster DNS IP (often 10.96.0.10). If not, check the pod's dnsPolicy setting and set it to ClusterFirst.
Pitfall 3: NodePort Service Not Accessible from Host
Why it happens: With minikube using the Docker driver, the NodePort is exposed on the minikube VM, not directly on localhost. Firewalls or using the wrong IP can also block access.
How to detect: minikube ip returns the VM IP, but curling that IP with the NodePort fails or times out.
How to recover: Use the minikube service command to open a tunnel or get the correct URL:
minikube service nginx-nodeport -n service-lab
This command prints a URL that forwards traffic to the Service. Alternatively, use kubectl port-forward for quick testing:
kubectl port-forward service/nginx-nodeport 8080:80 -n service-lab
Then access http://localhost:8080.
Pitfall 4: Forgetting to Delete Lab Resources
Why it happens: You might leave the cluster running and forget to clean up, consuming local resources.
How to avoid: Always delete the namespace when you finish experiments:
kubectl delete namespace service-lab
This removes all resources in the namespace. If you want to keep the cluster but only remove specific objects, delete them by name:
kubectl delete service nginx-service nginx-nodeport -n service-lab
kubectl delete deployment nginx-deployment -n service-lab
Failure Modes and Recovery
Services depend on selectors matching pod labels and on network components. Common failures and their recovery steps are listed below.
Failure: Service Has No Endpoints
If the selector does not match any pods, the Service will have empty endpoints.
Diagnosis:
kubectl get endpoints nginx-service -n service-lab
If output shows <none> for ENDPOINTS, check the selector and pod labels.
Recovery:
- Verify pod labels:
kubectl get pods -n service-lab --show-labels - Verify Service selector:
kubectl get service nginx-service -n service-lab -o yaml | grep -A2 selector - Correct the mismatch by editing the Service or Deployment.
Failure: DNS Name Not Resolving
If nslookup inside the pod fails, it may be because CoreDNS is not running or the pod's DNS policy is wrong.
Diagnosis:
kubectl get pods -n kube-system -l k8s-app=kube-dns
Recovery:
- If CoreDNS pods are missing, restart them:
kubectl rollout restart deployment coredns -n kube-system - Check pod DNS config:
kubectl exec -n service-lab -- cat /etc/resolv.conf
Failure: Service Type NodePort Not Accessible
If you cannot reach the NodePort from your host, check firewall rules or the node's IP.
Diagnosis:
minikube ip
kubectl get service nginx-nodeport -n service-lab
Recovery:
- Ensure you are using the correct IP and port.
- For minikube with Docker driver, you may need to use
minikube service nginx-nodeport -n service-labto open a tunnel.
Rollback Strategy
Since resources were created with commands, rollback is simply deleting the created objects.
To remove the entire lab, delete the namespace:
kubectl delete namespace service-lab
Expected output: namespace "service-lab" deleted
Alternatively, delete individual resources using kubectl delete <resource> <name> -n service-lab.
Operations Checklist
Use this checklist each time you set up or modify the lab to ensure consistency.
| Task | Command | Expected Result |
|---|---|---|
| Start cluster | minikube start | Cluster running, kubectl configured |
| Create namespace | kubectl create namespace service-lab | Namespace created |
| Deploy backend | kubectl create deployment nginx-deployment --image=nginx:1.25 --replicas=2 -n service-lab | 2 pods running |
| Create ClusterIP Service | kubectl expose deployment nginx-deployment --name=nginx-service --type=ClusterIP --port=80 --target-port=80 -n service-lab | Service with ClusterIP assigned |
| Verify endpoints | kubectl get endpoints nginx-service -n service-lab | Two pod IPs listed |
| Test from pod | kubectl run ... curl http://nginx-service | HTML response |
| Test DNS | nslookup nginx-service.service-lab.svc.cluster.local | Returns ClusterIP |
| Clean up | kubectl delete namespace service-lab | No resources remaining |
After each experiment, capture the commands and outputs in your notes. This builds a personal knowledge base and helps when you move to production.
Conclusion
You now have a functional local Kubernetes Service lab. You have provisioned a single-node cluster, deployed a sample application, created both ClusterIP and NodePort Services, and verified their behavior through endpoints, DNS, and direct access. You have also seen common failure modes and how to recover from them. The next step is to extend the lab: try creating a Service with multiple ports, experiment with externalTrafficPolicy, or add a NetworkPolicy to see how it affects Service access. Because the setup uses simple commands, you can safely iterate and tear down the environment at any time. This lab provides a solid foundation for understanding Kubernetes Services in production. Keep the operations checklist handy and repeat the exercises until the commands become second nature.