Lab¶
Duration: 30 minutes
Objectives¶
- Practice essential kubectl commands
- Use kubectl shortcuts and productivity features
- Debug common issues
- Explore cluster using k9s
- Master log viewing and pod inspection
Prerequisites¶
- Kind cluster running
- kubectl configured
- k9s installed (available in the workshop container)
Tasks¶
Task 1: kubectl Basics¶
Practice fundamental kubectl commands: create, scale, observe, and inspect.
Hint
Solution
# Create deployment
kubectl create deployment web --image=nginx:1.25-alpine --replicas=2
# View pods
kubectl get pods
kubectl get pods -o wide
kubectl get pods -o yaml | head -30
# Scale deployment
kubectl scale deployment web --replicas=4
# Watch scaling (Ctrl+C to stop)
kubectl get pods -w
# View deployment
kubectl get deployment web
kubectl describe deployment web
# View logs
kubectl logs deployment/web
kubectl logs -l app=web --tail=10
Task 2: Resource Filtering and Selection¶
Create multiple pods with different labels and practice filtering.
Requirements:
- Create 3 pods with different
appandenvlabels - Filter by single label, multiple labels, and negation
- Use
--show-labelsand--field-selector
Hint
kubectl run pod1 --image=nginx:1.25-alpine --labels="app=web,env=dev"
kubectl run pod2 --image=nginx:1.25-alpine --labels="app=web,env=prod"
kubectl run pod3 --image=nginx:1.25-alpine --labels="app=api,env=dev"
kubectl get pods -l app=web
kubectl get pods -l 'app=web,env=dev'
kubectl get pods -l 'env!=prod'
Solution
# Create pods with labels
kubectl run pod1 --image=nginx:1.25-alpine --labels="app=web,env=dev"
kubectl run pod2 --image=nginx:1.25-alpine --labels="app=web,env=prod"
kubectl run pod3 --image=nginx:1.25-alpine --labels="app=api,env=dev"
# Wait for pods
kubectl wait --for=condition=Ready pods pod1 pod2 pod3 --timeout=30s
# Filter by single label
kubectl get pods -l app=web
# Filter by multiple labels
kubectl get pods -l 'app=web,env=dev'
# Negative filter
kubectl get pods -l 'env!=prod'
# Show labels
kubectl get pods --show-labels
# Filter by field selector
kubectl get pods --field-selector status.phase=Running
# Combine filters
kubectl get pods -l app=web --field-selector status.phase=Running
Task 3: Debugging Failing Pods¶
Debug a Pod that won't start using describe and logs.
Requirements:
- Create a Pod with an intentionally wrong image name
- Use
kubectl describeto find the error - Fix the issue
Hint
Solution
# Create pod with wrong image
kubectl run broken --image=nginx:nonexistent
# Check status
kubectl get pods broken
# Output:
# NAME READY STATUS RESTARTS AGE
# broken 0/1 ImagePullBackOff 0 10s
# Describe to see what's wrong
kubectl describe pod broken
# Output (excerpt from Events):
# Events:
# Type Reason Age From Message
# ---- ------ ---- ---- -------
# Normal Scheduled 30s default-scheduler Successfully assigned default/broken to kind-worker
# Normal Pulling 30s kubelet Pulling image "nginx:nonexistent"
# Warning Failed 25s kubelet Failed to pull image "nginx:nonexistent":
# rpc error: ... manifest for nginx:nonexistent not found
# Fix by deleting and recreating with correct image
kubectl delete pod broken
kubectl run broken --image=nginx:1.25-alpine
kubectl wait --for=condition=Ready pod/broken --timeout=30s
kubectl get pods broken
# Output:
# NAME READY STATUS RESTARTS AGE
# broken 1/1 Running 0 15s
# Cleanup
kubectl delete pod broken
Task 4: Executing Commands and Port Forwarding¶
Access Pods interactively and expose services locally.
Requirements:
- Expose the
webdeployment as a Service - Execute commands inside the Pod
- Get an interactive shell
- Use port-forward to test locally
Hint
Solution
# Create a service for web deployment
kubectl expose deployment web --port=80
# Execute single command
kubectl exec deployment/web -- nginx -v
# Output:
# nginx version: nginx/1.25.x
# Interactive shell
kubectl exec -it deployment/web -- sh
# Inside pod:
ls /etc/nginx/
cat /etc/nginx/nginx.conf | head -10
exit
# Port forward in background
kubectl port-forward deployment/web 8080:80 &
PF_PID=$!
# Wait briefly for port-forward to start
sleep 1
# Test locally
curl http://localhost:8080 | head -5
# Output:
# <!DOCTYPE html>
# <html>
# <head>
# <title>Welcome to nginx!</title>
# Stop port forward
kill $PF_PID
Task 5: JSONPath and Custom Output¶
Extract specific information from kubectl output.
Requirements:
- Get all pod names as a list
- Extract pod IPs
- Use custom columns to show name, status, IP, and node
Hint
Solution
# Get all pod names
kubectl get pods -o jsonpath='{.items[*].metadata.name}'
echo ""
# Get pod IPs
kubectl get pods -o jsonpath='{.items[*].status.podIP}'
echo ""
# Formatted output with newlines
kubectl get pods -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.podIP}{"\n"}{end}'
# Custom columns showing useful fields
kubectl get pods -o custom-columns=NAME:.metadata.name,STATUS:.status.phase,IP:.status.podIP,NODE:.spec.nodeName
# Get container images for all pods
kubectl get pods -o jsonpath='{.items[*].spec.containers[*].image}'
echo ""
# Check resource requests on a specific pod
kubectl get pod pod1 -o jsonpath='{.spec.containers[*].resources}' 2>/dev/null || echo "pod1 may not exist; use any running pod name"
Task 6: Dry Run and Manifest Generation¶
Use dry-run to generate manifests and validate configs.
Requirements:
- Generate a Deployment YAML without creating it
- Validate a manifest with server-side dry-run
- Use
kubectl explainto explore a resource field
Hint
Solution
# Generate deployment YAML without creating it
kubectl create deployment test --image=nginx:1.25-alpine --dry-run=client -o yaml
# Generate and save to file
kubectl create deployment test --image=nginx:1.25-alpine --dry-run=client -o yaml > /tmp/test-deploy.yaml
# Validate with server-side dry-run
kubectl apply -f /tmp/test-deploy.yaml --dry-run=server
# Output:
# deployment.apps/test configured (server dry run)
# Explore resource documentation
kubectl explain deployment
kubectl explain deployment.spec.strategy
kubectl explain deployment.spec.strategy.rollingUpdate
# Generate service YAML
kubectl expose deployment web --port=80 --dry-run=client -o yaml
# Cleanup
rm /tmp/test-deploy.yaml
Task 7: Explore with k9s¶
Use k9s to explore and manage the cluster visually.
Requirements:
- Launch k9s
- Navigate to pods, services, deployments
- View logs for a pod
- Shell into a pod
- Filter resources
Hint
Solution
# Launch k9s
k9s
# Inside k9s, try these actions:
# 1. Press :po <Enter> to view pods
# 2. Select a pod with arrow keys, press l to view logs
# 3. Press Esc to go back, select pod again, press s to shell in
# 4. Type 'exit' to leave the shell
# 5. Press / then type 'web' to filter pods by name
# 6. Press Esc to clear the filter
# 7. Press :svc <Enter> to view services
# 8. Press :deploy <Enter> to view deployments
# 9. Press 0 to show all namespaces
# 10. Press :q to quit k9s
Verification¶
# Check all resources you've created
kubectl get all
# Verify label filtering works
kubectl get pods --show-labels
# Confirm port-forward is stopped
lsof -i :8080 2>/dev/null | grep kubectl || echo "No port-forward running"
Cleanup¶
# Delete deployment and service
kubectl delete deployment web
kubectl delete service web
# Delete pods
kubectl delete pods pod1 pod2 pod3 --ignore-not-found
kubectl delete pod broken --ignore-not-found
Bonus Challenges¶
1. Advanced JSONPath: Extract a table showing pod name, restarts, and age
Solution
# Create deployment to have some pods
kubectl create deployment web --image=nginx:1.25-alpine --replicas=2
# Wait for pods
kubectl wait --for=condition=Ready pods -l app=web --timeout=30s
# Table with pod name, restarts, and phase
kubectl get pods -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}Restarts: {.status.containerStatuses[0].restartCount}{"\t"}{.status.phase}{"\n"}{end}'
# Get specific annotations
kubectl get deployment web -o jsonpath='{.metadata.annotations}'
# Cleanup
kubectl delete deployment web
2. Events Monitoring: Watch all events in the cluster in real-time
Solution
# Watch all events (newest first)
kubectl get events --sort-by='.lastTimestamp'
# Watch events in real-time
kubectl get events -w
# Filter events by type (Warning)
kubectl get events --field-selector type=Warning
# Filter events for a specific object
kubectl get events --field-selector involvedObject.name=web
# Events across all namespaces
kubectl get events -A --sort-by='.lastTimestamp'
Common issues¶
| Issue | Solution |
|---|---|
command not found: k9s |
Check it's installed: which k9s or install via: brew install k9s |
| kubectl context is wrong | Run kubectl config get-contexts and kubectl config use-context <name> |
| JSONPath returns nothing | Check the path is correct with kubectl get pod <name> -o yaml first |
| Port-forward connection refused | Ensure pod is Running and the port number is correct |
Key takeaways¶
kubectl describeis your first stop for debugging — always check the Events section- Labels and selectors power resource filtering — label everything
- JSONPath lets you extract exactly the field you need from any resource
--dry-run=clientis perfect for generating manifests;--dry-run=servervalidates them- k9s provides fast, visual navigation for exploring clusters
Next section¶
Once you've reviewed the content and completed the lab, proceed to the next section