DevOps & CI/CD

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

11 min de leitura

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

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,

<h2>Entendendo Armazenamento em Kubernetes</h2>

<p>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.</p>

<p>Kubernetes resolve esse problema através de dois conceitos fundamentais: <strong>Persistent Volumes (PV)</strong> e <strong>Persistent Volume Claims (PVC)</strong>. 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.</p>

<h2>Storage Classes e Provisionamento Dinâmico</h2>

<h3>O Papel das Storage Classes</h3>

<p>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.</p>

<p>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.</p>

<h3>Exemplo Prático: Criando uma Storage Class</h3>

<pre><code class="language-yaml">apiVersion: storage.k8s.io/v1

kind: StorageClass

metadata:

name: fast-storage

provisioner: kubernetes.io/aws-ebs

parameters:

type: gp3

iops: &quot;3000&quot;

throughput: &quot;125&quot;

encrypted: &quot;true&quot;

allowVolumeExpansion: true

reclaimPolicy: Delete

volumeBindingMode: WaitForFirstConsumer</code></pre>

<p>Este exemplo cria uma Storage Class chamada <code>fast-storage</code> que provisiona automaticamente volumes EBS na AWS. O campo <code>provisioner</code> indica qual plugin é responsável pela criação. Os <code>parameters</code> são específicos do provisionador—aqui estamos pedindo um volume gp3 com 3000 IOPS criptografado. O <code>allowVolumeExpansion: true</code> permite que o volume cresça sem perder dados. <code>reclaimPolicy: Delete</code> significa que quando o PVC for deletado, o volume será removido. <code>volumeBindingMode: WaitForFirstConsumer</code> 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.</p>

<h2>Persistent Volumes e Claims na Prática</h2>

<h3>Fluxo de Funcionamento: PV e PVC</h3>

<p>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.</p>

<p>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.</p>

<h3>Exemplo Completo: PVC, Pod e Dados Persistentes</h3>

<pre><code class="language-yaml">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: &quot;securepassword&quot;

volumes:

  • name: db-storage

persistentVolumeClaim:

claimName: app-database-pvc</code></pre>

<p>Este manifesto cria um PVC solicitando 50Gi de armazenamento usando a Storage Class <code>fast-storage</code> que definimos anteriormente. O accessMode <code>ReadWriteOnce</code> 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 <code>/var/lib/postgresql/data</code>, onde PostgreSQL armazenará dados. Mesmo que o Pod seja deletado, o volume e os dados perseveram.</p>

<h3>Access Modes Explicados</h3>

<p>Existem três modos de acesso em Kubernetes:</p>

<ul>

<li><strong>ReadWriteOnce (RWO)</strong>: Apenas um nó pode montar em leitura-escrita. Ideal para aplicações stateful como bancos de dados.</li>

<li><strong>ReadOnlyMany (ROX)</strong>: Múltiplos nós podem montar em leitura. Útil para compartilhar dados estáticos entre várias réplicas.</li>

<li><strong>ReadWriteMany (RWX)</strong>: Múltiplos nós podem montar em leitura-escrita. Requer provisionador que suporte isso (NFS, EFS, etc).</li>

</ul>

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

<h2>Ciclo de Vida e Gestão de Volumes</h2>

<h3>Estados de um PersistentVolume</h3>

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

<p>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 <code>reclaimPolicy</code> tenha removido o volume.</p>

<h3>Expandindo Volumes sem Perda de Dados</h3>

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

<pre><code class="language-yaml">apiVersion: v1

kind: PersistentVolumeClaim

metadata:

name: app-database-pvc

spec:

accessModes:

  • ReadWriteOnce

storageClassName: fast-storage

resources:

requests:

storage: 100Gi # Aumentado de 50Gi para 100Gi</code></pre>

<p>Basta editar o PVC e aumentar o campo <code>storage</code> sob <code>resources.requests</code>. 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.</p>

<h3>Exemplo de Monitoramento e Troubleshooting</h3>

<pre><code class="language-bash"># 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</code></pre>

<p>Se um PVC ficar em estado <code>Pending</code>, 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 <code>kubectl logs -n kube-system -l app=storage-provisioner</code>.</p>

<h2>Casos de Uso Reais e Padrões Avançados</h2>

<h3>StatefulSets com Storage Persistente</h3>

<p>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.</p>

<pre><code class="language-yaml">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</code></pre>

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

<h3>Snapshot e Backup de Volumes</h3>

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

<pre><code class="language-yaml">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</code></pre>

<p>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.</p>

<h2>Conclusão</h2>

<p>Três pontos essenciais aprendidos: Primeiro, <strong>Persistent Volumes e Claims separam armazenamento da computação</strong>, 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, <strong>entender access modes, reclaim policies e ciclo de vida</strong> é fundamental para operar Kubernetes em produção com confiança—erros nessa área resultam em perda de dados irrecuperável. Terceiro, <strong>StatefulSets combinados com volumeClaimTemplates e snapshots</strong> oferecem uma base sólida para aplicações stateful em Kubernetes, mas exigem planejamento cuidadoso de estratégia de backup e recuperação.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://kubernetes.io/docs/concepts/storage/persistent-volumes/" target="_blank" rel="noopener noreferrer">Kubernetes Documentation - Persistent Volumes</a></li>

<li><a href="https://kubernetes.io/docs/concepts/storage/storage-classes/" target="_blank" rel="noopener noreferrer">Kubernetes Documentation - Storage Classes</a></li>

<li><a href="https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/" target="_blank" rel="noopener noreferrer">Kubernetes Documentation - StatefulSets</a></li>

<li><a href="https://kubernetes.io/docs/concepts/storage/volume-snapshots/" target="_blank" rel="noopener noreferrer">Kubernetes Documentation - Volume Snapshots</a></li>

<li><a href="https://www.linuxfoundation.org/training/kubernetes-storage-patterns/" target="_blank" rel="noopener noreferrer">Linux Foundation - Kubernetes Storage Patterns</a></li>

</ul>

<p>&lt;!-- FIM --&gt;</p>

Comentários

Mais em DevOps & CI/CD

Como Usar Azure para DevOps: AKS, Azure Pipelines e Resource Groups em Produção
Como Usar Azure para DevOps: AKS, Azure Pipelines e Resource Groups em Produção

Azure Resource Groups: Fundação da Organização em Nuvem Um Resource Group (Gr...

Como Usar Segurança em Containers Docker: Rootless, Capabilities e Scanning em Produção
Como Usar Segurança em Containers Docker: Rootless, Capabilities e Scanning em Produção

Segurança em Containers Docker: Rootless, Capabilities e Scanning Quando fala...

Dominando Tekton em Kubernetes: Pipelines Cloud-Native do Zero em Projetos Reais
Dominando Tekton em Kubernetes: Pipelines Cloud-Native do Zero em Projetos Reais

O que é Tekton e por que você precisa aprender Tekton é um framework open-sou...