Guia Completo de StatefulSets em Kubernetes: Bancos de Dados e Workloads com Estado Já leu

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 , e — 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)

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 partition em estratégias de atualização para testar mudanças em um subset de réplicas antes de aplicar a todos os pods. Defina podManagementPolicy: Parallel apenas se sua aplicação tolera inicializações simultâneas, caso contrário deixe como OrderedReady.

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.

Referências


Artigos relacionados