Sets and Controllers
Kubernetes organizes Pods into logical “Sets” and then uses specialized Controllers to manage their lifecycle. At its core, a Set is simply a group of resources most commonly Pods—that share a common purpose. Under the hood, Controllers constantly observe the cluster’s actual state and compare it to your declared desired state, spinning up or tearing down Pods to match. Let’s walk through each major Set type, starting with the foundational ReplicaSet.
apiVersion: apps/v1 kind: ReplicaSet metadata: name: my-app-replicaset spec: replicas: 3 selector: matchLabels: app: my-app template: metadata: labels: app: my-app spec: containers: - name: my-app-container image: nginx:1.23
A ReplicaSet ensures exactly N identical Pods exist at all times. Think of it as your application’s insurance policy if a Pod crashes or is evicted, the ReplicaSet immediately spins up a replacement. In practice, you rarely hand-craft ReplicaSets; instead, you declare a Deployment, and Kubernetes creates a ReplicaSet on your behalf. Deployments add rolling-update, rollback, and pause-resume capabilities, orchestrating ReplicaSets behind the scenes.
Moving beyond stateless replicas, a StatefulSet adds stable identities and durable storage to each Pod. It’s the go-to for databases, message queues, or any service that needs a persistent disk and predictable network name (e.g. pod-0, pod-1, ...). StatefulSet Pods start and terminate in a strict order, guaranteeing data consistency across restarts.
apiVersion: apps/v1 kind: StatefulSet metadata: name: my-stateful-app spec: serviceName: "my-stateful-service" replicas: 3 selector: matchLabels: app: my-app template: metadata: labels: app: my-app spec: containers: - name: my-app-container image: redis:latest volumeMounts: - name: my-app-storage mountPath: /data volumeClaimTemplates: - metadata: name: my-app-storage spec: accessModes: ["ReadWriteOnce"] resources: requests: storage: 1Gi
Where StatefulSets guarantee one-to-one identity with storage, a DaemonSet guarantees one-to-one Pod distribution across nodes. Perfect for cluster-wide agents like log shippers or monitoring daemons—any time a node joins, the DaemonSet Controller schedules a new Pod there; when a node leaves, the Pod follows.
apiVersion: apps/v1 kind: DaemonSet metadata: name: my-logging-agent spec: selector: matchLabels: app: logging-agent template: metadata: labels: app: logging-agent spec: containers: - name: my-logging-agent-container image: fluentd:latest
Not all work is ongoing, though. Jobs handle finite, one-off tasks: database migrations, ETL processes, or batch imports. A Job creates Pods that run to completion, retrying until success or hitting a backoffLimit.
apiVersion: batch/v1 kind: Job metadata: name: my-job spec: template: spec: containers: - name: my-job-container image: busybox command: ["echo", "Hello Kubernetes!"] restartPolicy: Never backoffLimit: 4
And when you need that batch to recur on a schedule—say nightly backups or weekly reports—you reach for a CronJob. It layers cron-style scheduling atop the Job controller, spinning up Jobs at your chosen times.
apiVersion: batch/v1 kind: CronJob metadata: name: my-cronjob spec: schedule: "0 0 * * *" jobTemplate: spec: template: spec: containers: - name: my-cronjob-container image: busybox command: ["echo", "Daily Backup"] restartPolicy: OnFailure
In every case, the pattern is the same: you declare the desired state in YAML, Kubernetes controllers watch the cluster, and they act in a continuous control loop to reconcile reality with your specification. Understanding how Sets and Controllers pair up lets you choose the right abstraction for stateless services, stateful workloads, cluster-wide agents, one-off tasks, or scheduled jobs—giving you both flexibility and reliability in managing your applications.