DevOps Admin

O que Todo Dev Deve Saber sobre Persistent Volumes e Storage Classes em Kubernetes Já leu

Entendendo Armazenamento em Kubernetes Quando você inicia um contêiner em Kubernetes, o sistema de arquivos é efêmero. Isso significa que qualquer dado escrito durante a execução do Pod é perdido quando o contêiner termina ou é reiniciado. Para aplicações que precisam persistir dados—como bancos de dados, aplicações stateful ou logs—precisamos de uma solução de armazenamento persistente que viva além do ciclo de vida do Pod. Kubernetes resolve esse problema através de dois conceitos fundamentais: Persistent Volumes (PV) e Persistent Volume Claims (PVC). Um Persistent Volume é um recurso de armazenamento no cluster que existe independentemente de qualquer Pod. Um Persistent Volume Claim é uma solicitação por esse armazenamento, feita por um Pod ou usuário. Pense em um PV como um imóvel disponível para aluguel e um PVC como um contrato de aluguel. Storage Classes e Provisionamento Dinâmico O Papel das Storage Classes Uma Storage Class automatiza o provisionamento de Persistent Volumes. Em vez de um administrador criar manualmente cada PV,

Entendendo Armazenamento em Kubernetes

Quando você inicia um contêiner em Kubernetes, o sistema de arquivos é efêmero. Isso significa que qualquer dado escrito durante a execução do Pod é perdido quando o contêiner termina ou é reiniciado. Para aplicações que precisam persistir dados—como bancos de dados, aplicações stateful ou logs—precisamos de uma solução de armazenamento persistente que viva além do ciclo de vida do Pod.

Kubernetes resolve esse problema através de dois conceitos fundamentais: Persistent Volumes (PV) e Persistent Volume Claims (PVC). Um Persistent Volume é um recurso de armazenamento no cluster que existe independentemente de qualquer Pod. Um Persistent Volume Claim é uma solicitação por esse armazenamento, feita por um Pod ou usuário. Pense em um PV como um imóvel disponível para aluguel e um PVC como um contrato de aluguel.

Storage Classes e Provisionamento Dinâmico

O Papel das Storage Classes

Uma Storage Class automatiza o provisionamento de Persistent Volumes. Em vez de um administrador criar manualmente cada PV, você define uma Storage Class que especifica como volumes devem ser criados: qual provedor de armazenamento usar, tipo de disco, replicação, e outras políticas. Quando um PVC solicita uma Storage Class específica, Kubernetes cria automaticamente um PV correspondente.

Diferentes ambientes requerem diferentes estratégias de armazenamento. Em um cluster on-premises, você pode usar iSCSI ou NFS. Na AWS, você usaria EBS ou EFS. No Google Cloud, seria PersistentDisk. A Storage Class abstrai esses detalhes, permitindo que aplicações sejam portáveis entre ambientes.

Exemplo Prático: Criando uma Storage Class

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

Este exemplo cria uma Storage Class chamada fast-storage que provisiona automaticamente volumes EBS na AWS. O campo provisioner indica qual plugin é responsável pela criação. Os parameters são específicos do provisionador—aqui estamos pedindo um volume gp3 com 3000 IOPS criptografado. O allowVolumeExpansion: true permite que o volume cresça sem perder dados. reclaimPolicy: Delete significa que quando o PVC for deletado, o volume será removido. volumeBindingMode: WaitForFirstConsumer otimiza a alocação aguardando que um Pod realmente use o volume antes de vinculá-lo, melhorando a performance em clusters com múltiplas zonas de disponibilidade.

Persistent Volumes e Claims na Prática

Fluxo de Funcionamento: PV e PVC

O workflow padrão funciona assim: primeiro, você cria um PVC especificando o tamanho necessário e a Storage Class desejada. Kubernetes detecta essa solicitação e, através da Storage Class especificada, provisiona automaticamente um PV (ou reclama um existente não vinculado). O PVC e PV são então ligados um ao outro. Por fim, você monta esse volume em um Pod especificando o nome do PVC.

Esse design de duas camadas oferece flexibilidade. Desenvolvedores crisp PVCs sem conhecer detalhes de infraestrutura. Administradores gerenciam Storage Classes e a infraestrutura subjacente. Ambos trabalham com abstrações apropriadas ao seu papel.

Exemplo Completo: PVC, Pod e Dados Persistentes

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: app-database-pvc
  namespace: default
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: fast-storage
  resources:
    requests:
      storage: 50Gi
---
apiVersion: v1
kind: Pod
metadata:
  name: postgres-pod
  namespace: default
spec:
  containers:
  - name: postgres
    image: postgres:15-alpine
    ports:
    - containerPort: 5432
    volumeMounts:
    - name: db-storage
      mountPath: /var/lib/postgresql/data
    env:
    - name: POSTGRES_PASSWORD
      value: "securepassword"
  volumes:
  - name: db-storage
    persistentVolumeClaim:
      claimName: app-database-pvc

Este manifesto cria um PVC solicitando 50Gi de armazenamento usando a Storage Class fast-storage que definimos anteriormente. O accessMode ReadWriteOnce indica que apenas um nó pode montar este volume simultaneamente em modo leitura-escrita (apropriado para um banco de dados). O Pod monta esse PVC no caminho /var/lib/postgresql/data, onde PostgreSQL armazenará dados. Mesmo que o Pod seja deletado, o volume e os dados perseveram.

Access Modes Explicados

Existem três modos de acesso em Kubernetes:

  • ReadWriteOnce (RWO): Apenas um nó pode montar em leitura-escrita. Ideal para aplicações stateful como bancos de dados.
  • ReadOnlyMany (ROX): Múltiplos nós podem montar em leitura. Útil para compartilhar dados estáticos entre várias réplicas.
  • ReadWriteMany (RWX): Múltiplos nós podem montar em leitura-escrita. Requer provisionador que suporte isso (NFS, EFS, etc).

Nem todo provisionador suporta todos os modos. Um volume EBS na AWS, por exemplo, suporta apenas RWO.

Ciclo de Vida e Gestão de Volumes

Estados de um PersistentVolume

Um PV passa por vários estados durante sua vida. Inicialmente está Available (disponível para ser reclamado). Quando um PVC o vincula, passa para Bound (vinculado). Se o PVC que o vinculava for deletado, o comportamento depende da reclaimPolicy. Com Delete, o PV é imediatamente deletado. Com Retain, o PV permanece no cluster mas sai do estado Bound, permitindo que seja reivindicado manualmente. Com Recycle (deprecated), o PV é limpo e retorna ao estado Available.

Compreender esses estados é crítico para diagnosticar problemas. Se um PVC fica pendente indefinidamente, provavelmente não há PV disponível ou nenhuma Storage Class pode provisionar um. Se dados não são mais acessíveis após deletar um Pod, é provável que a reclaimPolicy tenha removido o volume.

Expandindo Volumes sem Perda de Dados

Kubernetes permite expandir PVCs on-the-fly, desde que a Storage Class tenha allowVolumeExpansion: true e o sistema de arquivos suporte redimensionamento (ext4, XFS, etc).

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: app-database-pvc
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: fast-storage
  resources:
    requests:
      storage: 100Gi  # Aumentado de 50Gi para 100Gi

Basta editar o PVC e aumentar o campo storage sob resources.requests. Kubernetes detecta a mudança, expande o volume no provisionador subjacente, e o sistema de arquivos no Pod é redimensionado automaticamente. A aplicação não precisa ser reiniciada.

Exemplo de Monitoramento e Troubleshooting

# Listar todos os PVs no cluster
kubectl get pv

# Obter informações detalhadas sobre um PV específico
kubectl describe pv nome-do-pv

# Listar PVCs em um namespace
kubectl get pvc -n seu-namespace

# Verificar por quais Pods um PVC está sendo usado
kubectl get pods -n seu-namespace --field-selector spec.volumes[*].persistentVolumeClaim.claimName=app-database-pvc

# Monitorar o status de um PVC durante provisionamento
kubectl describe pvc app-database-pvc -n seu-namespace

# Editar e expandir um PVC
kubectl edit pvc app-database-pvc -n seu-namespace

Se um PVC ficar em estado Pending, verifique: (1) se a Storage Class existe e está correta, (2) se há recursos disponíveis no cluster, (3) os logs do controlador de provisionamento com kubectl logs -n kube-system -l app=storage-provisioner.

Casos de Uso Reais e Padrões Avançados

StatefulSets com Storage Persistente

StatefulSets são ideais para aplicações que precisam de identidade estável e armazenamento persistente. Cada réplica recebe seu próprio PVC, nomeado previsivamente, garantindo que quando um Pod é recriado, ele recupera seu volume anterior.

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql-cluster
  namespace: default
spec:
  serviceName: mysql-headless
  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: mysql-data
          mountPath: /var/lib/mysql
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: root-password
  volumeClaimTemplates:
  - metadata:
      name: mysql-data
    spec:
      accessModes:
        - ReadWriteOnce
      storageClassName: fast-storage
      resources:
        requests:
          storage: 20Gi

O volumeClaimTemplates é a chave aqui. Para cada réplica do StatefulSet, Kubernetes cria um PVC separado com nome mysql-data-mysql-cluster-0, mysql-data-mysql-cluster-1, etc. Se o Pod mysql-cluster-0 falhar e for recriado, ele automaticamente reclama mysql-data-mysql-cluster-0, recuperando todos os dados.

Snapshot e Backup de Volumes

Para proteção contra perda de dados, Kubernetes suporta snapshots de volumes (requer suporte do provisionador e CRD instalado).

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
  name: mysql-snapshot-20240115
  namespace: default
spec:
  volumeSnapshotClassName: csi-snapshot-class
  source:
    persistentVolumeClaimName: app-database-pvc
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-restored-pvc
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: fast-storage
  dataSource:
    name: mysql-snapshot-20240115
    kind: VolumeSnapshot
    apiGroup: snapshot.storage.k8s.io
  resources:
    requests:
      storage: 50Gi

O primeiro objeto cria um snapshot do PVC existente. O segundo cria um novo PVC restaurando a partir desse snapshot. Isso é invaluável para testes, desenvolvimento e recuperação de desastres.

Conclusão

Três pontos essenciais aprendidos: Primeiro, Persistent Volumes e Claims separam armazenamento da computação, permitindo que dados sobrevivam ao reinício de Pods enquanto abstraem detalhes de infraestrutura. Storage Classes automatizam esse provisionamento, tornando seu cluster escalável e portável entre ambientes diferentes. Segundo, entender access modes, reclaim policies e ciclo de vida é fundamental para operar Kubernetes em produção com confiança—erros nessa área resultam em perda de dados irrecuperável. Terceiro, StatefulSets combinados com volumeClaimTemplates e snapshots oferecem uma base sólida para aplicações stateful em Kubernetes, mas exigem planejamento cuidadoso de estratégia de backup e recuperação.

Referências


Artigos relacionados