<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: "3000"
throughput: "125"
encrypted: "true"
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: "securepassword"
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><!-- FIM --></p>