Kubernetes · Kubernetes Foundations · Foundation · 24 min read · FREE LESSON
Your First Cluster and kubectl: A Hands-On Guide
Create a local cluster, learn the kubectl commands you will use every day, read and write YAML with confidence, and organise work with namespaces and contexts, with a practice lab you can finish in an hour.
A story to begin with
Get a cluster in five minutes
For learning you do not need a cloud account. Any of these gives you a real Kubernetes cluster locally:
| Tool | Notes |
|---|---|
| kind | Kubernetes in Docker. Fast, great for testing and CI. |
| minikube | A single-node cluster with handy add-ons. |
| Docker Desktop | Has a one-click Kubernetes option. |
# with kind
kind create cluster --name lab
kubectl cluster-info --context kind-lab
kubectl get nodes
If kubectl get nodes shows a node in Ready state, you are connected to a working cluster.
How kubectl talks to clusters
kubectl reads a file (by default ~/.kube/config) that lists clusters, users and contexts. A context is a named combination of cluster, user and default namespace.
flowchart LR K[kubectl] --> CFG[kubeconfig file] CFG --> CTX1[context: kind-lab<br/>cluster + user + namespace] CFG --> CTX2[context: prod-eu<br/>cluster + user + namespace] CTX1 --> API1[Local cluster API] CTX2 --> API2[Production cluster API] style CTX2 fill:#f87171,color:#fff
kubectl config get-contexts # list contexts, current one has a star
kubectl config current-context
kubectl config use-context kind-lab # switch
kubectl config set-context --current --namespace=dev # default namespace for this context
Safety habit #1: always know your current context before running anything that changes state. Install a prompt helper (such as kube-ps1) that shows it, and consider colouring production red.
The commands you will use daily
# See things
kubectl get pods # list pods in the current namespace
kubectl get pods -A # all namespaces
kubectl get pods -o wide # extra columns: node, IP
kubectl get deploy,svc,pods # several kinds at once
kubectl describe pod <name> # detailed state and EVENTS at the bottom
kubectl logs <pod> # container logs
kubectl logs <pod> --previous # logs from the previous crashed container
kubectl logs -f deploy/web # follow logs of a deployment
# Create and change things (prefer files)
kubectl apply -f app.yaml # create or update from a file
kubectl apply -f ./manifests/ # a whole folder
kubectl delete -f app.yaml # delete what the file describes
kubectl scale deploy/web --replicas=5
kubectl rollout status deploy/web
kubectl rollout undo deploy/web
# Look inside and reach things
kubectl exec -it <pod> -- sh # shell inside a container
kubectl port-forward svc/web 8080:80 # reach a service from your laptop
kubectl top pods # CPU and memory (needs metrics-server)
# Learn the API itself
kubectl explain deployment.spec.strategy # built-in documentation of any field
kubectl api-resources # every kind the cluster knows
kubectl explain is the most underrated command: it documents every field, offline, for the exact version of your cluster.
Declarative vs imperative
| Style | Example | When to use |
|---|---|---|
| Imperative | kubectl create deployment web --image=nginx |
Quick experiments |
| Declarative | kubectl apply -f deployment.yaml |
Everything real: reviewable, repeatable, stored in Git |
Generate YAML to learn from instead of memorising it:
kubectl create deployment web --image=nginx:1.27 --replicas=2 --dry-run=client -o yaml > web.yaml
Reading a YAML manifest
Every Kubernetes object has the same four top-level parts:
apiVersion: apps/v1 # which API group and version defines this kind
kind: Deployment # what type of object
metadata: # identity: name, namespace, labels, annotations
name: web
namespace: dev
labels: {app: web}
spec: # the DESIRED state; its shape depends on the kind
replicas: 2
selector:
matchLabels: {app: web}
template: # a blueprint for the pods this Deployment creates
metadata:
labels: {app: web}
spec:
containers:
- name: web
image: nginx:1.27
A fifth part, status, is written by the cluster to show the actual state. You never write it. Reading spec and status side by side shows you what you asked for and what you got.
Three rules that remove most YAML errors:
- Indentation uses spaces, never tabs, and it expresses structure.
- A dash
-starts one item in a list. - The
selectorof a Deployment must match the labels in its podtemplate, or the Deployment will refuse to work.
Namespaces: rooms inside the cluster
kubectl get namespaces
kubectl create namespace dev
kubectl get pods -n dev # look in a specific namespace
kubectl apply -f app.yaml -n dev
Namespaces separate teams, environments and apps, and let you attach quotas and access rules per namespace. Default ones: default, kube-system (cluster components), kube-public, kube-node-lease.
Lab: deploy, expose, update and break an app (about an hour)
# 1. A namespace for the lab
kubectl create namespace lab && kubectl config set-context --current --namespace=lab
# 2. Deploy two copies of a web server
kubectl create deployment web --image=nginx:1.26 --replicas=2
kubectl get pods -w # Ctrl+C when both are Running
# 3. Expose it inside the cluster and reach it from your laptop
kubectl expose deployment web --port=80
kubectl port-forward svc/web 8080:80 # open http://localhost:8080 in a browser
# 4. Roll out a new version and watch it
kubectl set image deployment/web nginx=nginx:1.27
kubectl rollout status deployment/web
# 5. Break it on purpose, then diagnose and fix
kubectl set image deployment/web nginx=nginx:does-not-exist
kubectl get pods # ErrImagePull / ImagePullBackOff
kubectl describe pod <broken-pod> # read Events
kubectl rollout undo deployment/web # roll back
# 6. Clean up
kubectl delete namespace lab
Write down what you saw at each step. Step 5 is the most valuable: you have now diagnosed and fixed a real failure with three commands.
Safe-operation habits to adopt now
- Know your context and namespace before every command.
- Prefer
kubectl apply -ffrom version-controlled files over hand-typed changes. - Use
--dry-run=client -o yamlandkubectl diff -f file.yamlto preview changes. - Read
describeoutput (especially Events) before restarting anything. - Keep production access read-only by default; make changes through your pipeline.
How to onboard a team onto Kubernetes in your organization
flowchart LR A[Shared lab cluster<br/>per learner namespace] --> B[Starter repo with<br/>templates and examples] B --> C[Guided workshop:<br/>deploy, expose, break, fix] C --> D[Cheat sheet and<br/>kubectl aliases] D --> E[Read-only prod access,<br/>changes via pipeline] style C fill:#6366f1,color:#fff
- Provide a safe place to practise: a shared sandbox cluster where each learner gets their own namespace with a small quota, so mistakes cost nothing.
- Create a starter repository with example manifests (Deployment, Service, ConfigMap) that follow your standards.
- Run a two-hour workshop using the lab above. Rotate who drives. End with a "break it, fix it" challenge.
- Publish a one-page cheat sheet of the commands and your naming and label conventions.
- Set access rules: developers get edit rights in dev namespaces, read-only in production; production changes go through CI/CD.
- Add guardrails: a prompt that shows context, resource quotas on namespaces, and policy checks that block risky manifests.
- Measure: time for a new engineer to deploy their first service, number of accidental production changes (should reach zero), and survey confidence.
Pitfalls
- Giving everyone cluster-admin on shared clusters.
- Teaching YAML syntax for hours instead of letting people deploy something.
- Skipping the safety habits until after the first accident.
Interview questions
Q1. What is the difference between kubectl apply and kubectl create?
create makes a new object and fails if it exists. apply is declarative: it creates or updates to match the file and records the last applied state, so it is the right choice for repeatable, Git-driven workflows.
Q2. What is a kubeconfig context?
A named bundle of cluster, user and default namespace. kubectl uses the current context to decide which API server to talk to and as whom.
Q3. How do you see why a pod is not starting?
kubectl describe pod and read the Events section, then kubectl logs (with --previous after a crash), and check image names, config references and resource requests.
Q4. What is the purpose of spec and status?
spec is the desired state you declare; status is the observed state written by the cluster. Controllers work to make status match spec.
Q5. How can you preview a change before applying it?
kubectl diff -f file.yaml shows what would change, and --dry-run=client or --dry-run=server validates without persisting.
Q6. Name three ways to reduce the risk of running a command on the wrong cluster. Show the context in your shell prompt, use separate kubeconfig files or colour-coded terminals for production, and restrict production access to read-only with changes through the pipeline.
Next: Pods in Depth explains the unit you have been creating.