Kubernetes Workshop: Fundamentals – Exercises with Solutions
Reference: 01-fundamentals-EN.md
Difficulty level: Fundamentals
Format: Hands-on lab with sample solutions and walkthroughs
Goal of this exercise document
This exercise document complements the Fundamentals workshop with concrete tasks, verifiable results, and complete solutions. The focus is on safe basic operation, the Kubernetes object model, kubectl, Pods, Deployments, ReplicaSets, Services, Labels, Selectors, ConfigMaps, Secrets, Namespaces, rollouts, scaling, rollback, and first-level troubleshooting.
You work only in a lab cluster. Do not use production cluster contexts or real credentials.
Prerequisites
You need:
kubectl- a local or provided Kubernetes cluster, for example
kind,minikube, ork3d - a non-critical cluster context
- write permissions in your own Namespace
Recommended Namespace for all exercises:
export NS=workshop-fundamentals
Working rules
- Check the active context before each exercise.
- Work only in the
$NSNamespace. - Save manifests as files before applying them.
- Use
kubectl apply --dry-run=serverif your cluster supports it. - After every change, check status, Events, and logs.
- Document the cleanup at the end.
Exercise overview
| No. | Topic | Result |
|---|---|---|
| 1 | Context, cluster, and Namespace | safe lab context and Namespace |
| 2 | Reading the object model | Deployment manifest explained |
| 3 | Deployment and Service | simple web app reachable internally |
| 4 | Labels and Selectors | Service error diagnosed and fixed |
| 5 | ConfigMap and Secret | configuration and sample Secret mounted |
| 6 | Rollout, scaling, and rollback | image update, scaling, and rollback performed |
| 7 | Troubleshooting: ImagePullBackOff | wrong image diagnosed and corrected |
| 8 | Troubleshooting: CrashLoopBackOff | crashing application diagnosed and corrected |
| 9 | Operations note and cleanup | resource list, validation, and cleanup documented |
Part A – Exercises
Exercise 1: Check context, cluster, and Namespace
Goal
You make sure that you are working in the correct cluster and create your own Namespace.
Task
- Check the active context.
- Check cluster information and Nodes.
- Create the Namespace
workshop-fundamentals. - Set the Namespace for the current context.
- Document cluster name, number of Nodes, and Kubernetes version.
Expected result
- The Namespace exists.
kubectl get podsshows resources fromworkshop-fundamentalsby default.- You can explain why the context is checked before every lab.
Exercise 2: Read the Kubernetes object model
Goal
You read a Deployment manifest and identify apiVersion, kind, metadata, spec, selector, Pod template, and container configuration.
Task
Create the file deployment.yml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: workshop-fundamentals
labels:
app: web
tier: frontend
environment: lab
spec:
replicas: 2
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
tier: frontend
environment: lab
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
Answer in writing:
- Which API group is used?
- Which resource is created?
- Where is the desired state defined?
- Which Labels do the Pods receive?
- What is the Selector used for?
Exercise 3: Roll out Deployment and Service
Goal
You roll out a simple web application and make it reachable through an internal ClusterIP Service.
Task
- Apply
deployment.yml. - Create the file
service.yml:
apiVersion: v1
kind: Service
metadata:
name: web
namespace: workshop-fundamentals
labels:
app: web
spec:
type: ClusterIP
selector:
app: web
ports:
- name: http
port: 80
targetPort: 80
- Apply
service.yml. - Check Deployment, ReplicaSet, Pods, Service, Endpoints, and EndpointSlices.
- Test the Service with a temporary debug Pod.
Expected result
- Two Pods are running.
- The Service has Endpoints.
- A temporary Pod can reach
http://web.
Exercise 4: Intentionally break Labels and Selectors
Goal
You understand why Services find Pods through Labels and Selectors, not through Pod names.
Task
- Intentionally change the Service Selector in
service.ymlto an incorrect value:
selector:
app: wrong-web
- Apply the change.
- Check Service, Endpoints, and EndpointSlices.
- Correct the Selector back to
app: web. - Check again.
Expected result
- With the wrong Selector, the Service has no Endpoints.
- After the correction, Endpoints are present again.
Exercise 5: Mount ConfigMap and Secret
Goal
You distinguish non-sensitive configuration from sensitive sample values and mount both into a Pod.
Task
Create configmap.yml:
apiVersion: v1
kind: ConfigMap
metadata:
name: web-config
namespace: workshop-fundamentals
data:
APP_MODE: lab
WELCOME_TEXT: "Hello from Kubernetes"
Create secret.yml:
apiVersion: v1
kind: Secret
metadata:
name: web-secret
namespace: workshop-fundamentals
type: Opaque
stringData:
DEMO_PASSWORD: "not-a-real-secret"
Extend your Deployment so that APP_MODE is mounted as an environment variable and DEMO_PASSWORD is mounted as a file.
Expected result
- The ConfigMap exists.
- The Secret exists.
- The Pod contains
APP_MODEas an environment variable. - The Secret is visible as a file in the container.
Exercise 6: Perform rollout, scaling, and rollback
Goal
You use Deployment rollouts in a controlled way and can revert a change.
Task
- Check the current rollout status.
- Scale the Deployment to three replicas.
- Change the image from
nginx:1.27tonginx:1.26. - Check rollout status and rollout history.
- Perform a rollback.
- Check which image is active afterward.
Expected result
- Three Pods are running.
- The rollout history shows at least two revisions.
- After the rollback, the previous image is running again.
Exercise 7: Diagnose ImagePullBackOff
Goal
You provoke an image pull error and fix it based on Events.
Task
Create bad-image.yml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: bad-image
namespace: workshop-fundamentals
spec:
replicas: 1
selector:
matchLabels:
app: bad-image
template:
metadata:
labels:
app: bad-image
spec:
containers:
- name: app
image: nginx:this-tag-does-not-exist
ports:
- containerPort: 80
- Apply the manifest.
- Check Pod status and Events.
- Correct the image to
nginx:1.27. - Check again.
Exercise 8: Diagnose CrashLoopBackOff
Goal
You provoke a crashing application, read logs, and correct the startup command.
Task
Create crashloop.yml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: crashloop
namespace: workshop-fundamentals
spec:
replicas: 1
selector:
matchLabels:
app: crashloop
template:
metadata:
labels:
app: crashloop
spec:
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "echo 'I terminate immediately'; exit 1"]
- Apply the manifest.
- Check the Pod status.
- Read logs and previous logs.
- Correct the command so that the container keeps running.
- Check again.
Exercise 9: Operations note and cleanup
Goal
You document what was created, how you verified the state, and how everything is cleaned up.
Task
Create notes.md with:
- cluster context
- Namespace
- applied manifests
- created resources
- most important diagnostic commands
- known errors and fixes
- cleanup command
Part B – Solutions and walkthroughs
Solution 1: Check context, cluster, and Namespace
kubectl config current-context
kubectl cluster-info
kubectl version --client
kubectl get nodes -o wide
kubectl create namespace workshop-fundamentals
kubectl config set-context --current --namespace=workshop-fundamentals
kubectl get pods
Validation:
kubectl get namespace workshop-fundamentals
kubectl config view --minify --output 'jsonpath={..namespace}'
Sample explanation:
- The context determines which cluster
kubectlcommunicates with. - The Namespace limits the default view of
kubectl, but it does not replace security isolation. kubectl cluster-infoshows API reachability.kubectl get nodes -o wideshows Node status and runtime environment.
Solution 2: Read the Kubernetes object model
Sample answers:
| Question | Answer |
|---|---|
| API group | apps/v1; resource from the Apps API group |
| Resource | Deployment |
| desired state | under spec |
| Pod Labels | app=web, tier=frontend, environment=lab |
| Selector | connects the Deployment with the Pods it manages |
Helpful exploration:
kubectl explain deployment
kubectl explain deployment.spec
kubectl explain deployment.spec.template.spec.containers
kubectl api-resources | grep -i deployment
Solution 3: Roll out Deployment and Service
kubectl apply --dry-run=server -f deployment.yml
kubectl apply -f deployment.yml
kubectl rollout status deployment/web
kubectl apply --dry-run=server -f service.yml
kubectl apply -f service.yml
Check:
kubectl get deployment,replicaset,pod,svc,endpoints,endpointslices
kubectl describe deployment web
kubectl describe svc web
Service test:
kubectl run curl --rm -i --tty --restart=Never --image=curlimages/curl:8.8.0 -- http://web
Alternative if interactive kubectl run behaves differently in your environment:
kubectl run curl --restart=Never --image=curlimages/curl:8.8.0 -- sleep 3600
kubectl wait --for=condition=Ready pod/curl --timeout=60s
kubectl exec curl -- curl -s http://web
kubectl delete pod curl
Expected state:
deployment.apps/webis available.service/webhas a ClusterIP.endpoints/webcontains Pod IP addresses.
Solution 4: Correct Labels and Selectors
Apply the incorrect Selector:
kubectl apply -f service.yml
kubectl get svc,endpoints,endpointslices
kubectl describe svc web
Typical symptom:
Endpoints: <none>
Diagnosis:
kubectl get pods --show-labels
kubectl get svc web -o yaml
Correction in service.yml:
selector:
app: web
Afterward:
kubectl apply -f service.yml
kubectl get endpoints web
kubectl get endpointslices -l kubernetes.io/service-name=web
Technical explanation:
A Service does not know fixed Pod names. It finds Pods through the Selector. If Selector and Pod Labels do not match, the Service exists, but it has no targets.
Solution 5: Mount ConfigMap and Secret
Apply ConfigMap and Secret:
kubectl apply -f configmap.yml
kubectl apply -f secret.yml
kubectl get configmap,secret
Deployment extension:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: workshop-fundamentals
labels:
app: web
tier: frontend
environment: lab
spec:
replicas: 2
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
tier: frontend
environment: lab
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
env:
- name: APP_MODE
valueFrom:
configMapKeyRef:
name: web-config
key: APP_MODE
volumeMounts:
- name: web-secret-volume
mountPath: /etc/demo-secret
readOnly: true
volumes:
- name: web-secret-volume
secret:
secretName: web-secret
Apply and check:
kubectl apply -f deployment.yml
kubectl rollout status deployment/web
POD=$(kubectl get pod -l app=web -o jsonpath='{.items[0].metadata.name}')
kubectl exec "$POD" -- printenv APP_MODE
kubectl exec "$POD" -- ls -l /etc/demo-secret
kubectl exec "$POD" -- cat /etc/demo-secret/DEMO_PASSWORD
Technical classification:
- ConfigMaps are intended for non-sensitive configuration.
- Secrets are intended for sensitive values, but Base64 is not encryption.
- Real Secrets do not belong in learning manifests or plaintext Git.
Solution 6: Rollout, scaling, and rollback
Check status:
kubectl rollout status deployment/web
kubectl get deployment web
Scale:
kubectl scale deployment/web --replicas=3
kubectl rollout status deployment/web
kubectl get pods -l app=web
Change image:
kubectl set image deployment/web nginx=nginx:1.26
kubectl rollout status deployment/web
kubectl rollout history deployment/web
Check active image:
kubectl get deployment web -o jsonpath='{.spec.template.spec.containers[0].image}{"\n"}'
Rollback:
kubectl rollout undo deployment/web
kubectl rollout status deployment/web
kubectl get deployment web -o jsonpath='{.spec.template.spec.containers[0].image}{"\n"}'
Sample explanation:
scalechangesspec.replicas.set imagechanges the Pod template and therefore creates a new revision.rollout undosets the previous revision back as the desired state.
Solution 7: ImagePullBackOff
Apply:
kubectl apply -f bad-image.yml
kubectl get pods -l app=bad-image
Diagnosis:
POD=$(kubectl get pod -l app=bad-image -o jsonpath='{.items[0].metadata.name}')
kubectl describe pod "$POD"
kubectl get events --sort-by=.metadata.creationTimestamp
Typical finding:
- The image tag does not exist.
- Events contain messages for
Failed to pull imageorErrImagePull.
Correction:
kubectl set image deployment/bad-image app=nginx:1.27
kubectl rollout status deployment/bad-image
kubectl get pods -l app=bad-image
Or correct the file and apply it again:
sed -i 's/nginx:this-tag-does-not-exist/nginx:1.27/' bad-image.yml
kubectl apply -f bad-image.yml
Solution 8: CrashLoopBackOff
Apply:
kubectl apply -f crashloop.yml
kubectl get pods -l app=crashloop
Diagnosis:
POD=$(kubectl get pod -l app=crashloop -o jsonpath='{.items[0].metadata.name}')
kubectl describe pod "$POD"
kubectl logs "$POD"
kubectl logs "$POD" --previous
Typical finding:
- The container starts.
- The command exits with exit code
1. - Kubernetes restarts it; this creates
CrashLoopBackOff.
Correction by manifest:
apiVersion: apps/v1
kind: Deployment
metadata:
name: crashloop
namespace: workshop-fundamentals
spec:
replicas: 1
selector:
matchLabels:
app: crashloop
template:
metadata:
labels:
app: crashloop
spec:
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "echo 'I stay active'; sleep 3600"]
Apply:
kubectl apply -f crashloop.yml
kubectl rollout status deployment/crashloop
kubectl get pods -l app=crashloop
Solution 9: Operations note and cleanup
Template for notes.md:
# Operations Note – Fundamentals Lab
## Context
- Cluster context: <from kubectl config current-context>
- Namespace: workshop-fundamentals
- Date: <enter date>
## Manifests
- deployment.yml
- service.yml
- configmap.yml
- secret.yml
- bad-image.yml
- crashloop.yml
## Validation
- kubectl get deployment,rs,pod,svc,endpoints,endpointslices
- kubectl rollout status deployment/web
- kubectl describe pod <pod>
- kubectl logs <pod>
## Errors and fixes
| Error | Cause | Fix |
| --- | --- | --- |
| Service without Endpoints | wrong Selector | corrected Selector to app=web |
| ImagePullBackOff | invalid image tag | set image to nginx:1.27 |
| CrashLoopBackOff | process exits with exit 1 | changed command to sleep 3600 |
## Cleanup
kubectl delete namespace workshop-fundamentals
Cleanup:
kubectl delete namespace workshop-fundamentals
Validation after cleanup:
kubectl get namespace workshop-fundamentals
Expected state: The Namespace is deleted or briefly in Terminating state.
Assessment checklist
| Criterion | Completed? |
|---|---|
| Context checked before applying changes | ☐ |
| own Namespace used | ☐ |
| Deployment manifest explained | ☐ |
| Service with Endpoints created | ☐ |
| Selector error diagnosed | ☐ |
| ConfigMap and Secret mounted correctly | ☐ |
| Rollout, scaling, and rollback performed | ☐ |
ImagePullBackOff explained based on Events |
☐ |
CrashLoopBackOff explained based on logs |
☐ |
| Cleanup documented | ☐ |