
Curso Kubernetes 11 — Storage: PV, PVC, NFS e local-path
Container é efêmero. Banco de dados, uploads, filas e backups não podem ser. O Kubernetes separa quem usa o storage (pod) de quem fornece (infraestrutura) com três objetos: PV, PVC e StorageClass.
Selo do curso: testado em Kubernetes 1.35, NFS e local-path.
O modelo
| Objeto | Papel |
|---|---|
| Volume | montagem dentro do pod |
| PV (PersistentVolume) | recurso de storage no cluster |
| PVC (PersistentVolumeClaim) | pedido de storage pelo usuário |
| StorageClass | “classe” que provisiona PVs dinamicamente |
| CSI | driver de storage (padrão moderno) |
Fluxo:
PVC (pedido) → StorageClass (provisiona) → PV (disco) → Pod (monta)
Volume efêmero vs persistente
emptyDir: vive enquanto o pod vive. Bom para cache/tmp.hostPath: monta um diretório do nó. Evite em produção; prende o pod ao nó.PVC: persiste além do pod.
volumes:
- name: cache
emptyDir: {}
- name: data
persistentVolumeClaim:
claimName: app-data
PV e PVC na mão
PV:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs-5gi
spec:
capacity:
storage: 5Gi
accessModes: ["ReadWriteMany"]
persistentVolumeReclaimPolicy: Retain
nfs:
server: 192.168.56.50
path: /srv/nfs/app
PVC:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-data
spec:
accessModes: ["ReadWriteMany"]
resources:
requests:
storage: 5Gi
O PVC só vincula se tamanho, access mode e classe baterem. Se ficar Pending, olhe os eventos:
kubectl describe pvc app-data
kubectl get pv
StorageClass e provisionamento dinâmico
Com StorageClass, você não cria PV manualmente:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: portal-nfs
provisioner: nfs.csi.k8s.io
parameters:
server: 192.168.56.50
share: /srv/nfs/portal
reclaimPolicy: Retain
volumeBindingMode: Immediate
No PVC:
spec:
storageClassName: portal-nfs
volumeBindingMode: WaitForFirstConsumer é útil para storage local: o PV só é criado quando o pod é agendado, evitando PVC preso a um nó errado.
Access modes
| Modo | Significado |
|---|---|
| RWO | ReadWriteOnce — um nó por vez |
| ROX | ReadOnlyMany — vários nós, leitura |
| RWX | ReadWriteMany — vários nós, escrita |
| RWOP | ReadWriteOncePod — um pod por vez |
NFS costuma suportar RWX. Discos de bloco normalmente são RWO. local-path é RWO e preso ao nó — se o pod mudar de nó, o volume não acompanha.
O caso do local-path
O local-path-provisioner cria PVs apontando para um diretório do nó. É ótimo para laboratório e para cargas que não migram (cache, monitoramento local).
Problema: o PV tem nodeAffinity para o nó onde foi criado. Se o pod for reagendado para outro nó, o PVC fica Pending.
Sintoma:
pod has unbound immediate PersistentVolumeClaims
ou o pod fica Pending com evento de node affinity. Solução: usar storage compartilhado (NFS/Ceph) ou aceitar que a carga é presa ao nó.
StatefulSet e PVC
StatefulSet cria um PVC por réplica:
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: portal-nfs
resources:
requests:
storage: 5Gi
O PVC tem o nome <template>-<statefulset>-<ordinal>. Deletar o StatefulSet não apaga o PVC por padrão. Isso é bom para recuperação e ruim para limpeza; gerencie com intenção.
Redimensionamento e snapshots
- Redimensionar PVC depende do driver e do
allowVolumeExpansionna StorageClass. - Snapshots dependem de CSI (
VolumeSnapshotClass). - Backup de aplicação não é só volume: banco precisa de dump consistente. Velero ajuda com PV, mas o dump lógico é insubstituível.
Laboratório
- Crie um PVC NFS e monte em um pod:
kubectl apply -f pvc-nfs.yaml
kubectl run app --image=nginx:1.27 --overrides='
{"spec":{"containers":[{"name":"app","image":"nginx:1.27",
"volumeMounts":[{"name":"data","mountPath":"/data"}]}],
"volumes":[{"name":"data","persistentVolumeClaim":{"claimName":"app-data"}}]}}'
kubectl exec app -- sh -c 'echo hello > /data/teste && cat /data/teste'
- Delete o pod e recrie; o arquivo deve continuar.
- Crie um PVC com
local-pathe simule reagendamento para outro nó — veja o PVC ficar preso. - Inspecione
kubectl get pv,pvce os eventos.
Erros comuns
| Sintoma | Causa |
|---|---|
PVC Pending |
sem PV/StorageClass, tamanho ou access mode incompatível |
Pod Pending com PVC |
node affinity do PV local |
Permission denied |
UID/GID ou permissão no NFS |
| Dados “sumiram” | pod montou emptyDir em vez do PVC |
| PVC não expande | StorageClass sem allowVolumeExpansion |
| RWX não funciona | driver não suporta múltiplos nós |
Checklist
- [ ] Sei a diferença entre emptyDir, hostPath e PVC.
- [ ] StorageClass padrão definida.
- [ ] Access mode correto para a carga.
- [ ] Sei se o storage é preso ao nó (local-path).
- [ ] StatefulSet tem
volumeClaimTemplates. - [ ] Backup do volume e dump lógico testados.
Glossário
- PV: recurso de storage.
- PVC: pedido de storage.
- StorageClass: provisionador dinâmico.
- CSI: Container Storage Interface.
- RWX/RWO: modos de acesso.
- Reclaim policy: o que acontece com o PV ao deletar o PVC.
Próximo
Agora que os dados persistem, vamos configurar a aplicação sem rebuildar imagem: ConfigMap, Secret e o guard que já derrubou um deploy nosso.
Próximo: Curso Kubernetes 12 — ConfigMap, Secret e o guard de credenciais
Continuar a ler
Leia também
KubernetesKubernetes de Verdade — do zero ao cluster em produção
Uma trilha prática para entender, montar e operar um cluster Kubernetes — do primeiro pod ao failover do Postgres.
4 min de leitura
KubernetesCurso Kubernetes 28 — DR e capstone: provar que o cluster volta
RPO, RTO e drills reais: cenários de desastre, runbook, restore medido e o capstone que fecha o curso com um cluster que se recupera.
5 min de leitura
KubernetesCurso Kubernetes 27 — SLO/SLI, on-call e postmortem blameless
O que prometer, como medir e o que fazer quando o erro estoura: SLI, SLO, error budget, alertas que acordam alguém e postmortem sem caça às bruxas.
4 min de leitura
Comentários
Ainda sem comentários
Seja o primeiro a compartilhar sua opinião sobre este artigo.
Deixe um comentário