Kubernetes basics
Run a local Kubernetes cluster with kind, deploy a web app with a Deployment and Service, watch self-healing, scale, do a rolling update and rollback, and learn the kubectl commands used to debug.
In this tutorial you will run a small Kubernetes cluster on your own laptop, deploy a web server with three copies, expose it with a Service, scale it, update it without downtime, break it on purpose and watch Kubernetes repair it. Everything runs locally and costs nothing.
Prerequisites: Docker installed and running (see *Docker: images and containers*), kubectl installed, and either kind (Kubernetes in Docker) or Minikube. This lesson uses kind. The install pages on kubernetes.io and kind.sigs.k8s.io cover Windows, macOS and Linux. Check with kubectl version --client and kind version. You need about 4 GB of free RAM.
What problem Kubernetes solves
Docker runs one container on one machine. Real systems need more: run five copies for traffic, restart a container that crashes, move work to another machine when one dies, roll out a new version without downtime, and give all copies one stable address.
Kubernetes (often written K8s) is a system that does this for you. You tell it the state you want ("three copies of this image, always") and it keeps reality matching that wish. Think of a restaurant manager: you say "always keep three chefs at the counter" and when one goes home sick, the manager calls a replacement without being asked.
You do not need Kubernetes for a small project; Docker Compose or a managed platform is simpler. Learn it because many companies run on it and interviews ask about it.
The objects you need to know
Core objects
| Object | What it is | Everyday analogy |
|---|---|---|
| Cluster | A set of machines (nodes) managed together | The whole restaurant |
| Node | One machine in the cluster | A kitchen station |
| Pod | The smallest unit: one or more containers that run together | One chef at work |
| Deployment | Keeps N identical Pods running and handles updates | The manager's rule: always 3 chefs |
| Service | A stable address and load balancer in front of Pods | The counter where orders arrive |
| ConfigMap / Secret | Settings and sensitive values given to Pods | The recipe card / the safe key |
| Namespace | A named partition inside a cluster | Separate sections of the kitchen |
Pods are mortal. Each Pod gets its own IP address that changes whenever it is replaced, so you never talk to a Pod directly. You create a Deployment (which creates and replaces Pods) and a Service (which gives them one steady name).
The cluster has a control plane (the brain: an API server you talk to with kubectl, a store of the desired state, a scheduler that picks nodes and controllers that fix differences) and worker nodes that run your Pods. In managed services such as Amazon EKS, Azure AKS and Google GKE, the provider runs the control plane for you.
Step 1: create a cluster
Start a cluster
- 1Make sure Docker is running.
- 2Run
kind create cluster --name demo. It starts a Kubernetes node as a Docker container and sets upkubectlto talk to it. - 3Run
kubectl cluster-info --context kind-demoto confirm the API server answers. - 4Run
kubectl get nodes. You should see one node with statusReady.
kind create cluster --name demo
kubectl get nodesNAME STATUS ROLES AGE VERSION
demo-control-plane Ready control-plane 45s v1.xx.xStep 2: deploy with a manifest
Kubernetes is driven by YAML files called manifests. Create a file web.yaml. It has two objects separated by ---: a Deployment of three nginx Pods and a Service in front of them.
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
memory: 128Mi
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 2
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector:
app: web
ports:
- port: 80
targetPort: 80replicas: 3 is the desired count. The selector must match the Pod labels: that is how the Deployment knows which Pods are its own, and how the Service finds them. requests tell the scheduler how much to reserve; limits cap use. The readiness probe means a Pod receives traffic only once it answers on port 80.
kubectl apply -f web.yaml
kubectl get deployments
kubectl get pods
kubectl get service webapply is declarative, like Terraform: run it again after editing the file and Kubernetes changes only what differs.
NAME READY UP-TO-DATE AVAILABLE AGE
web 3/3 3 3 20s
NAME READY STATUS RESTARTS AGE
web-6d8f9c7b5d-4kq2x 1/1 Running 0 20s
web-6d8f9c7b5d-9wzjn 1/1 Running 0 20s
web-6d8f9c7b5d-tl6vh 1/1 Running 0 20sStep 3: reach the app
The Service has a cluster-internal IP, so your browser cannot reach it directly. For local testing use port-forward, which tunnels a port on your laptop to the Service.
kubectl port-forward service/web 8080:80Leave this running. In another terminal run curl http://localhost:8080 (or open the URL in a browser). You should get the nginx welcome page. Press Ctrl+C in the first terminal to stop forwarding.
In real clusters you expose apps to the internet with a Service of type LoadBalancer (the cloud creates a public load balancer, which costs money) or with an Ingress (one entry point that routes by host name and path to many Services). Ingress is a good next topic once the basics feel comfortable.
Step 4: see self-healing
Break it on purpose
- 1In one terminal run
kubectl get pods --watch. - 2In a second terminal copy one Pod name and delete it with
kubectl delete pod <pod-name>. - 3Watch the first terminal: a new Pod with a new name appears within seconds and the count returns to 3. The Deployment noticed that reality (2 Pods) no longer matched the wish (3) and fixed it.
Step 5: scale and update
kubectl scale deployment web --replicas=5
kubectl get pods
kubectl set image deployment/web nginx=nginx:1.27-alpine
kubectl rollout status deployment/web
kubectl rollout history deployment/webscale changes the replica count immediately. set image changes the container image, which starts a rolling update: new Pods are started and old ones stopped gradually, and readiness probes make sure traffic only reaches healthy Pods, so users see no downtime.
In real projects you change the image in the YAML file and run kubectl apply -f again, so Git stays the source of truth. kubectl set image is fine for quick experiments. If an update goes wrong, undo it:
kubectl rollout undo deployment/webReturns the Deployment to the previous revision.
Step 6: debugging commands you will use daily
Debugging toolbox
| Command | What it tells you |
|---|---|
kubectl get pods -o wide | Which node each Pod runs on and its IP |
kubectl describe pod <name> | Events at the bottom: why a Pod is Pending, failing to pull an image, or being killed |
kubectl logs <pod> | The container's output; add -f to follow and --previous for a crashed container |
kubectl exec -it <pod> -- sh | A shell inside the container |
kubectl get events --sort-by=.metadata.creationTimestamp | Recent cluster events |
Try a failure. Run kubectl set image deployment/web nginx=nginx:does-not-exist, then kubectl get pods. New Pods show ErrImagePull or ImagePullBackOff, but the old Pods keep serving because the rolling update waits for new ones to become ready. Run kubectl describe pod on a failing Pod to read the reason, then fix it with kubectl rollout undo deployment/web.
ConfigMaps and Secrets in one minute
Do not bake settings into images. A ConfigMap holds plain settings and a Secret holds sensitive values; both can be passed to a container as environment variables. Example for the Brew & Bean API:
kubectl create configmap api-config --from-literal=SHOP_NAME="Brew & Bean" --from-literal=CLOSING_HOUR=22
kubectl create secret generic api-secret --from-literal=DB_PASSWORD=change-meIn the Deployment's container spec you would then add envFrom entries with configMapRef and secretRef naming api-config and api-secret.
Secrets are not magic
A Kubernetes Secret is only base64-encoded by default, not encrypted for people who can read it. Restrict who can read Secrets with RBAC, never commit Secret YAML with real values to Git, and in production use encryption at rest or an external secrets manager.
Step 7: clean up
kubectl delete -f web.yaml
kind delete cluster --name demoRemoves the app and then the whole local cluster.
Where to go next and interviews
Next topics: Ingress, persistent volumes for databases, Helm (a package manager for manifests), horizontal pod autoscaling, and a managed cluster on EKS, AKS or GKE. Managed clusters cost money even when idle (the nodes and sometimes a control plane fee), so see *Cloud costs, free tiers and avoiding surprise bills* first.
How this comes up in interviews: What is the difference between a Pod, a Deployment and a Service? What happens when a Pod crashes? How does a rolling update work? What is the difference between a liveness and a readiness probe? (liveness: restart me if I am stuck; readiness: do not send me traffic yet.) How do you debug a Pod stuck in CrashLoopBackOff? (kubectl logs --previous, then describe.)
You are a Kubernetes engineer helping a beginner.
My Pod shows the status [status, e.g. CrashLoopBackOff]. Here is the output of `kubectl describe pod [pod-name]` and `kubectl logs [pod-name] --previous`:
<describe>
[paste here]
</describe>
<logs>
[paste here]
</logs>
Explain in plain English what is wrong, list the three most likely causes in order, and give the exact kubectl commands to confirm each one.What to expect: A good answer reads the Events section and the last log lines, gives commands you can run, and does not tell you to delete the cluster.
Try it: Add a ConfigMap-driven page
Create a ConfigMap whose value is a one-line HTML message, mount it into the nginx Pods as /usr/share/nginx/html/index.html, apply it, and view it through port-forward.
Key takeaways
- Kubernetes keeps the state you declare: you say what you want, controllers make it true and keep it true.
- Pods are temporary. Use a Deployment to manage them and a Service to give them one stable address.
- Labels and selectors connect Deployments, Pods and Services; a mismatch is a classic bug.
- Rolling updates replace Pods gradually; readiness probes protect users, and
rollout undoreverses a bad release. get,describe,logsandexecsolve most debugging problems.- Secrets are not encrypted by default; managed clusters cost money while they run, so delete practice clusters.
Check yourself
Finishing the quiz marks the lesson as done.
1. What is the main job of a Deployment?
2. You delete one of three Pods from a Deployment. What happens?
3. Why do we create a Service instead of using Pod IP addresses?
4. What does a readiness probe control?
5. A new image tag does not exist. During a rolling update what do users usually see?
Saved on this device. Create a free account to keep it on every device.
Practise this with Sam
A real mock interview, out loud, with an honest scorecard. Free to start.