Persistent Volumes e Persistent Volume Claims

1. Introdução ao Armazenamento Persistente no Kubernetes

1.1 Por que armazenamento efêmero não é suficiente (stateless vs. stateful)

Em ambientes Kubernetes, containers e pods são efêmeros por natureza. Quando um pod é reiniciado ou substituído, todo o armazenamento local é perdido. Para aplicações stateless (servidores web, APIs REST) isso é aceitável, mas para bancos de dados, sistemas de arquivos e aplicações que precisam manter estado, o armazenamento efêmero é insuficiente. Aí entram os Persistent Volumes (PV) e Persistent Volume Claims (PVC).

1.2 Visão geral do ciclo de vida de dados em containers e pods

O ciclo de vida dos dados em Kubernetes segue esta hierarquia:

  • Container efêmero: dados desaparecem com o container
  • emptyDir: dados persistem enquanto o pod existir
  • hostPath: dados persistem no nó (não recomendado para produção)
  • PersistentVolume: dados persistem independentemente do ciclo de vida do pod

1.3 O papel do DevOps na gestão de armazenamento persistente

Como profissional DevOps, você precisa:
- Provisionar e gerenciar volumes de forma automatizada
- Garantir alta disponibilidade e recuperação de desastres
- Implementar políticas de backup e snapshot
- Otimizar custos de armazenamento em cloud providers

2. Conceitos Fundamentais: PV e PVC

2.1 Persistent Volume (PV)

Um PV é um recurso no cluster que representa um pedaço de armazenamento provisionado pelo administrador. Ele tem um ciclo de vida independente de qualquer pod.

Ciclo de vida do PV:
- Available: disponível para ser vinculado
- Bound: vinculado a um PVC
- Released: PVC foi deletado, volume aguarda reclaim
- Failed: erro no volume

2.2 Persistent Volume Claim (PVC)

Um PVC é uma solicitação de armazenamento feita pelo usuário. O Kubernetes automaticamente encontra um PV que atenda aos requisitos (capacidade, modo de acesso) e faz o binding.

2.3 StorageClass

StorageClass permite provisionamento dinâmico. Em vez de criar PVs manualmente, você define uma classe de armazenamento que provisiona volumes automaticamente quando um PVC é criado.

3. Criando e Configurando Persistent Volumes

3.1 Exemplo de manifesto YAML para um PV

PV com hostPath (para desenvolvimento local):

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-hostpath
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  hostPath:
    path: /data/volumes/pv1

PV com NFS:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-nfs
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  nfs:
    server: 192.168.1.100
    path: /exports/data

PV com AWS EBS:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-ebs
spec:
  capacity:
    storage: 20Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  awsElasticBlockStore:
    volumeID: vol-0abcd1234efgh5678
    fsType: ext4

3.2 Modos de acesso

  • ReadWriteOnce (RWO): montado como leitura/escrita por um único nó
  • ReadOnlyMany (ROX): montado como somente leitura por múltiplos nós
  • ReadWriteMany (RWX): montado como leitura/escrita por múltiplos nós

3.3 Políticas de reclaim

  • Retain: volume mantido após PVC ser deletado (requer limpeza manual)
  • Delete: volume é deletado automaticamente
  • Recycle: volume é limpo e fica disponível novamente (depreciado)

4. Trabalhando com Persistent Volume Claims

4.1 Manifesto YAML de um PVC

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-exemplo
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: ""  # Deixa vazio para usar PVs estáticos

4.2 Vinculação (binding) automática e manual

Binding automático: o Kubernetes encontra um PV que atenda aos requisitos do PVC.

Binding manual com selector:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-selector
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
  selector:
    matchLabels:
      tipo: "fast-storage"

4.3 Exemplo prático: PVC montado em um Pod

apiVersion: v1
kind: Pod
metadata:
  name: pod-com-volume
spec:
  containers:
  - name: app
    image: nginx
    volumeMounts:
    - name: dados
      mountPath: /usr/share/nginx/html
  volumes:
  - name: dados
    persistentVolumeClaim:
      claimName: pvc-exemplo

Para StatefulSet:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  serviceName: mysql
  replicas: 3
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:8.0
        volumeMounts:
        - name: dados-mysql
          mountPath: /var/lib/mysql
  volumeClaimTemplates:
  - metadata:
      name: dados-mysql
    spec:
      accessModes:
        - ReadWriteOnce
      resources:
        requests:
          storage: 10Gi
      storageClassName: standard

5. Provisionamento Dinâmico com StorageClass

5.1 O que é uma StorageClass

StorageClass permite provisionamento dinâmico de volumes. Você define um provisioner (ex: ebs.csi.aws.com, pd.csi.storage.gke.io) e parâmetros específicos.

5.2 Exemplo de StorageClass para cloud providers

AWS EBS (gp3):

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

GCE Persistent Disk:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: gce-pd-ssd
provisioner: pd.csi.storage.gke.io
parameters:
  type: pd-ssd
  replication-type: none
reclaimPolicy: Retain

Azure Disk:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: azure-disk-premium
provisioner: disk.csi.azure.com
parameters:
  skuname: Premium_LRS
reclaimPolicy: Delete

5.3 Uso de default StorageClass e anotações em PVC

Para definir uma StorageClass como padrão:

kubectl patch storageclass standard -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

Um PVC sem storageClassName usará a StorageClass padrão:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-automatico
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi

6. Boas Práticas e Cenários Comuns para DevOps

6.1 Gerenciamento de estado em StatefulSets

StatefulSets garantem:
- Ordem de deployment (0, 1, 2...)
- Identidade única (pod-0, pod-1)
- Volume dedicado para cada réplica via volumeClaimTemplates

6.2 Backup, snapshot e migração de volumes

Use ferramentas como Velero para backup e restore:

velero backup create meu-backup --include-namespaces producao
velero restore create --from-backup meu-backup

6.3 Segurança: permissões, acesso a NFS e criptografia

  • Defina fsGroup e runAsUser no securityContext do pod
  • Use NetworkPolicies para restringir acesso ao NFS
  • Ative criptografia em repouso no storage class:
    text parameters: encrypted: "true" kmsKeyId: "arn:aws:kms:us-east-1:123456789:key/abc-123"

7. Troubleshooting e Monitoramento de Volumes

7.1 Verificando status de PV e PVC

kubectl get pv
kubectl get pvc -n meu-namespace
kubectl describe pv pv-exemplo
kubectl describe pvc pvc-exemplo -n meu-namespace

7.2 Problemas comuns

PVC pendente:
- Verifique se existe PV com capacidade e modo de acesso compatíveis
- Verifique se a StorageClass existe e está acessível

PV não encontrado:
- Verifique se o PV foi criado no namespace correto
- Verifique se o PVC tem storageClassName vazio (para binding estático)

Conflitos de acesso:
- Um PV com ReadWriteOnce só pode ser montado em um nó por vez

7.3 Logs e eventos: como diagnosticar falhas de montagem

kubectl describe pod pod-com-volume
kubectl logs pod-com-volume -c app
kubectl get events --sort-by='.lastTimestamp'

Eventos comuns:
- FailedMount: erro ao montar volume no pod
- WaitForFirstConsumer: volume aguardando pod ser escalonado
- ProvisioningFailed: erro no provisionamento dinâmico

8. Conclusão

Persistent Volumes e Persistent Volume Claims são fundamentais para aplicações stateful no Kubernetes. Como profissional DevOps, dominar esses conceitos permite gerenciar armazenamento de forma eficiente, automatizada e segura. A combinação de PVs estáticos com StorageClasses dinâmicas oferece flexibilidade para diferentes cenários, desde desenvolvimento local até produção em cloud.

Lembre-se sempre de:
- Usar StatefulSets para aplicações stateful
- Implementar backups com Velero ou ferramentas similares
- Monitorar eventos e logs para troubleshooting rápido
- Seguir boas práticas de segurança e criptografia


Referências