📊 StatefulSets Overview
StatefulSets manage stateful applications that require stable network identities, persistent storage, and ordered deployment/scaling.
Stable Identity
Each pod gets a persistent hostname that survives rescheduling
Ordered Operations
Pods are created, scaled, and deleted in a predictable order
Persistent Storage
Each pod can have its own persistent volume that survives pod restarts
When to Use StatefulSets
| Use Case | Example | Key Requirement |
|---|---|---|
| Databases | MySQL, PostgreSQL, MongoDB | Data persistence, ordered startup |
| Message Queues | Kafka, RabbitMQ | Stable network identity for brokers |
| Distributed Systems | Elasticsearch, Cassandra | Cluster coordination, data sharding |
| Stateful Services | ZooKeeper, etcd | Leader election, consensus |
Creating a StatefulSet
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres-db
spec:
serviceName: postgres-service
replicas: 3
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:14
ports:
- containerPort: 5432
env:
- name: POSTGRES_DB
value: myapp
- name: POSTGRES_USER
value: admin
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secret
key: password
volumeMounts:
- name: postgres-storage
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: postgres-storage
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: fast-ssd
resources:
requests:
storage: 10Gi
<pod-name>.<service-name>.<namespace>.svc.cluster.local
📝 Ordered Deployment & Scaling
Pods are created sequentially: postgres-0 → postgres-1 → postgres-2...
Deployment Strategies
spec:
podManagementPolicy: OrderedReady # Default: sequential
# OR
podManagementPolicy: Parallel # All pods start simultaneously
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 2 # Only update pods with ordinal >= 2
🗄️ Database Workloads
MySQL StatefulSet Example
apiVersion: v1
kind: Service
metadata:
name: mysql-headless
spec:
clusterIP: None
selector:
app: mysql
ports:
- port: 3306
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql-headless
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
initContainers:
- name: init-mysql
image: mysql:8.0
command:
- bash
- "-c"
- |
set -ex
# Generate mysql server-id from pod ordinal
[[ `hostname` =~ -([0-9]+)$ ]] || exit 1
ordinal=${BASH_REMATCH[1]}
echo [mysqld] > /mnt/conf.d/server-id.cnf
echo server-id=$((100 + $ordinal)) >> /mnt/conf.d/server-id.cnf
# Copy config files
cp /mnt/config-map/master.cnf /mnt/conf.d/
cp /mnt/config-map/slave.cnf /mnt/conf.d/
volumeMounts:
- name: conf
mountPath: /mnt/conf.d
- name: config-map
mountPath: /mnt/config-map
containers:
- name: mysql
image: mysql:8.0
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password
ports:
- containerPort: 3306
volumeMounts:
- name: data
mountPath: /var/lib/mysql
- name: conf
mountPath: /etc/mysql/conf.d
livenessProbe:
exec:
command: ["mysqladmin", "ping"]
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
exec:
command: ["mysql", "-h", "127.0.0.1", "-e", "SELECT 1"]
initialDelaySeconds: 5
periodSeconds: 2
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 20Gi
Database Best Practices
🔧 DaemonSets Overview
DaemonSets ensure that all (or some) nodes run a copy of a pod. Perfect for node-level services like log collectors, monitoring agents, and network plugins.
Node Coverage
Automatically deploys to all nodes
Auto-Scaling
Adds pods when new nodes join
Node Selection
Target specific nodes with selectors
Common DaemonSet Use Cases
Log Collection
Fluentd, Logstash, Filebeat running on every node to collect logs
Monitoring
Node exporters, Datadog agents, New Relic agents for metrics
Network
CNI plugins like Calico, Weave, Flannel for pod networking
Storage
Storage drivers and CSI plugins for volume provisioning
Creating a DaemonSet
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd-elasticsearch
namespace: kube-system
spec:
selector:
matchLabels:
name: fluentd-elasticsearch
template:
metadata:
labels:
name: fluentd-elasticsearch
spec:
tolerations:
# Allow this pod to be scheduled on master nodes
- key: node-role.kubernetes.io/master
effect: NoSchedule
containers:
- name: fluentd-elasticsearch
image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
env:
- name: FLUENT_ELASTICSEARCH_HOST
value: "elasticsearch.logging"
- name: FLUENT_ELASTICSEARCH_PORT
value: "9200"
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
🚀 Advanced DaemonSet Features
Node Selection
# Deploy only to nodes with SSD storage
spec:
template:
spec:
nodeSelector:
disktype: ssd
# OR use node affinity for more complex rules
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node.kubernetes.io/instance-type
operator: In
values:
- m5.large
- m5.xlarge
Update Strategy
spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1 # Update one node at a time
maxSurge: 0 # Don't create extra pods during update
# For immediate updates (not recommended for production)
updateStrategy:
type: OnDelete # Pods updated only when manually deleted
Priority and Preemption
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority-daemonset
value: 1000000
globalDefault: false
description: "High priority class for critical DaemonSets"
---
spec:
template:
spec:
priorityClassName: high-priority-daemonset
📦 Jobs Overview
Jobs create one or more pods and ensure they run to successful completion. Perfect for batch processing, data migrations, and one-time tasks.
Single Task
Run once and complete
Parallel Processing
Multiple pods working together
Completion Tracking
Guaranteed execution to success
Job Patterns
| Pattern | Completions | Parallelism | Use Case |
|---|---|---|---|
| Single Job | 1 | 1 | Database migration |
| Fixed Completion Count | N | 1 to N | Process N items |
| Work Queue | null | N | Process until queue empty |
| Indexed Job | N | N | Parallel array processing |
Basic Job Example
apiVersion: batch/v1
kind: Job
metadata:
name: data-migration
spec:
template:
spec:
containers:
- name: migrate
image: myapp:latest
command: ["python", "migrate.py"]
env:
- name: SOURCE_DB
value: "postgresql://old-db:5432/myapp"
- name: TARGET_DB
value: "postgresql://new-db:5432/myapp"
restartPolicy: Never
backoffLimit: 4 # Retry 4 times before marking as failed
activeDeadlineSeconds: 600 # Timeout after 10 minutes
ttlSecondsAfterFinished: 86400 # Clean up after 24 hours
Parallel Processing Job
apiVersion: batch/v1
kind: Job
metadata:
name: parallel-processing
spec:
parallelism: 5 # Run 5 pods in parallel
completions: 20 # Complete 20 successful runs total
completionMode: Indexed # Each pod gets a unique index (0-19)
template:
spec:
containers:
- name: worker
image: batch-processor:latest
env:
- name: JOB_COMPLETION_INDEX
valueFrom:
fieldRef:
fieldPath: metadata.annotations['batch.kubernetes.io/job-completion-index']
command:
- sh
- -c
- |
echo "Processing batch $JOB_COMPLETION_INDEX"
# Process data partition based on index
python process.py --partition=$JOB_COMPLETION_INDEX --total=20
restartPolicy: Never
completionMode: Indexed for embarrassingly parallel workloads where each pod processes a different subset of data.
🔄 Job Lifecycle Management
Handling Failures
spec:
backoffLimit: 6 # Maximum retries (default: 6)
# Exponential backoff for retries
# 10s, 20s, 40s, 80s, 160s, 320s (max)
# Pod failure policies (Kubernetes 1.25+)
podFailurePolicy:
rules:
- action: Ignore # Don't count toward backoffLimit
onExitCodes:
values: [1, 2, 3] # Ignore these exit codes
- action: FailJob # Immediately fail the job
onExitCodes:
values: [42] # Fatal error code
- action: Count # Normal counting (default)
onPodConditions:
- type: DisruptionTarget # Pod was evicted
Clean Up Policies
# Automatic cleanup after completion spec: ttlSecondsAfterFinished: 3600 # Delete after 1 hour # Different TTL for success/failure (proposed feature) successfulJobsHistoryLimit: 3 failedJobsHistoryLimit: 1
⏰ CronJobs Overview
CronJobs create Jobs on a schedule using cron syntax. Perfect for backups, reports, maintenance tasks, and periodic data processing.
│ ┌───────────── hour (0-23)
│ │ ┌───────────── day of month (1-31)
│ │ │ ┌───────────── month (1-12)
│ │ │ │ ┌───────────── day of week (0-6)
│ │ │ │ │
* * * * *
Common Cron Patterns
0 * * * *
Every hour at minute 0
*/15 * * * *
Every 15 minutes
0 2 * * *
Daily at 2:00 AM
0 0 * * 0
Weekly on Sunday at midnight
0 0 1 * *
Monthly on the 1st at midnight
Creating a CronJob
apiVersion: batch/v1
kind: CronJob
metadata:
name: database-backup
spec:
schedule: "0 2 * * *" # Daily at 2 AM
timeZone: "America/New_York" # Kubernetes 1.24+
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: postgres:14
command:
- /bin/bash
- -c
- |
DATE=$(date +%Y%m%d_%H%M%S)
pg_dump $DATABASE_URL > /backup/db_$DATE.sql
aws s3 cp /backup/db_$DATE.sql s3://my-backups/postgres/
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-credentials
key: url
- name: AWS_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
name: aws-credentials
key: access-key
- name: AWS_SECRET_ACCESS_KEY
valueFrom:
secretKeyRef:
name: aws-credentials
key: secret-key
restartPolicy: OnFailure
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
concurrencyPolicy: Forbid # Don't run if previous job still running
startingDeadlineSeconds: 300 # Skip if can't start within 5 minutes
🎯 Advanced CronJob Configuration
Concurrency Policies
Allow (default)
Multiple jobs can run concurrently
Forbid
Skip new job if previous is still running
Replace
Cancel current job and start new one
Report Generation Example
apiVersion: batch/v1
kind: CronJob
metadata:
name: weekly-report
spec:
schedule: "0 9 * * 1" # Every Monday at 9 AM
jobTemplate:
spec:
template:
spec:
containers:
- name: report-generator
image: reporting-app:latest
command: ["python", "generate_report.py"]
args: ["--type=weekly", "--format=pdf"]
volumeMounts:
- name: reports
mountPath: /reports
- name: email-sender
image: email-service:latest
command: ["sh", "-c"]
args:
- |
while [ ! -f /reports/weekly_report.pdf ]; do
sleep 5
done
python send_email.py \
--to=team@example.com \
--subject="Weekly Report" \
--attachment=/reports/weekly_report.pdf
volumeMounts:
- name: reports
mountPath: /reports
volumes:
- name: reports
emptyDir: {}
restartPolicy: OnFailure
🚀 CronJob Best Practices
Optimization Tips
Monitoring CronJobs
# List all cronjobs
kubectl get cronjobs
# View cronjob details
kubectl describe cronjob database-backup
# View job history
kubectl get jobs --selector=cronjob-name=database-backup
# Check last schedule time
kubectl get cronjob database-backup -o jsonpath='{.status.lastScheduleTime}'
# Manually trigger a cronjob
kubectl create job --from=cronjob/database-backup manual-backup-$(date +%s)
🎨 Workload Patterns & Best Practices
Choosing the Right Workload Type
| Workload Type | Use When | Don't Use When | Example |
|---|---|---|---|
| Deployment | Stateless apps, web servers, APIs | Need stable network identity or storage | Nginx, Node.js app |
| StatefulSet | Databases, distributed systems | Stateless applications | MongoDB, Kafka |
| DaemonSet | Node-level services | Application workloads | Log collectors, monitoring |
| Job | One-time tasks, batch processing | Long-running services | Data migration, backup |
| CronJob | Scheduled recurring tasks | Event-driven tasks | Reports, cleanup |
Combined Patterns
Pattern: Backup System
Combine StatefulSet (database) + CronJob (backups) + Job (restore)
- StatefulSet runs PostgreSQL with persistent storage
- CronJob performs daily backups to S3
- Job restores from backup when needed
Pattern: Log Pipeline
Combine DaemonSet (collection) + Deployment (processing) + StatefulSet (storage)
- DaemonSet runs Fluentd on all nodes
- Deployment runs Logstash for processing
- StatefulSet runs Elasticsearch cluster
Migration Patterns
# Pattern: Blue-Green Database Migration
---
# Step 1: Deploy new database version as StatefulSet
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres-v2
spec:
# ... new version configuration
---
# Step 2: Run migration job
apiVersion: batch/v1
kind: Job
metadata:
name: data-migration
spec:
template:
spec:
initContainers:
- name: wait-for-new-db
image: busybox
command: ['sh', '-c', 'until nc -z postgres-v2-0 5432; do sleep 1; done']
containers:
- name: migrate
image: migrate-tool:latest
command: ["./migrate.sh"]
---
# Step 3: Switch service to new version
apiVersion: v1
kind: Service
metadata:
name: postgres
spec:
selector:
app: postgres
version: v2 # Update selector
Resource Management
- StatefulSets: Reserve enough resources for all replicas
- DaemonSets: Account for one pod per node in resource planning
- Jobs: Set resource limits to prevent runaway consumption
- CronJobs: Consider overlap when setting resource requests
Monitoring & Observability
# Add Prometheus annotations for metrics
metadata:
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9090"
prometheus.io/path: "/metrics"
# Common metrics to track:
# - StatefulSet: Ready replicas, persistent volume usage
# - DaemonSet: Node coverage, resource usage per node
# - Job: Success/failure rate, duration
# - CronJob: Schedule adherence, missed runs
🔧 Troubleshooting Guide
Common Issues and Solutions
StatefulSet Stuck
Symptom: Pods not creating in order
Solution: Check PVC binding, previous pod health
DaemonSet Not Scheduling
Symptom: Pods missing on some nodes
Solution: Check taints, tolerations, node selectors
Job Failing Repeatedly
Symptom: Backoff limit exceeded
Solution: Check logs, increase backoffLimit, fix script
CronJob Not Running
Symptom: Missed schedules
Solution: Check startingDeadlineSeconds, timezone
Debugging Commands
kubectl rollout status statefulset/mysql
Check StatefulSet rollout status
kubectl logs -l name=fluentd-elasticsearch --all-containers
View DaemonSet logs from all nodes
kubectl describe job data-migration
Check Job events and status
kubectl get events --sort-by='.lastTimestamp'
View recent cluster events