# Kubernetes Basics — Pods, Deployments, Services, Scaling and Self-Healing Explained

Source: https://skills101.in/learn/cloud-devops/kubernetes-basics — Skills101 free tutorial (Cloud & DevOps). Updated 2026-11-09.

Docker runs containers on **one** machine. But production means **many** containers on **many** machines: who restarts a crashed container at 3 a.m., spreads them across servers, rolls out new versions without downtime, and adds more when traffic spikes? That's **Kubernetes** (often written **K8s**).

## 1. What Kubernetes does for you

You **declare** what you want — *"run 3 copies of `myapp:2.1`, expose them on port 80"* — and Kubernetes keeps reality matching that declaration forever:

- **Self-healing**: a container crashes → it's restarted; a server dies → its containers are recreated elsewhere.
- **Scaling**: change 3 to 10 (or let an autoscaler do it).
- **Rolling updates and rollbacks** with no downtime.
- **Service discovery and load balancing** between containers.
- **Config and secrets** injected at runtime.

## 2. Architecture

![Kubernetes architecture: the control plane (API server, scheduler, controller manager, etcd) manages worker nodes; each node runs a kubelet, kube-proxy and the pods that contain your containers](https://skills101.in/learn-img/cloud-devops/k8s-architecture.svg)

| Part | Role |
|---|---|
| **API server** | the front door; `kubectl` and everything else talk to it |
| **etcd** | the cluster's database of desired and current state |
| **Scheduler** | picks which node a new pod runs on |
| **Controller manager** | control loops that fix differences ("3 wanted, 2 running → start 1") |
| **Node** | a worker machine (VM) |
| **kubelet** | agent on each node that starts and watches containers |
| **kube-proxy** | networking rules for Services |

Managed services — **EKS** (AWS), **AKS** (Azure), **GKE** (Google) — run the control plane for you; you manage the workloads. For learning, run a local cluster with **minikube** or **kind**.

## 3. The core objects

| Object | What it is |
|---|---|
| **Pod** | the smallest unit: one (or a few tightly coupled) containers with a shared IP |
| **Deployment** | "keep N identical pods of this image running"; handles updates/rollbacks |
| **ReplicaSet** | created by a Deployment to keep the pod count; you rarely touch it directly |
| **Service** | a stable name + IP that load-balances to matching pods (pods come and go) |
| **Ingress** | HTTP routing from outside (`api.example.com/` → the API Service), usually with TLS |
| **ConfigMap / Secret** | configuration and sensitive values, injected as env vars or files |
| **Namespace** | a folder for objects (`dev`, `staging`, `prod`) |

![How the objects connect: an Ingress routes outside traffic to a Service, which load-balances across the pods that a Deployment keeps running, selected by matching labels](https://skills101.in/learn-img/cloud-devops/k8s-objects.svg)

The glue is **labels**: the Deployment stamps pods with `app: courses-api`, and the Service sends traffic to every pod with that label.

## 4. A Deployment and a Service in YAML

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: courses-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: courses-api
  template:
    metadata:
      labels:
        app: courses-api
    spec:
      containers:
        - name: api
          image: ghcr.io/skills101/courses-api:4f2c9e1
          ports:
            - containerPort: 3000
          envFrom:
            - configMapRef:
                name: courses-config
            - secretRef:
                name: courses-secrets
          resources:
            requests: { cpu: 100m, memory: 128Mi }
            limits: { cpu: 500m, memory: 256Mi }
          readinessProbe:
            httpGet: { path: /health, port: 3000 }
            periodSeconds: 5
          livenessProbe:
            httpGet: { path: /health, port: 3000 }
            initialDelaySeconds: 10
---
apiVersion: v1
kind: Service
metadata:
  name: courses-api
spec:
  selector:
    app: courses-api
  ports:
    - port: 80
      targetPort: 3000
```

- **requests** = what the scheduler reserves; **limits** = the maximum (a container over its memory limit is killed and restarted).
- **readinessProbe** — "ready for traffic?" Pods failing it are removed from the Service (e.g. while warming up).
- **livenessProbe** — "still alive?" Failing it restarts the container (e.g. stuck in a deadlock).
- `100m` CPU = 0.1 of a core.

Other pods can now call `http://courses-api` — the Service name is a DNS name inside the cluster.

## 5. kubectl: the commands you'll use

```bash
kubectl apply -f k8s/                     # create/update everything in the folder (declarative)
kubectl get pods -o wide                  # pods and which node they're on
kubectl get deploy,svc                    # deployments and services
kubectl describe pod courses-api-7d9c-x2  # events: why is it Pending/CrashLoopBackOff?
kubectl logs -f deploy/courses-api        # logs
kubectl exec -it courses-api-7d9c-x2 -- sh
kubectl scale deploy/courses-api --replicas=6
kubectl set image deploy/courses-api api=ghcr.io/skills101/courses-api:9a8b7c6
kubectl rollout status deploy/courses-api
kubectl rollout undo deploy/courses-api   # roll back to the previous version
```

Common pod states while debugging: **Pending** (no node has room / image pull waiting), **ImagePullBackOff** (wrong image name or no registry access), **CrashLoopBackOff** (the app keeps exiting — check `logs`), **OOMKilled** (memory limit too low or a leak).

## 6. Rolling updates and autoscaling

When the image changes, the Deployment starts new pods, waits until they're **ready**, then removes old ones — a few at a time — so there's no downtime. If the new pods never become ready, the rollout stops and you `rollout undo`.

A **HorizontalPodAutoscaler** adds or removes pods based on load:

```yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: courses-api
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: courses-api
  minReplicas: 3
  maxReplicas: 20
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 60
```

When average CPU goes above 60% of the requested CPU, more pods are added (and nodes too, if a cluster autoscaler is set up).

## 7. Do you even need Kubernetes?

Kubernetes is powerful but complex. A single VPS with Docker Compose or a PaaS is often the right answer for a small product. Kubernetes pays off when you have **many services, several teams, frequent deploys and real scaling needs**. Interviewers like candidates who know this trade-off.

## 8. Case study: the 3 a.m. crash that nobody noticed

An exam-prep API had a memory leak that crashed a container every few hours. On a plain server, each crash meant downtime until someone restarted it. On Kubernetes with 3 replicas and a liveness probe, each crashed pod was restarted in seconds while the other two served traffic — users saw nothing. The team still fixed the leak (they saw the restarts in monitoring), but the system **healed itself** in the meantime.

## Common mistakes

- No readiness probe → traffic is sent to pods that aren't ready yet.
- No resource requests/limits → one pod starves the node.
- Secrets in plain ConfigMaps or in Git (Kubernetes Secrets are only base64-encoded; enable encryption at rest or use a secrets manager).
- Using `latest` tags, so rollouts and rollbacks are unpredictable.
- `kubectl edit` in production instead of changing YAML in Git and re-applying.

## Quick recap

- Kubernetes keeps the **declared** state: self-healing, scaling, rolling updates.
- Control plane (API server, etcd, scheduler, controllers) + worker nodes (kubelet, kube-proxy, pods).
- **Pod** < **Deployment** (replicas, updates) + **Service** (stable name, load balancing) + **Ingress** (HTTP from outside); labels connect them.
- Use readiness/liveness probes, requests/limits, HPA; `kubectl rollout undo` to roll back.


Practice quiz and free certificate: https://skills101.in/learn/cloud-devops/kubernetes-basics