Stage 5 · Platform
Workloads & Scheduling
Built-in Controllers
Deployment, StatefulSet, DaemonSet, Job, CronJob — internals.
Controller Model
Every controller follows the same pattern: watch for events, compare desired state to actual state, and take action to converge them. This is the reconciliation loop. Controllers never push changes — they pull the current state and act to make reality match intent.
for {
event := watch.nextEvent()
current := getActualState(event.object)
desired := event.object.spec
if current != desired {
reconcile(current, desired)
}
}This loop runs continuously for every resource the controller manages. The controller reads the spec (desired state) from the API server and compares it to the status (actual state), then takes action to close the gap.
Deployment
Deployments manage ReplicaSets and provide rolling updates, rollbacks, and scaling. When you update a Deployment, it creates a new ReplicaSet with the updated template, scales it up, and scales down the old ReplicaSet. This provides zero-downtime deployments.
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
labels:
app: web-app
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # One extra pod during update
maxUnavailable: 0 # Never reduce below desired count
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
spec:
containers:
- name: web
image: nginx:1.25
ports:
- containerPort: 80
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 0
periodSeconds: 5
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "250m"
memory: "256Mi"maxSurge:1 means one extra pod is created before one old pod is terminated. maxUnavailable:0 means the desired replica count is never reduced. Together, they ensure zero downtime during updates.
maxSurge controls how many extra pods can run during update. maxUnavailable controls how many pods can be missing. With maxSurge:1, maxUnavailable:0, you always have at least desired replicas and at most desired+1. For critical workloads, prefer maxUnavailable:0.
StatefulSet
StatefulSets provide stable network identities and persistent storage for stateful applications like databases. Pods are created in order (pod-0, pod-1, pod-2) and deleted in reverse order. Each pod gets a persistent volume and a predictable hostname.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres
replicas: 3
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:16
ports:
- containerPort: 5432
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-credentials
key: password
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: fast-ssd
resources:
requests:
storage: 10GiEach pod gets a unique PVC named data-postgres-0, data-postgres-1, data-postgres-2. These persist across pod restarts. When pods are deleted, their PVCs are NOT deleted — you must manage cleanup separately.
DaemonSet
DaemonSets ensure exactly one pod runs on every node (or a subset). They are used for node-level agents: logging, monitoring, networking (CNI plugins), and storage (CSI drivers). Pods are automatically scheduled on new nodes as they join.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-exporter
namespace: monitoring
spec:
selector:
matchLabels:
app: node-exporter
template:
metadata:
labels:
app: node-exporter
spec:
tolerations:
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
hostNetwork: true
hostPID: true
containers:
- name: node-exporter
image: prom/node-exporter:v1.7.0
args:
- --path.procfs=/host/proc
- --path.sysfs=/host/sys
- --path.rootfs=/host/root
volumeMounts:
- name: proc
mountPath: /host/proc
readOnly: true
- name: sys
mountPath: /host/sys
readOnly: true
- name: root
mountPath: /host/root
mountPropagation: HostToContainer
readOnly: trueThe tolerations let the pod run on control plane nodes. hostNetwork and hostPID give access to host networking and process information. volumeMounts with HostToContainer propagation let the exporter see host filesystem mounts.
Job and CronJob
Jobs run pods to completion. They are used for batch workloads: data processing, migrations, backups. CronJobs schedule Jobs on a cron expression. Both support parallelism, completions, and backoff limits.
apiVersion: batch/v1
kind: CronJob
metadata:
name: db-backup
spec:
schedule: "0 2 * * *" # 2 AM daily
concurrencyPolicy: Forbid # Don't run if previous still running
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 5
startingDeadlineSeconds: 600
jobTemplate:
spec:
backoffLimit: 2
activeDeadlineSeconds: 3600 # 1 hour max
template:
spec:
restartPolicy: Never
containers:
- name: backup
image: postgres:16
command:
- pg_dump
- -h
- postgres-service
- -U
- postgres
- -d
- mydb
env:
- name: PGPASSWORD
valueFrom:
secretKeyRef:
name: postgres-credentials
key: passwordconcurrencyPolicy: Forbid prevents overlapping runs. Allow lets them run in parallel. Replace cancels the currently running job when a new one is scheduled. startingDeadlineSeconds ensures a missed run is still triggered if caught within the window.
ReplicaSet Internals
ReplicaSets maintain a stable set of pod replicas. You rarely create them directly — Deployments create and manage them. A ReplicaSet uses a selector to find pods it owns and creates/deletes pods to match the desired count.
Run kubectl rollout history deployment/web-app to see all revisions. Each revision is a ReplicaSet. Roll back with kubectl rollout undo deployment/web-app --to-revision=<N>. The revision is stored in the deployment's annotation kubernetes.io/change-cause.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.