O que é StatefulSet?
Um StatefulSet é um objeto do Kubernetes projetado para gerenciar aplicações com estado (stateful), onde cada réplica precisa manter uma identidade única e estável. Diferentemente de um Deployment, que trata todas as réplicas de forma intercambiável, um StatefulSet garante que cada pod tenha um nome persistente, um identificador ordinal e, se configurado, armazenamento dedicado que persiste mesmo após recriações. Isso é fundamental para aplicações como bancos de dados, sistemas de fila distribuídos e qualquer workload que mantenha dados locais ou dependa de descoberta de peers.
Quando você cria um StatefulSet com 3 réplicas, não obtém pods com nomes aleatórios como em um Deployment. Em vez disso, você recebe meu-app-0, meu-app-1 e meu-app-2 — sempre nesta ordem, sempre com estes nomes. Isso permite que outras aplicações ou componentes do próprio cluster façam descoberta de serviço previsível e confiável. A ordem de criação e exclusão também é garantida: os pods são criados e iniciados sequencialmente (0 → 1 → 2) e removidos em ordem reversa (2 → 1 → 0).
Diferenças Fundamentais: StatefulSet vs Deployment
O Ciclo de Vida e Ordenação
Um Deployment cria e destrói pods sem ordem específica e reatribui nomes sempre que um pod falha. Um StatefulSet, pelo contrário, respeita uma ordem rigorosa e mantém identidade estável. Se o pod meu-banco-1 falha, o StatefulSet garante que a próxima instância também será chamada meu-banco-1 e, se houver PersistentVolumeClaim associado, terá acesso aos mesmos dados.
Armazenamento e PersistentVolumes
StatefulSets suportam nativamente volumeClaimTemplates, que criam automaticamente PersistentVolumeClaims (PVCs) únicos para cada réplica. Um Deployment pode usar volumes compartilhados, mas cada pod não tem garantia de dados persistentes entre recriações. Com StatefulSet e volumeClaimTemplates, cada réplica meu-banco-0, meu-banco-1 e meu-banco-2 possui seu próprio volume que persiste independentemente.
Descoberta de Serviço
StatefulSets exigem um Serviço Headless (sem IP de cluster), que não faz balanceamento de carga e expõe cada pod individualmente via DNS. Um Deployment típico usa um Serviço regular que balanceia requisições. Isso permite que aplicações cliente conheçam e se conectem a instâncias específicas de um StatefulSet pelo nome: meu-banco-0.meu-servico.default.svc.cluster.local.
Anatomia de um StatefulSet: Componentes Essenciais
Serviço Headless
Toda aplicação stateful no Kubernetes exige um Serviço Headless para comunicação entre réplicas e descoberta DNS. Um Serviço Headless define clusterIP: None, o que faz com que o Kubernetes não aloque um IP de cluster virtual. Em vez disso, requisições DNS retornam os IPs individuais de cada pod.
apiVersion: v1
kind: Service
metadata:
name: mysql-service
labels:
app: mysql
spec:
clusterIP: None
selector:
app: mysql
ports:
- port: 3306
targetPort: 3306
name: mysql
StatefulSet com volumeClaimTemplates
O StatefulSet abaixo demonstra uma configuração real para MySQL, incluindo persistência, descoberta ordenada e um init container que aguarda o serviço estar pronto. O campo serviceName vincula este StatefulSet ao Serviço Headless, e volumeClaimTemplates cria um PVC único para cada réplica.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql-service
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
ports:
- containerPort: 3306
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: root-password
volumeMounts:
- name: data
mountPath: /var/lib/mysql
livenessProbe:
exec:
command:
- sh
- -c
- mysqladmin ping -u root -p${MYSQL_ROOT_PASSWORD}
initialDelaySeconds: 30
periodSeconds: 10
initContainers:
- name: init-mysql
image: busybox
command:
- sh
- -c
- |
set -ex
ordinal=$(hostname | rev | cut -d'-' -f1 | rev)
echo "Pod ordinal: $ordinal"
if [ "$ordinal" -eq "0" ]; then
echo "Master node detected"
fi
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 10Gi
Explicação Detalhada dos Campos-Chave
O campo serviceName: mysql-service liga este StatefulSet ao Serviço Headless, permitindo descoberta DNS por nome. O volumeClaimTemplates funciona como um template que gera um PVC para cada réplica: data-mysql-0, data-mysql-1, data-mysql-2. Cada pod automaticamente recebe um volume montado em /var/lib/mysql que persiste entre restarts. O initContainer usa um truque comum: extrai o ordinal do hostname (por exemplo, "mysql-2" → ordinal 2) para permitir configuração baseada em função.
Casos de Uso Reais e Padrões de Implementação
Banco de Dados com Replicação
Bancos de dados como MySQL, PostgreSQL e MongoDB exigem topologia conhecida e armazenamento persistente. Um StatefulSet permite que você configure um nó primário (ordinal 0) e réplicas secundárias que sabem para onde replicar dados. Cada réplica mantém seu próprio estado sem conflito.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres-service
replicas: 3
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:15
ports:
- containerPort: 5432
name: postgres
env:
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secret
key: password
volumeMounts:
- name: pgdata
mountPath: /var/lib/postgresql/data
readinessProbe:
exec:
command:
- /bin/sh
- -c
- pg_isready -U postgres
initialDelaySeconds: 10
periodSeconds: 5
volumeClaimTemplates:
- metadata:
name: pgdata
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 20Gi
Aplicações de Fila Distribuída (RabbitMQ, Kafka)
Sistemas de fila como RabbitMQ e Kafka dependem de um cluster onde cada node precisa de identidade estável e dados persistentes. Um StatefulSet garante que quando um nó falha, sua identidade é preservada e pode se reintegrar ao cluster com seu estado anterior.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: rabbitmq
spec:
serviceName: rabbitmq-service
replicas: 3
selector:
matchLabels:
app: rabbitmq
template:
metadata:
labels:
app: rabbitmq
spec:
containers:
- name: rabbitmq
image: rabbitmq:3.12-management
ports:
- containerPort: 5672
name: amqp
- containerPort: 15672
name: management
env:
- name: RABBITMQ_DEFAULT_USER
value: rabbitmq
- name: RABBITMQ_DEFAULT_PASS
valueFrom:
secretKeyRef:
name: rabbitmq-secret
key: password
- name: RABBITMQ_NODENAME
value: rabbit@$(HOSTNAME).rabbitmq-service.default.svc.cluster.local
volumeMounts:
- name: rabbitmq-data
mountPath: /var/lib/rabbitmq
initContainers:
- name: wait-for-service
image: busybox
command: ['sh', '-c', 'until nslookup rabbitmq-service; do echo waiting for DNS; sleep 2; done']
volumeClaimTemplates:
- metadata:
name: rabbitmq-data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 5Gi
Descoberta de Peers em um Cluster
Em aplicações como Elasticsearch ou Consul, cada nó precisa descobrir e se comunicar com os outros. Com StatefulSet e Serviço Headless, cada pod conhece o DNS de seus peers: elasticsearch-0.elasticsearch-service, elasticsearch-1.elasticsearch-service, etc. Isso simplifica a configuração inicial do cluster.
Monitoramento, Troubleshooting e Boas Práticas
Verificando o Status de um StatefulSet
Para diagnosticar problemas, inicie obtendo informações sobre o StatefulSet e seus pods:
# Listar StatefulSets e suas réplicas prontas
kubectl get statefulset
kubectl describe statefulset mysql
# Verificar os pods criados com seus ordinais
kubectl get pods -l app=mysql
kubectl describe pod mysql-0
# Verificar PVCs associados
kubectl get pvc
kubectl describe pvc data-mysql-0
# Verificar descoberta DNS do Serviço Headless
kubectl exec -it mysql-0 -- nslookup mysql-service
Logs e Diagnostics
Logs são essenciais para entender comportamento de replicação e erros de inicialização:
# Ver logs do container principal
kubectl logs mysql-0
# Ver logs do init container
kubectl logs mysql-0 -c init-mysql
# Ver eventos recentes associados ao pod
kubectl describe pod mysql-0 | grep -A 10 Events
# Executar comandos dentro do pod para debug
kubectl exec -it mysql-0 -- mysql -u root -p${MYSQL_ROOT_PASSWORD} -e "SHOW SLAVE STATUS\G"
Políticas de Escalamento Seguro
StatefulSets não devem ser escalados agressivamente em aplicações de banco de dados. Escale sempre manualmente e com cuidado:
# Escalar para 5 réplicas (cria mysql-3 e mysql-4 sequencialmente)
kubectl scale statefulset mysql --replicas=5
# Reduzir para 2 réplicas (remove mysql-4, mysql-3, mysql-2 nesta ordem)
kubectl scale statefulset mysql --replicas=2
# Atualizar a imagem (rolling update respeitando ordem)
kubectl set image statefulset/mysql mysql=mysql:8.1 --record
Dicas Práticas
Sempre use
partitionem estratégias de atualização para testar mudanças em um subset de réplicas antes de aplicar a todos os pods. DefinapodManagementPolicy: Parallelapenas se sua aplicação tolera inicializações simultâneas, caso contrário deixe comoOrderedReady.
Use PodDisruptionBudgets para proteger a disponibilidade durante manutenção:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: mysql-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: mysql
Implemente health checks robustos (liveness e readiness probes) específicos para sua aplicação. Um StatefulSet com pods que falham nas verificações de saúde pode levar a um estado degradado que requer intervenção manual.
Conclusão
StatefulSets resolvem um problema fundamental do Kubernetes: como executar aplicações que precisam de identidade estável, ordem de inicialização garantida e armazenamento persistente. A combinação de um Serviço Headless, volumeClaimTemplates e nomes de pods previsíveis permite que bancos de dados, sistemas de fila e outros workloads com estado funcionem de forma confiável. A chave para dominar StatefulSets é entender que você não está apenas replicando containers — está orquestrando componentes de um sistema distribuído onde cada instância importa e tem um papel específico. Por fim, lembre-se que escalabilidade em aplicações stateful é diferente de stateless: procure sempre pelos documentos de sua aplicação específica para entender topologias de replicação, failover e backup antes de decidir pelo StatefulSet no Kubernetes.