# Pods and Deployments

## 1. Pods

A **Pod** is the smallest deployable unit: one or more containers that share a network namespace (one IP, `localhost` between them), volumes and lifecycle, always scheduled together on one node. Most Pods run one app container, sometimes with **sidecars** (log shippers, proxies) or **init containers** (run to completion before the app starts — migrations, waiting for dependencies).

```yaml
apiVersion: v1
kind: Pod
metadata: { name: api, labels: { app: api } }
spec:
  initContainers:
    - name: wait-for-db
      image: busybox:1.36
      command: ["sh", "-c", "until nc -z postgres 5432; do sleep 2; done"]
  containers:
    - name: api
      image: ghcr.io/petpal/api:1.4.2
      ports: [{ containerPort: 8080, name: http }]
      resources:
        requests: { cpu: 250m, memory: 256Mi }   # used for scheduling
        limits:   { memory: 512Mi }              # OOMKilled above this
      readinessProbe:
        httpGet: { path: /ready, port: http }
        periodSeconds: 5
      livenessProbe:
        httpGet: { path: /health, port: http }
        initialDelaySeconds: 10
        periodSeconds: 10
        failureThreshold: 3
      securityContext:
        runAsNonRoot: true
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        capabilities: { drop: ["ALL"] }
```

### Resources

- **requests**: what the scheduler reserves; the basis for CPU shares and autoscaling percentages.
- **limits**: CPU limits **throttle**; memory limits **kill** (OOMKilled, exit code 137).
- **QoS classes**: `Guaranteed` (requests = limits for all), `Burstable`, `BestEffort` (none) — BestEffort is evicted first under node pressure.
- Common practice: always set requests; set a memory limit; be careful with CPU limits (throttling hurts latency).

### Probes

| Probe | Failing means | Effect |
|---|---|---|
| **startup** | still starting | other probes wait (slow starters) |
| **readiness** | can't serve traffic now | removed from Service endpoints (not restarted) |
| **liveness** | stuck/deadlocked | container **restarted** |

Liveness must check only the process itself — never dependencies like the database, or a DB blip restarts every Pod.

### Pod lifecycle

Phases: `Pending` → `Running` → `Succeeded`/`Failed`. Container states: Waiting (`ContainerCreating`, `ImagePullBackOff`, `CrashLoopBackOff`), Running, Terminated. **Restart policy** `Always` (Deployments), `OnFailure`/`Never` (Jobs). On termination Kubernetes sends **SIGTERM**, waits `terminationGracePeriodSeconds` (30 s default) and then SIGKILLs — apps must handle SIGTERM by finishing in-flight requests (`devops-cloud/docker/` on PID 1 signal handling).

## 2. Deployments

A **Deployment** manages **ReplicaSets**, which keep N identical Pods running. Changing the Pod template (new image, env, resources) creates a new ReplicaSet and performs a **rolling update**:

```yaml
apiVersion: apps/v1
kind: Deployment
metadata: { name: api }
spec:
  replicas: 4
  revisionHistoryLimit: 5
  selector: { matchLabels: { app: api } }
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1          # at most 5 Pods during rollout
      maxUnavailable: 0    # never fewer than 4 ready
  minReadySeconds: 10
  progressDeadlineSeconds: 300
  template:
    metadata: { labels: { app: api } }
    spec:
      containers:
        - name: api
          image: ghcr.io/petpal/api:1.4.3
```

```bash
kubectl set image deploy/api api=ghcr.io/petpal/api:1.4.3   # (prefer editing the file + apply)
kubectl rollout status deploy/api
kubectl rollout history deploy/api
kubectl rollout undo deploy/api --to-revision=3
kubectl scale deploy/api --replicas=6
kubectl rollout restart deploy/api        # recreate Pods (e.g. to pick up a changed Secret)
```

`Recreate` strategy kills all old Pods first (downtime; for apps that can't run two versions). Canary and blue/green need extra tooling (Argo Rollouts, Flagger, or two Deployments + traffic splitting — `devops-cloud/cicd/`).

## 3. Jobs, CronJobs, DaemonSets

- **Job**: run Pods to completion (`completions`, `parallelism`, `backoffLimit`, `activeDeadlineSeconds`, `ttlSecondsAfterFinished`).
- **CronJob**: Jobs on a schedule (`schedule: "0 3 * * *"`, `concurrencyPolicy: Forbid`, `timeZone`).
- **DaemonSet**: one Pod per (matching) node — node exporters, log agents, CNI.

## Under the Hood: How a Rolling Update Proceeds

The Deployment controller computes a hash of the Pod template (`pod-template-hash` label) and creates a new ReplicaSet for it. It then scales the new ReplicaSet up and the old one down in steps bounded by `maxSurge` (extra Pods allowed above `replicas`) and `maxUnavailable` (Pods allowed to be not-ready). With 4 replicas, surge 1, unavailable 0: create 1 new Pod (5 total) → wait until it's **Ready** for `minReadySeconds` → scale old to 3 → create another new Pod → … Readiness probes gate every step, so a broken version stalls the rollout with old Pods still serving; if no progress happens within `progressDeadlineSeconds`, the Deployment condition becomes `Progressing=False` (CI should fail and roll back). Old ReplicaSets are kept at 0 replicas (up to `revisionHistoryLimit`) — `rollout undo` simply scales an old one back up.

![A rolling update with four replicas, maxSurge 1 and maxUnavailable 0: the new ReplicaSet gains one ready Pod at a time while the old ReplicaSet loses one, gated by readiness probes, until all four Pods run the new version](diagrams/01-rolling-update.svg)

## Common Pitfalls

1. **No readiness probe** — traffic hits Pods before they're ready; rollouts can't detect broken versions.
2. **Liveness probe checking dependencies** — cascading restarts.
3. **No requests** — bad scheduling, noisy neighbours, broken autoscaling.
4. **`:latest` tags** — no rollbacks, unpredictable nodes.
5. **Ignoring SIGTERM** — dropped requests during every deploy.
6. **Running as root with a writable filesystem**.

## Quick Reference

```
Pod: containers + init + sidecars share IP/volumes · requests (schedule) vs limits (CPU throttle, mem OOMKill)
probes: startup · readiness (endpoints) · liveness (restart; no deps)
Deployment → ReplicaSet → Pods · RollingUpdate maxSurge/maxUnavailable · rollout status/history/undo
Job · CronJob · DaemonSet · SIGTERM → grace period → SIGKILL
