Kubernetes Demo: 2 Pods + 1 Service + Label Matching
Goal: Create 2 Pods running different Nginx images and expose both through a single ClusterIP Service using label selectors.
Service: nginx-svc
Selector: app=nginx-demo
nginx-v1
app=nginx-demo
nginx-v2
app=nginx-demo
Step 1: Create 2 Pods and 1 Service
Copy and run:
cat > demo.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: nginx-v1
labels:
app: nginx-demo
version: v1
spec:
containers:
- name: nginx
image: scmgalaxy/nginx-devopsschoolv1
ports:
- containerPort: 80
---
apiVersion: v1
kind: Pod
metadata:
name: nginx-v2
labels:
app: nginx-demo
version: v2
spec:
containers:
- name: nginx
image: scmgalaxy/nginx-devopsschoolv2
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
spec:
type: ClusterIP
selector:
app: nginx-demo
ports:
- port: 80
targetPort: 80
EOF
kubectl apply -f demo.yaml
Step 2: Verify Pods, Labels and Service
# View pods with labels
kubectl get pods --show-labels
# View pods matching service selector
kubectl get pods -l app=nginx-demo -o wide
# View service
kubectl get svc nginx-svc
# Check which pods are selected (endpoints)
kubectl get endpointslice -l kubernetes.io/service-name=nginx-svc -o wide
# Check service selector
kubectl describe svc nginx-svc
Step 3: Test Service Load Balancing
Run requests from inside the Kubernetes cluster:
kubectl run test-client --rm -i \
--restart=Never \
--image=curlimages/curl \
-- sh -c 'for i in 1 2 3 4 5 6 7 8 9 10; do curl -s http://nginx-svc; echo; done'
Both Pods are eligible to receive traffic. The exact distribution is not guaranteed.
Step 4: Change Pod Label and Observe Service
Remove nginx-v2 from the Service by changing its matching label:
# Change v2 label
kubectl label pod nginx-v2 app=other --overwrite
# Only nginx-v1 should match
kubectl get pods -l app=nginx-demo
# Verify service endpoints
kubectl get endpointslice -l kubernetes.io/service-name=nginx-svc -o wide
Restore the original label:
kubectl label pod nginx-v2 app=nginx-demo --overwrite
# Both pods match again
kubectl get pods -l app=nginx-demo
kubectl get endpointslice -l kubernetes.io/service-name=nginx-svc -o wide
Step 5: Cleanup
kubectl delete -f demo.yaml
Key concept: Kubernetes Service uses spec.selector to find Pods with matching metadata.labels. The Service does not select Pods by their names or container images.
Kubernetes Deployment + 3 Service Types
Step 1: Create Deployment
kubectl create deployment nginx-demo \
--image=scmgalaxy/nginx-devopsschoolv1 \
--replicas=2 \
--port=80
Step 2: Expose as ClusterIP
kubectl expose deployment nginx-demo \
--name=nginx-clusterip \
--type=ClusterIP \
--port=80 \
--target-port=80
Step 3: Expose as NodePort
kubectl expose deployment nginx-demo \
--name=nginx-nodeport \
--type=NodePort \
--port=80 \
--target-port=80
Step 4: Expose as LoadBalancer
kubectl expose deployment nginx-demo \
--name=nginx-lb \
--type=LoadBalancer \
--port=80 \
--target-port=80
Step 5: Verify Everything
kubectl get deployments
kubectl get pods -o wide
kubectl get svc
kubectl get endpointslice
kubectl get pods --show-labels
kubectl describe svc nginx-nodeport
Step 6: Test Services
ClusterIP (inside cluster):
kubectl run curl-test --rm -it \
--image=curlimages/curl --restart=Never \
-- curl http://nginx-clusterip
NodePort (from outside, if node networking permits):
curl http://<NODE-IP>:<NODEPORT>
LoadBalancer (after external IP is assigned):
curl http://<EXTERNAL-IP>
Note: NodePort is normally allocated from 30000–32767. LoadBalancer requires a cloud load balancer integration or a solution such as MetalLB.
Step 7: Cleanup
kubectl delete svc nginx-clusterip nginx-nodeport nginx-lb
kubectl delete deployment nginx-demo
Result: One Deployment → 2 Pods → 3 Services (ClusterIP, NodePort, LoadBalancer), all targeting the same Pods through label matching.