StatefulSets e Armazenamento Persistente no Kubernetes

Implante aplicativos com estado com dimensionamento ordenado e dados persistentes.

Os StatefulSets do Kubernetes são a solução ideal para gerenciar aplicações com estado que exigem identidades estáveis, armazenamento persistente e padrões de implantação ordenados — essenciais para bancos de dados, sistemas distribuídos e camadas de cache.

Se você é novo no Kubernetes ou está configurando um cluster, considere explorar distribuições do Kubernetes como k3s ou MicroK8s para desenvolvimento, ou instalar Kubernetes com Kubespray para clusters de nível de produção.

apresentação na cafeteria Esta bela imagem foi gerada pelo modelo de IA Flux 1 dev.

O que são StatefulSets?

Os StatefulSets são objetos de API de carga de trabalho do Kubernetes projetados especificamente para gerenciar aplicações com estado. Diferente das Deployments, que tratam todos os pods como intercambiáveis, os StatefulSets mantêm uma identidade única para cada pod com garantias sobre ordenação e unicidade.

Principais Características:

  • Identificadores de Rede Estáveis: Cada pod recebe um hostname previsível que persiste através de reinicializações
  • Armazenamento Persistente: PersistentVolumeClaims (PVCs) dedicados que seguem os pods através de reprogramações
  • Implantação Ordenada: Os pods são criados sequencialmente (0, 1, 2…) e encerrados em ordem inversa
  • Atualizações Ordenadas: As atualizações rolantes ocorrem em ordem, garantindo a estabilidade da aplicação

Os StatefulSets são críticos para aplicações como PostgreSQL, MySQL, MongoDB, Cassandra, Elasticsearch, Kafka, ZooKeeper, Redis e etcd — qualquer carga de trabalho onde a identidade do pod e a persistência de dados importam.

Compreendendo o Armazenamento Persistente no Kubernetes

O Kubernetes fornece uma camada de abstração de armazenamento sofisticada que desacopla o gerenciamento de armazenamento do ciclo de vida dos pods:

Componentes de Armazenamento

PersistentVolume (PV): Uma unidade de armazenamento no cluster provisionada por um administrador ou criada dinamicamente via uma StorageClass. Os PVs existem independentemente dos pods.

PersistentVolumeClaim (PVC): Uma solicitação de armazenamento por um pod. Os PVCs vinculam-se a PVs disponíveis que correspondem aos seus requisitos (tamanho, modo de acesso, classe de armazenamento).

StorageClass: Define diferentes “classes” de armazenamento com vários provisionadores (AWS EBS, GCE PD, Azure Disk, NFS, etc.) e parâmetros como replicação, níveis de desempenho e políticas de backup.

Modos de Acesso

  • ReadWriteOnce (RWO): Volume montado como leitura/escrita por um único nó
  • ReadOnlyMany (ROX): Volume montado como somente leitura por muitos nós
  • ReadWriteMany (RWX): Volume montado como leitura/escrita por muitos nós (requer backends de armazenamento especiais)

Arquitetura de Armazenamento de StatefulSets

Os StatefulSets usam volumeClaimTemplates para criar automaticamente PersistentVolumeClaims para cada réplica do pod. Isso é fundamentalmente diferente das Deployments:

Como os volumeClaimTemplates Funcionam

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
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: password
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "fast-ssd"
      resources:
        requests:
          storage: 10Gi

Quando você cria este StatefulSet:

  1. O Kubernetes cria o pod mysql-0 e o PVC data-mysql-0
  2. Em seguida cria o pod mysql-1 e o PVC data-mysql-1
  3. Finalmente cria o pod mysql-2 e o PVC data-mysql-2

Cada pod recebe seu próprio volume persistente dedicado de 10GB. Se mysql-1 for excluído ou reprogramado, o Kubernetes o recria e reanexa o mesmo PVC data-mysql-1, preservando todos os dados.

Criando um Service Headless para StatefulSets

Os StatefulSets requerem um Service Headless para fornecer identidades de rede estáveis:

apiVersion: v1
kind: Service
metadata:
  name: mysql-service
spec:
  clusterIP: None  # Isso o torna um serviço headless
  selector:
    app: mysql
  ports:
  - port: 3306
    name: mysql

Isso cria entradas DNS para cada pod:

  • mysql-0.mysql-service.default.svc.cluster.local
  • mysql-1.mysql-service.default.svc.cluster.local
  • mysql-2.mysql-service.default.svc.cluster.local

As aplicações podem se conectar diretamente a instâncias específicas de pods usando esses nomes DNS estáveis.

Padrões de Implantação de StatefulSets

Para equipes que gerenciam implantações complexas do Kubernetes, os Helm Charts fornecem uma maneira poderosa de empacotar e implantar StatefulSets com modelagem, versionamento e gerenciamento de dependências. O Helm simplifica o gerenciamento de configurações de StatefulSets em diferentes ambientes.

Escalonamento Ordenado

Ao escalar de 3 para 5 réplicas:

kubectl scale statefulset mysql --replicas=5

O Kubernetes cria pods em ordem: mysql-3 → aguarda Ready → mysql-4

Ao reduzir de 5 para 3 réplicas:

kubectl scale statefulset mysql --replicas=3

O Kubernetes encerra em ordem inversa: mysql-4 → aguarda término → mysql-3

Atualizações Rolantes (Rolling Updates)

Os StatefulSets suportam duas estratégias de atualização:

OnDelete: Atualizações manuais — os pods atualizam apenas quando você os exclui RollingUpdate: Atualizações automáticas sequenciais em ordem ordinal inversa

spec:
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      partition: 2  # Atualiza apenas pods com ordinal >= 2

O parâmetro partition habilita implantações canary — você pode atualizar pods de números altos primeiro e testar antes de estender para todas as réplicas.

Melhores Práticas de Armazenamento

Provisionamento Dinâmico

Sempre use StorageClasses para provisionamento dinâmico de volumes:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp3
  iops: "3000"
  throughput: "125"
allowVolumeExpansion: true
reclaimPolicy: Retain

allowVolumeExpansion: Habilita redimensionamento de PVCs sem recriá-los reclaimPolicy: Retain mantém os dados do PV após a exclusão do PVC, Delete remove-os automaticamente

Políticas de Retenção de PVC

O Kubernetes 1.23+ suporta persistentVolumeClaimRetentionPolicy:

spec:
  persistentVolumeClaimRetentionPolicy:
    whenDeleted: Retain    # Mantém PVCs quando o StatefulSet é excluído
    whenScaled: Delete     # Exclui PVCs ao reduzir escala

Opções:

  • Retain: Mantém PVCs (comportamento padrão, mais seguro)
  • Delete: Exclui automaticamente PVCs (útil para ambientes de desenvolvimento)

Estratégias de Backup

Instantâneos de Volume (Volume Snapshots): Use recursos VolumeSnapshot para criar backups pontuais

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
  name: mysql-snapshot-20251113
spec:
  volumeSnapshotClassName: csi-snapclass
  source:
    persistentVolumeClaimName: data-mysql-0

Backups ao Nível de Aplicação: Use ferramentas como mysqldump, pg_dump ou Velero para backups específicos de banco de dados

Replicação Distribuída: Configure replicação ao nível da aplicação (replicação MySQL, replicação de streaming PostgreSQL) como primeira linha de defesa

Casos de Uso no Mundo Real

Cluster de Banco de Dados (PostgreSQL)

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
spec:
  serviceName: postgres
  replicas: 3
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
      - name: postgres
        image: postgres:16
        ports:
        - containerPort: 5432
        env:
        - name: POSTGRES_PASSWORD
          valueFrom:
            secretKeyRef:
              name: postgres-secret
              key: password
        - name: PGDATA
          value: /var/lib/postgresql/data/pgdata
        volumeMounts:
        - name: data
          mountPath: /var/lib/postgresql/data
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: "standard"
      resources:
        requests:
          storage: 20Gi

Cache Distribuído (Redis)

Para clusters Redis, você precisa de StatefulSet e configuração cuidadosa:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis
spec:
  serviceName: redis-service
  replicas: 6
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - name: redis
        image: redis:7-alpine
        command:
        - redis-server
        - "--appendonly"
        - "yes"
        - "--appendfsync"
        - "everysec"
        ports:
        - containerPort: 6379
          name: redis
        volumeMounts:
        - name: data
          mountPath: /data
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 5Gi

Fila de Mensagens (Kafka)

O Kafka requer armazenamento persistente para logs e identidades de rede estáveis para coordenação de brokers:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: kafka
spec:
  serviceName: kafka-service
  replicas: 3
  selector:
    matchLabels:
      app: kafka
  template:
    metadata:
      labels:
        app: kafka
    spec:
      containers:
      - name: kafka
        image: confluentinc/cp-kafka:7.5.0
        ports:
        - containerPort: 9092
          name: kafka
        env:
        - name: KAFKA_BROKER_ID
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        volumeMounts:
        - name: data
          mountPath: /var/lib/kafka/data
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 100Gi

Monitoramento e Solução de Problemas

Para uma referência abrangente dos comandos Kubernetes usados nesta seção, consulte a Lista de Comandos Kubernetes (CheatSheet).

Verificar Status do StatefulSet

# Ver detalhes do StatefulSet
kubectl get statefulset mysql
kubectl describe statefulset mysql

# Verificar ordem de criação e status dos pods
kubectl get pods -l app=mysql -w

# Verificar status do PVC
kubectl get pvc
kubectl describe pvc data-mysql-0

Problemas Comuns

Pod Preso em Pendente: Verifique o status do PVC e a disponibilidade de armazenamento

kubectl describe pod mysql-0
kubectl get events --sort-by='.lastTimestamp'

Armazenamento Cheio: Expanda o PVC se a StorageClass permitir

kubectl patch pvc data-mysql-0 -p '{"spec":{"resources":{"requests":{"storage":"20Gi"}}}}'

Pod Não Encerra: Verifique bloqueios ao nível da aplicação ou problemas de desmontagem de volume

kubectl delete pod mysql-0 --grace-period=0 --force

Métricas para Monitorar

  • Uso de Armazenamento: Monitore a capacidade e porcentagem de uso do PVC
  • Desempenho de I/O: Rastreie IOPS, throughput e latência
  • Reinicializações de Pod: Reinicializações frequentes podem indicar problemas de armazenamento
  • Tempo de Vinculação de PVC: Vinculação lenta sugere problemas de provisionamento

Estratégias de Migração

Ao migrar para StatefulSets, certifique-se de que seu cluster Kubernetes esteja configurado corretamente. Para configurações de homelab ou clusters pequenos, revise nossa comparação abrangente de distribuições Kubernetes para escolher a plataforma certa para seus requisitos de carga de trabalho.

De Deployment para StatefulSet

  1. Crie StatefulSet com volumeClaimTemplates
  2. Reduza a escala da Deployment de forma graciosa
  3. Restaure dados de backups para os pods do StatefulSet
  4. Atualize referências DNS/Service
  5. Exclua a Deployment antiga e os PVCs

Backup Antes da Migração

# Criar instantâneo dos PVCs existentes
kubectl get pvc -o yaml > pvc-backup.yaml

# Criar instantâneos de volume
kubectl apply -f volume-snapshot.yaml

Considerações de Segurança

Criptografia de Armazenamento

Habilite criptografia em repouso usando parâmetros de StorageClass:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: encrypted-storage
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp3
  encrypted: "true"
  kmsKeyId: arn:aws:kms:us-east-1:123456789012:key/abcd-1234

Controle de Acesso

Use RBAC para restringir quem pode criar/modificar StatefulSets e PVCs:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: statefulset-manager
rules:
- apiGroups: ["apps"]
  resources: ["statefulsets"]
  verbs: ["get", "list", "create", "update", "delete"]
- apiGroups: [""]
  resources: ["persistentvolumeclaims"]
  verbs: ["get", "list", "create", "delete"]

Políticas de Rede

Restrinja a comunicação pod-a-pod:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: mysql-network-policy
spec:
  podSelector:
    matchLabels:
      app: mysql
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: backend
    ports:
    - protocol: TCP
      port: 3306

Otimização de Desempenho

Níveis de Desempenho de Armazenamento

Escolha StorageClasses apropriadas com base na carga de trabalho:

  • Alto IOPS: Bancos de dados com leitura/escrita aleatória pesada (gp3, io2)
  • Alto Throughput: Agregação de logs, análises (st1, sc1)
  • Equilibrado: Aplicações de uso geral (gp3)

Distribuição de Pods

Use anti-affinity de pods para distribuir pods de StatefulSets através de zonas de disponibilidade:

spec:
  template:
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchLabels:
                app: mysql
            topologyKey: topology.kubernetes.io/zone

Solicitações e Limites de Recursos

Defina recursos apropriados para desempenho consistente:

resources:
  requests:
    cpu: "2"
    memory: "4Gi"
    ephemeral-storage: "10Gi"
  limits:
    cpu: "4"
    memory: "8Gi"
    ephemeral-storage: "20Gi"

Padrões Avançados

Aplicação com Estado com Init Containers

Use init containers para inicialização de banco de dados:

spec:
  template:
    spec:
      initContainers:
      - name: init-mysql
        image: mysql:8.0
        command:
        - bash
        - "-c"
        - |
          if [[ ! -d /var/lib/mysql/mysql ]]; then
            mysqld --initialize-insecure --datadir=/var/lib/mysql
          fi          
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql

Pods Multi-Container para Sidecars

Adicione sidecars de backup ou agentes de monitoramento:

spec:
  template:
    spec:
      containers:
      - name: mysql
        image: mysql:8.0
        # ... mysql config ...
      - name: backup-sidecar
        image: mysql-backup:latest
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
          readOnly: true

ConfigMaps para Configuração Dinâmica

Separe a configuração da definição do StatefulSet:

apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql-config
data:
  my.cnf: |
    [mysqld]
    max_connections=200
    innodb_buffer_pool_size=2G    
---
spec:
  template:
    spec:
      containers:
      - name: mysql
        volumeMounts:
        - name: config
          mountPath: /etc/mysql/conf.d
      volumes:
      - name: config
        configMap:
          name: mysql-config

Assinar

Receba novos artigos sobre sistemas, infraestrutura e engenharia de IA.