Skip to main content

Kubernetes cheatsheet

Learn Kubernetes step by step, from your first pod to locking down who can do what. Each section has three parts:

  • In short β€” the idea in one sentence.
  • Example β€” commands and manifests with a comment saying what each does.
  • Try it β€” a tiny exercise to check you understood.

Want the longer story behind a topic? The complete guide walks through it in more depth.

πŸ“– Full guide: Kubernetes β†’ Intermediate
How to use this page

Know the basics of Docker first β€” Kubernetes runs containers. Then create the practice cluster: it runs on your laptop and nothing you do there can break anything real. Go through Part 1 in order; Part 2 covers what every real deployment needs; Part 3 is for running clusters in production.

Contents​

Practice cluster

Part 1 β€” Beginner: What Kubernetes is Β· kubectl basics Β· Pods Β· Deployments Β· YAML manifests Β· Services Β· Labels & selectors Β· Namespaces & contexts

Part 2 β€” Core: ConfigMaps & Secrets Β· Health checks (probes) Β· Requests & limits Β· Rolling updates & rollbacks Β· Storage Β· Jobs & CronJobs Β· Ingress Β· Troubleshooting Β· Common mistakes

Part 3 β€” Advanced: StatefulSets Β· DaemonSets Β· Scheduling: taints & affinity Β· Autoscaling Β· Access control (RBAC) Β· Network policies Β· Kustomize & Helm Β· How a cluster works Β· Words you'll meet

Practice cluster​

In short: kind ("Kubernetes in Docker") runs a real, single-machine cluster inside Docker. Every example below runs in a namespace called practice.

# Practice cluster: needs Docker running
brew install kind kubectl # Linux/Windows: see kind.sigs.k8s.io
kind create cluster --name learn # about a minute
kubectl get nodes # learn-control-plane Ready
kubectl create namespace practice
kubectl config set-context --current --namespace practice # use "practice" by default

Docker Desktop's built-in Kubernetes, minikube, or k3d work the same way. To start over: kind delete cluster --name learn.

Part 1 β€” Beginner​

1. What Kubernetes is​

In short: Kubernetes (often "K8s") runs your containers across a group of machines and keeps them in the state you asked for β€” restarting, replacing, and spreading them without you watching.

WordMeaning
ClusterA set of machines Kubernetes manages as one
NodeOne machine (real or virtual) in the cluster
Control planeThe "brain": stores what you asked for and works to make it true
PodThe smallest thing Kubernetes runs: one or more containers sharing a network address
ManifestA YAML file describing what you want ("3 copies of this app")
kubectlThe command-line tool you use to talk to the cluster ("cube control")

The key idea is desired state: you declare what you want; Kubernetes keeps comparing it with what is and fixes any difference β€” this loop is called reconciliation.

Try it: run kubectl get nodes -o wide and find your node's container runtime and Kubernetes version.

2. kubectl basics​

In short: almost every command is kubectl <verb> <type> [name].

kubectl get nodes                        # list things of one type
kubectl get pods -A # pods in every namespace (-A = all)
kubectl get pods -n kube-system -o wide # more columns (node, IP)
kubectl api-resources | head -5 # every type of object the cluster knows
kubectl explain deployment.spec.replicas # built-in documentation for any field
kubectl version --client # your kubectl version
VerbDoes
getList objects (-o yaml shows the full object)
describeHuman-readable details, plus recent events β€” start debugging here
apply -f file.yamlCreate or update objects from a file
deleteRemove objects
logs / execRead a container's output / run a command inside it

Short names save typing: po (pods), deploy, svc (services), ns (namespaces), cm (configmaps).

Try it: use kubectl explain pod.spec.containers.image to read what the image field means.

3. Pods​

In short: a pod wraps one or more containers. You rarely create pods directly, but every app runs inside them, so you'll read and debug them all the time.

kubectl run web --image=nginx:1.27                # create one pod running nginx
kubectl wait --for=condition=Ready pod/web # wait until it's up
kubectl get pods # web 1/1 Running
kubectl describe pod web | tail -5 # Events: Scheduled, Pulled, Created, Started
kubectl logs web # the container's output
kubectl exec web -- nginx -v # run a command inside: nginx version: nginx/1.27…
kubectl delete pod web # gone for good β€” nothing brings it back

For an interactive shell inside a pod: kubectl exec -it web -- sh.

A pod on its own is fragile: if it's deleted or its node dies, it isn't replaced. That's what Deployments are for.

Try it: run a busybox:1.36 pod with the command sleep 3600 (kubectl run box --image=busybox:1.36 -- sleep 3600), then exec into it and look at /etc/hosts.

4. Deployments​

In short: a Deployment keeps a chosen number of identical pods running, and replaces any that disappear.

kubectl create deployment web --image=nginx:1.27 --replicas=3
kubectl rollout status deployment/web # waits: "successfully rolled out"
kubectl get deploy,rs,pods -l app=web # deployment β†’ replicaset β†’ 3 pods

kubectl delete "$(kubectl get pods -l app=web -o name | head -1)" # kill one pod…
kubectl rollout status deployment/web
kubectl get pods -l app=web # …and there are 3 again, one of them new

kubectl scale deployment web --replicas=5 # more copies
kubectl rollout status deployment/web
kubectl get deployment web # READY 5/5

A Deployment manages a ReplicaSet, which manages the pods. You work with the Deployment; the ReplicaSet is how it does rolling updates.

Try it: scale to 0 replicas and back to 2. What happens to the pods in between?

5. YAML manifests​

In short: real projects describe objects in YAML files kept in git, and use kubectl apply β€” running it again only applies what changed.

# web.yaml
apiVersion: apps/v1 # which API group and version this type belongs to
kind: Deployment # the type of object
metadata:
name: web
spec:
replicas: 2
selector:
matchLabels:
app: web # "the pods I manage are the ones labelled app=web"
template: # the pod to create β€” its labels must match the selector
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
kubectl apply -f web.yaml                 # deployment.apps/web created
kubectl apply -f web.yaml # deployment.apps/web unchanged
kubectl get deployment web -o yaml | head -20 # what the cluster stored (with defaults filled in)
kubectl delete -f web.yaml # delete everything the file describes

Every object has the same four top-level parts: apiVersion, kind, metadata (name, labels), and spec (what you want). The cluster adds status (what is). kubectl diff -f web.yaml previews a change before you apply it.

Try it: change replicas to 3 in the file and apply again. Which pods does Kubernetes create or keep?

6. Services​

In short: pods come and go and their IP addresses change. A Service gives a group of pods one stable name and address, and spreads requests across them.

kubectl apply -f web.yaml
kubectl expose deployment web --port 80 # a Service named "web" in front of the pods
kubectl get service web # TYPE ClusterIP, a stable internal IP

kubectl run client --image=busybox:1.36 --rm -i --restart=Never -- \
wget -qO- http://web | grep -o "<title>.*</title>" # other pods reach it by name: <title>Welcome to nginx!</title>

kubectl port-forward service/web 8080:80 > /dev/null & # reach it from your laptop
sleep 2 && curl -s localhost:8080 | grep -o "<title>.*</title>"
kill %1 # stop the port-forward
TypeReachable fromUse for
ClusterIP (default)Inside the cluster onlyTalking between your services
NodePortEvery node's IP, on a port 30000–32767Simple testing
LoadBalancerA cloud load balancer's public addressExposing a service on AWS/GCP/Azure

Inside the cluster, the full name is web.practice.svc.cluster.local (service Β· namespace Β· …); web alone works from the same namespace.

Try it: scale web to 3 and run the busybox wget several times β€” the Service spreads the requests across pods.

7. Labels & selectors​

In short: labels are key-value tags on objects; selectors pick objects by their labels. Deployments and Services find their pods this way.

kubectl apply -f web.yaml
kubectl get pods --show-labels # app=web, pod-template-hash=…
kubectl get pods -l app=web # only pods with app=web
kubectl get pods -l 'app in (web, api)' # either value
kubectl label deployment web team=shop # add a label
kubectl get deployments -l team=shop
kubectl get endpointslices -l kubernetes.io/service-name=web # which pod IPs a Service sends to

A Service sends traffic to every pod whose labels match its selector. If a Service shows no endpoints, its selector doesn't match any ready pod β€” compare kubectl describe service <name> with the pods' labels.

Try it: start a lone pod labelled app=web (kubectl run extra --image=nginx:1.27 --labels=app=web). Does the web Service now send traffic to it too?

8. Namespaces & contexts​

In short: namespaces divide one cluster into separate spaces (per team or environment); a context in your kubeconfig says which cluster, user, and default namespace kubectl uses.

kubectl get namespaces                            # default, kube-system, practice, …
kubectl create namespace team-a
kubectl run web --image=nginx:1.27 -n team-a # the same name can exist in each namespace
kubectl get pods -n team-a

kubectl config get-contexts # every cluster you can talk to; * = current
kubectl config current-context # kind-learn
kubectl config set-context --current --namespace practice # change your default namespace

Your settings live in ~/.kube/config (the kubeconfig). Always check the current context before running a command that changes things β€” it's the Kubernetes version of "am I on production?".

Try it: delete the team-a namespace. What happened to the pod inside it?

Part 2 β€” Core​

9. ConfigMaps & Secrets​

In short: keep settings out of images. ConfigMaps hold ordinary settings; Secrets hold passwords and keys. Both reach containers as environment variables or files.

# config-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: config-demo
spec:
restartPolicy: Never
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "echo $GREETING $MODE $DB_PASSWORD"]
envFrom:
- configMapRef:
name: web-config # every key becomes an environment variable
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret # one key from a Secret
key: PASSWORD
kubectl create configmap web-config --from-literal=GREETING=hello --from-literal=MODE=dev
kubectl create secret generic db-secret --from-literal=PASSWORD=s3cret
kubectl get secret db-secret -o jsonpath='{.data.PASSWORD}' | base64 -d; echo # s3cret β€” anyone who can read it can decode it
kubectl apply -f config-demo.yaml
kubectl wait --for=jsonpath='{.status.phase}'=Succeeded pod/config-demo
kubectl logs config-demo # hello dev s3cret

Secrets are only base64-encoded, not encrypted β€” anyone who can read them in the cluster can decode them. Limit who can read Secrets (RBAC), turn on encryption at rest, or use an external secret store.

Try it: mount the ConfigMap as files instead (volumes: - name: cfg / configMap: {name: web-config} and a volumeMount), and cat one of the files.

10. Health checks (probes)​

In short: probes let Kubernetes ask your app "are you ready?" and "are you alive?" β€” it only sends traffic to ready pods, and restarts pods that stop being alive.

# probed.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: probed
spec:
replicas: 1
selector:
matchLabels: {app: probed}
template:
metadata:
labels: {app: probed}
spec:
containers:
- name: nginx
image: nginx:1.27
readinessProbe: # failing β†’ removed from the Service, not restarted
httpGet: {path: /, port: 80}
periodSeconds: 5
livenessProbe: # failing β†’ container restarted
httpGet: {path: /, port: 80}
initialDelaySeconds: 5
periodSeconds: 10
kubectl apply -f probed.yaml
kubectl rollout status deployment/probed
kubectl get pods -l app=probed # READY 1/1
ProbeQuestionWhen it fails
startupProbeHas it finished starting?Other probes wait; restarted if it never starts
readinessProbeCan it take traffic right now?Taken out of the Service until it passes again
livenessProbeIs it stuck?Container is restarted

Keep liveness checks simple (the process responds) β€” don't check the database in a liveness probe, or a database blip restarts every pod at once.

Try it: change the readiness path to /missing and apply. The pod stays Running but shows 0/1 READY β€” and the rollout never finishes.

11. Requests & limits​

In short: requests are what a container is guaranteed (used to pick a node); limits are the most it may use.

# limited.yaml
apiVersion: v1
kind: Pod
metadata:
name: limited
spec:
restartPolicy: Never
containers:
- name: hog
image: busybox:1.36
command: ["sh", "-c", "head -c 300m /dev/zero | tail"] # tries to hold 300 MB in memory
resources:
requests: {cpu: 100m, memory: 64Mi} # 100m = 0.1 of a CPU core
limits: {cpu: 500m, memory: 100Mi}
kubectl apply -f limited.yaml
kubectl wait --for=jsonpath='{.status.phase}'=Failed pod/limited --timeout=60s
kubectl get pod limited -o jsonpath='{.status.containerStatuses[0].state.terminated.reason}'; echo # OOMKilled
kubectl get pod limited -o jsonpath='{.status.qosClass}'; echo # Burstable
Over the…Result
Memory limitContainer killed: OOMKilled, exit code 137
CPU limitSlowed down ("throttled"), not killed
Requests of every nodePod stays Pending β€” nowhere to fit

QoS (Quality of Service) class: requests = limits β†’ Guaranteed; requests below limits β†’ Burstable; neither set β†’ BestEffort, the first to be evicted when a node runs short of memory.

Try it: raise the memory limit to 400Mi and apply again (delete the pod first). Does it still get killed?

12. Rolling updates & rollbacks​

In short: changing a Deployment's pod template starts a rolling update β€” new pods start before old ones stop, so the app stays up. A bad rollout can be undone with one command.

kubectl apply -f web.yaml
kubectl rollout status deployment/web

kubectl set image deployment/web nginx=nginx:1.27-alpine # container "nginx" gets a new image
kubectl rollout status deployment/web # replaces pods a few at a time
kubectl rollout history deployment/web # revisions 1 and 2

kubectl rollout undo deployment/web # back to the previous revision
kubectl rollout status deployment/web
kubectl get deployment web -o jsonpath='{.spec.template.spec.containers[0].image}'; echo # nginx:1.27
  strategy:                       # under the Deployment's spec
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # at most 1 extra pod during the update
maxUnavailable: 0 # never drop below the desired count

A rollout only replaces old pods when new ones pass their readiness probe β€” that's what stops a broken version taking all the traffic.

Try it: set the image to nginx:no-such-tag. The new pod shows ImagePullBackOff, the old pods keep serving, and rollout undo fixes it.

13. Storage​

In short: a container's files vanish when it restarts. A PersistentVolumeClaim (PVC) asks the cluster for storage that outlives pods.

# storage.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: notes
spec:
accessModes: [ReadWriteOnce] # one node at a time can write
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: Pod
metadata:
name: writer
spec:
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "echo saved-at-$(date +%s) >> /data/notes.txt; sleep 3600"]
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: notes
kubectl apply -f storage.yaml
kubectl wait --for=condition=Ready pod/writer --timeout=90s
kubectl get pvc notes # STATUS Bound
kubectl delete pod writer
kubectl apply -f storage.yaml # a brand-new pod, same claim
kubectl wait --for=condition=Ready pod/writer --timeout=90s
kubectl exec writer -- cat /data/notes.txt # two lines β€” the first pod's line survived

A StorageClass decides what real storage is created (a cloud disk, a local folder); kubectl get storageclass shows yours. Deleting a PVC usually deletes the data too.

14. Jobs & CronJobs​

In short: a Job runs pods until a task finishes (migrations, reports); a CronJob creates a Job on a schedule.

kubectl create job hello --image=busybox:1.36 -- sh -c 'echo job done'
kubectl wait --for=condition=complete job/hello --timeout=60s
kubectl logs job/hello # job done

kubectl create cronjob tick --image=busybox:1.36 --schedule='*/5 * * * *' -- date
kubectl get cronjob tick # SCHEDULE */5 * * * *
kubectl create job tick-now --from=cronjob/tick # run it once, now
kubectl wait --for=condition=complete job/tick-now --timeout=60s

The schedule uses cron syntax: minute, hour, day of month, month, day of week β€” */5 * * * * means "every 5 minutes". Set backoffLimit (retries) and activeDeadlineSeconds (a time limit) on real Jobs.

Try it: create a Job whose command fails (exit 1) and watch kubectl get pods -l job-name=<name> β€” how many times does it retry?

15. Ingress​

In short: an Ingress routes outside HTTP traffic to Services by host name and path β€” one entry point for many services. It needs an ingress controller (such as ingress-nginx or Traefik) installed in the cluster.

# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop
spec:
ingressClassName: nginx
rules:
- host: shop.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service: {name: api, port: {number: 80}}
- path: /
pathType: Prefix
backend:
service: {name: web, port: {number: 80}}
tls:
- hosts: [shop.example.com]
secretName: shop-tls # a TLS certificate stored as a Secret

The newer Gateway API (Gateway and HTTPRoute objects) does the same job with more features and is replacing Ingress in many clusters.

16. Troubleshooting​

In short: get shows what is wrong, describe shows why (events at the bottom), and logs --previous shows what a crashed container said.

kubectl run crash --image=busybox:1.36 -- sh -c 'echo "config missing"; exit 1'
sleep 20
kubectl get pod crash # STATUS CrashLoopBackOff (or Error), RESTARTS 1+
kubectl logs crash # config missing β€” output of the last run

kubectl run badimg --image=nginx:no-such-tag
sleep 10
kubectl get pod badimg # ErrImagePull / ImagePullBackOff
kubectl describe pod badimg | grep -m1 -i "failed to pull"

kubectl get events --sort-by=.lastTimestamp | tail -5 # the cluster's recent history
StatusUsual causeLook at
PendingNo node has enough CPU/memory, or no volumedescribe pod β†’ Events ("Insufficient cpu")
ImagePullBackOffWrong image name/tag, or no access to a private registrydescribe pod β†’ Events
CrashLoopBackOffThe app starts and exits β€” bad config, missing secret, crashlogs (add --previous once it's running again)
Running but 0/1 READYReadiness probe failingdescribe pod β†’ probe messages
OOMKilledOver the memory limitRaise the limit, or fix the leak
Service unreachableSelector doesn't match, wrong targetPortget endpointslices, describe service

17. Common mistakes​

In short: the classic Kubernetes traps, and the fix for each.

MistakeFix
Using the latest image tagPin versions; latest makes rollouts and rollbacks unpredictable
No requests or limitsSet both β€” otherwise one pod can starve a whole node
No readiness probeTraffic hits pods before they're ready; bad rollouts take everything down
Selector and pod labels don't matchThe Service has no endpoints; the Deployment is rejected
kubectl edit in productionChange the YAML in git and apply, so the files stay the truth
Forgetting -n / wrong contextCheck kubectl config current-context before changing anything
Secrets in git as plain YAMLThey're only base64 β€” use Sealed Secrets, SOPS, or an external secret store
Liveness probe checks dependenciesA database outage then restarts every pod; check only the process itself
One replica of an important appRun at least 2, and add a PodDisruptionBudget

Part 3 β€” Advanced​

18. StatefulSets​

In short: a StatefulSet runs pods that each need a stable name and their own storage β€” databases, queues β€” created and removed in order.

# statefulset.yaml
apiVersion: v1
kind: Service
metadata:
name: db
spec:
clusterIP: None # "headless": a DNS name per pod instead of one shared IP
selector: {app: db}
ports: [{port: 80}]
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: db
spec:
serviceName: db
replicas: 2
selector:
matchLabels: {app: db}
template:
metadata:
labels: {app: db}
spec:
containers:
- name: app
image: busybox:1.36
command: ["sleep", "3600"]
kubectl apply -f statefulset.yaml
kubectl rollout status statefulset/db
kubectl get pods -l app=db -o name # pod/db-0 pod/db-1 β€” fixed, ordered names
kubectl exec db-0 -- nslookup db-1.db.practice.svc.cluster.local | grep Name
# Name: db-1.db.practice.svc.cluster.local β€” each pod has its own DNS name

The name is pod Β· service Β· namespace; apps in the same namespace can use just db-1.db (busybox's nslookup needs the full name). Add volumeClaimTemplates to give each pod its own PVC, which stays attached to that pod name when it's replaced. For production databases, a managed service or an operator is usually safer than your own StatefulSet.

19. DaemonSets​

In short: a DaemonSet runs one copy of a pod on every node (or every matching node) β€” for log collectors, monitoring agents, and network plugins.

# daemonset.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-agent
spec:
selector:
matchLabels: {app: node-agent}
template:
metadata:
labels: {app: node-agent}
spec:
containers:
- name: agent
image: busybox:1.36
command: ["sh", "-c", "while true; do echo checking $(hostname); sleep 30; done"]
kubectl apply -f daemonset.yaml
kubectl rollout status daemonset/node-agent
kubectl get daemonset node-agent # DESIRED = CURRENT = number of nodes (1 on kind)

When a node joins the cluster, the DaemonSet adds a pod to it automatically.

20. Scheduling: taints & affinity​

In short: the scheduler picks a node for each pod. Taints keep pods off a node unless they tolerate it; affinity and nodeSelector pull pods towards certain nodes.

NODE=$(kubectl get nodes -o jsonpath='{.items[0].metadata.name}')
kubectl taint nodes "$NODE" dedicated=gpu:NoSchedule # only pods that tolerate this may land here

kubectl run plain --image=nginx:1.27
sleep 5
kubectl get pod plain -o jsonpath='{.status.phase}'; echo # Pending β€” nothing tolerates the taint

kubectl run tolerant --image=nginx:1.27 --overrides='{"spec":{"tolerations":[{"key":"dedicated","operator":"Equal","value":"gpu","effect":"NoSchedule"}]}}'
kubectl wait --for=condition=Ready pod/tolerant --timeout=60s
kubectl get pod tolerant -o jsonpath='{.status.phase}'; echo # Running

kubectl taint nodes "$NODE" dedicated=gpu:NoSchedule- # remove the taint (note the -)
kubectl wait --for=condition=Ready pod/plain --timeout=60s # now it schedules too
ToolUse it to
nodeSelector: {disktype: ssd}Run only on nodes with a label
Node affinityThe same, with "preferred" as well as "required" rules
Pod anti-affinitySpread replicas across nodes or zones
Taints + tolerationsReserve nodes (GPUs, a team)
topologySpreadConstraintsSpread pods evenly across zones

21. Autoscaling​

In short: a HorizontalPodAutoscaler (HPA) changes a Deployment's replica count to keep average CPU (or another metric) near a target.

kubectl apply -f web.yaml
kubectl set resources deployment web --requests=cpu=100m # HPA percentages are of the request
kubectl autoscale deployment web --cpu=70% --min=2 --max=5
kubectl get hpa web # TARGETS cpu: <unknown>/70% until metrics exist

The HPA needs the metrics-server add-on (built into most cloud clusters; install it yourself on kind). Other autoscalers:

AutoscalerChanges
HPANumber of pods
VPA (Vertical Pod Autoscaler)Each pod's requests and limits
Cluster Autoscaler / KarpenterNumber of nodes
KEDAPods, from queue length and other event sources β€” even to zero

22. Access control (RBAC)​

In short: RBAC (Role-Based Access Control) decides who may do what: a Role lists allowed actions, and a RoleBinding gives that Role to a user or ServiceAccount (an identity for pods).

kubectl create serviceaccount reader
kubectl create role pod-reader --verb=get,list,watch --resource=pods
kubectl create rolebinding reader-can-read --role=pod-reader --serviceaccount=practice:reader

kubectl auth can-i list pods --as=system:serviceaccount:practice:reader # yes
kubectl auth can-i delete pods --as=system:serviceaccount:practice:reader # βœ— no β€” exit code 1, handy in scripts
ObjectScope
Role / RoleBindingOne namespace
ClusterRole / ClusterRoleBindingThe whole cluster

Give each app its own ServiceAccount with only the permissions it needs ("least privilege"), and set automountServiceAccountToken: false for pods that never call the Kubernetes API.

23. Network policies​

In short: by default every pod can talk to every other pod. A NetworkPolicy limits that β€” for example, "only the web pods may reach the database".

# netpol.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: db-only-from-web
spec:
podSelector:
matchLabels: {app: db} # this policy protects the db pods
policyTypes: [Ingress]
ingress:
- from:
- podSelector:
matchLabels: {role: web} # only pods labelled role=web may connect
ports:
- port: 5432

Policies are enforced by the cluster's network plugin (CNI β€” Container Network Interface). Calico and Cilium enforce them; some simple plugins silently ignore them, so always test that a blocked connection really fails. A common starting point is a "deny all ingress" policy per namespace, then explicit allows.

24. Kustomize & Helm​

In short: real apps need the same manifests with small differences per environment. Kustomize (built into kubectl) patches plain YAML; Helm fills in templates and installs them as versioned "releases".

# kustomization.yaml
resources: # this file sits next to web.yaml
- web.yaml
namePrefix: staging-
labels:
- pairs: {env: staging}
includeSelectors: false
images:
- name: nginx
newTag: 1.27-alpine # override the image tag without editing web.yaml
kubectl kustomize . | grep -E "name: staging-web|image:"   # preview: name: staging-web, image: nginx:1.27-alpine
kubectl apply -k . # apply the customised result
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install my-db bitnami/postgresql --set auth.postgresPassword=pw # install a "chart"
helm list # installed releases
helm upgrade my-db bitnami/postgresql --set primary.resources.limits.memory=512Mi
helm rollback my-db 1

Use Kustomize for your own apps with a few environment differences; use Helm to install third-party software, or when you need real templating.

25. How a cluster works​

In short: the control plane stores the desired state and makes decisions; each node runs the pods it's given and reports back.

ComponentRuns onDoes
kube-apiserverControl planeThe front door: every kubectl command and component talks to it
etcdControl planeThe database holding every object
kube-schedulerControl planePicks a node for each new pod
kube-controller-managerControl planeRuns the loops that make reality match the spec (Deployments, Jobs…)
kubeletEvery nodeStarts and watches the pods assigned to its node
kube-proxy / CNI pluginEvery nodeMakes Service addresses and pod networking work
Container runtimeEvery nodeActually runs containers (containerd, CRI-O)
kubectl get pods -n kube-system        # on kind you can see most of these as pods

When you apply a Deployment: the API server stores it in etcd β†’ the Deployment controller creates a ReplicaSet β†’ the ReplicaSet controller creates pods β†’ the scheduler assigns nodes β†’ each kubelet starts the containers.

26. Words you'll meet​

In short: the jargon, in one line each.

WordMeaning
WorkloadAnything that runs pods: Deployment, StatefulSet, DaemonSet, Job
ReplicaOne copy of a pod
ControllerA loop that watches objects and acts to reach their desired state
OperatorA controller that knows how to run one piece of software (a database, a queue)
CRDCustom Resource Definition β€” adds a new object type to the cluster
SidecarA helper container in the same pod (a proxy, a log shipper)
Init containerRuns to completion before the main containers start
PDBPodDisruptionBudget β€” how many pods may be down during maintenance
Service meshA layer (Istio, Linkerd) adding encryption, retries, and traffic control between services
GitOpsThe cluster continuously syncs itself from YAML in git (Argo CD, Flux)
EKS / GKE / AKSManaged Kubernetes on AWS / Google Cloud / Azure

For the architecture in depth, scheduling details, and interview questions, see the complete guide.