Chapter 7.1 - In-Depth StatefulSets
Learning Objectives
By the end of this chapter, you will be able to:
- Understand StatefulSets in depth
- Deploy databases with StatefulSets
- Manage scaling and updates
- Understand init containers in StatefulSets
- Implement master-slave replication
- Manage quorums and leader election
Introduction
StatefulSets are essential for stateful applications. This chapter dives deeper into the concepts and presents advanced use cases.
StatefulSet Architecture
Components
Example: PostgreSQL Database
Step 1: Headless Service
apiVersion: v1
kind: Service
metadata:
name: postgres
spec:
clusterIP: None
selector:
app: postgres
ports:
- port: 5432
name: postgres
Step 2: ConfigMap for Configuration
apiVersion: v1
kind: ConfigMap
metadata:
name: postgres-config
data:
postgresql.conf: |
max_connections = 200
shared_buffers = 256MB
effective_cache_size = 1GB
Step 3: Secret for Credentials
apiVersion: v1
kind: Secret
metadata:
name: postgres-secret
type: Opaque
data:
postgres-password: cG9zdGdyZXM= # base64
replication-password: cmVwbGljYXRpb24= # base64
Step 4: StatefulSet
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres
replicas: 3
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
initContainers:
- name: init-postgres
image: postgres:14
command:
- /bin/bash
- -c
- |
set -e
if [[ $HOSTNAME == "postgres-0" ]]; then
echo "Initializing primary database..."
else
echo "Waiting for primary to be ready..."
until pg_isready -h postgres-0.postgres; do
sleep 1
done
fi
env:
- name: PGHOST
value: postgres-0.postgres
containers:
- name: postgres
image: postgres:14
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secret
key: postgres-password
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
ports:
- containerPort: 5432
name: postgres
volumeMounts:
- name: postgres-data
mountPath: /var/lib/postgresql/data
- name: postgres-config
mountPath: /etc/postgresql
volumes:
- name: postgres-config
configMap:
name: postgres-config
volumeClaimTemplates:
- metadata:
name: postgres-data
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "fast-ssd"
resources:
requests:
storage: 50Gi
Init Containers
Init containers run before the main containers and can:
- Initialize the database
- Wait for other services to be ready
- Configure the environment
- Check dependencies
Example:
spec:
template:
spec:
initContainers:
- name: wait-for-db
image: busybox:1.35
command:
- sh
- -c
- |
until nc -z postgres-0.postgres 5432; do
echo "Waiting for database..."
sleep 2
done
containers:
- name: app
image: my-app:1.0
Master-Slave Replication
Architecture
Configuration:
spec:
template:
spec:
containers:
- name: postgres
env:
- name: POSTGRES_REPLICATION_MODE
value: "master" # For postgres-0
- name: POSTGRES_MASTER_HOST
value: "postgres-0.postgres" # For postgres-1, postgres-2
Quorum and Leader Election
For applications requiring a quorum (e.g., etcd, Consul):
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: etcd
spec:
serviceName: etcd
replicas: 3 # Quorum: (3/2) + 1 = 2
template:
spec:
containers:
- name: etcd
image: quay.io/coreos/etcd:v3.5.0
env:
- name: ETCD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: ETCD_INITIAL_CLUSTER
value: "etcd-0=http://etcd-0.etcd:2380,etcd-1=http://etcd-1.etcd:2380,etcd-2=http://etcd-2.etcd:2380"
Quorum: (N/2) + 1, where N = number of replicas
Scaling
Scaling Up
kubectl scale statefulset postgres --replicas=5
Process:
- Creates
postgres-3 - Waits for
postgres-3to be Ready - Creates
postgres-4 - Waits for
postgres-4to be Ready
Scaling Down
kubectl scale statefulset postgres --replicas=2
Process:
- Deletes
postgres-4(last) - Waits for
postgres-4to terminate - Deletes
postgres-3 - Waits for
postgres-3to terminate
Important: For databases, ensure data is backed up before scaling down.
Updates
Rolling Update
spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 0 # Update all Pods
Process:
- Updates
postgres-2(last) - Updates
postgres-1 - Updates
postgres-0(first)
Partial Update (Canary)
spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 2 # Keeps postgres-0 and postgres-1 on the old version
Result: Only postgres-2 is updated.
Useful Commands
Management
# View StatefulSets
kubectl get statefulset
kubectl get sts
# Details
kubectl describe statefulset postgres
# View Pods
kubectl get pods -l app=postgres
# Logs of a specific Pod
kubectl logs postgres-0
Scaling
# Scaling up
kubectl scale statefulset postgres --replicas=5
# Scaling down
kubectl scale statefulset postgres --replicas=2
Updates
# Update the image
kubectl set image statefulset/postgres postgres=postgres:15
# View update status
kubectl rollout status statefulset/postgres
# Rollback
kubectl rollout undo statefulset/postgres
Best Practices
1. Regular Backups
Set up automatic volume backups.
2. Monitoring
Monitor the status of each Pod and performance metrics.
3. Appropriate Quorum
Use an odd number of replicas to avoid ties.
4. Init Containers
Use init containers for initialization and checks.
5. Headless Service
Always create a headless service for DNS discovery.
Summary
In this chapter, you learned:
In-depth StatefulSets: Stateful applications with stable identities
Databases: Deploying PostgreSQL with replication
Init containers: Initialization before main containers
Replication: Master-slave configuration
Quorum: Calculation and configuration for distributed applications
Scaling: Guaranteed order (ascending/descending)
Updates: RollingUpdate and partial updates
Best practices: Backups, monitoring, quorum
Next Steps
Chapter 7.2: DaemonSets
Chapter 7.3: Jobs
Chapter 7.4: CronJobs
Chapter created: December 2024