No Kubernetes, pods são efêmeros. Quando um pod morre, seus dados locais são perdidos. Para aplicações que precisam salvar dados permanentemente (bancos de dados, repositórios de arquivos), precisamos de Armazenamento Persistente.
Neste guia, configuraremos um servidor NFS (Network File System) dedicado e integraremos o Kubernetes a ele usando o NFS Subdir External Provisioner. Isso permite que o Kubernetes crie pastas dinamicamente no servidor NFS sempre que um pod solicitar armazenamento, sem necessidade de intervenção manual.
A arquitetura consiste em:
storage.geanmartins.net): Uma VM dedicada com um disco extra (LVM) exportando um diretório via NFS.nfs-common para montar os volumes.Conecte-se à VM dedicada ao storage.
sudo hostnamectl set-hostname storage.geanmartins.net
Nota: O disco extra (
/dev/vdb) de 512GB foi adicionado na infraestrutura criada pelo Terraform.
Verifique se o disco está disponível:
lsblk -l
Vamos usar LVM (Logical Volume Manager). Isso permite redimensionar o disco facilmente no futuro sem precisar formatar ou perder dados.
Defina as variáveis para o particionamento:
MOUNT_POINT=/kube
DISK_DEVICE=/dev/vdb
VG_NAME=vg-kube
LV_NAME=lv-kube
Crie o Physical Volume (PV), Volume Group (VG) e Logical Volume (LV) usando 100% do espaço livre:
sudo pvcreate ${DISK_DEVICE}
sudo vgcreate ${VG_NAME} ${DISK_DEVICE}
sudo lvcreate -l 100%FREE -n ${LV_NAME} ${VG_NAME}
Formate o volume lógico com o sistema de arquivos XFS (excelente para grandes volumes de dados) e crie o diretório de montagem:
sudo mkfs.xfs /dev/mapper/${VG_NAME}-${LV_NAME}
sudo mkdir ${MOUNT_POINT}
Adicione ao /etc/fstab para montar automaticamente no boot:
sudo cp -p /etc/fstab{,.dist}
echo "/dev/mapper/${VG_NAME}-${LV_NAME} ${MOUNT_POINT} xfs defaults 1 2" | sudo tee -a /etc/fstab
Monte o disco e verifique o espaço disponível:
sudo systemctl daemon-reload
sudo mount -a
df -hT | grep kube
Instale o pacote utilitário do NFS (o comando varia conforme a distribuição, aqui assumimos base RHEL/Oracle/Fedora pelo uso do dnf, se for Debian use apt install nfs-kernel-server):
sudo dnf install nfs-utils
Crie o diretório que será exportado para o Kubernetes e ajuste as permissões. Usamos nobody:nobody e 0777 para garantir que qualquer pod, independente do usuário que esteja rodando internamente, consiga escrever no disco.
sudo mkdir -p /kube/datas
sudo chown nobody:nobody /kube/datas
sudo chmod 0777 /kube/datas
Configure o arquivo /etc/exports para permitir acesso às redes IPv4 e IPv6 do cluster:
echo '/kube/datas 10.48.9.0/24(rw,sync,no_root_squash,no_subtree_check)' | sudo tee /etc/exports
echo '/kube/datas fd00:0:b:9::/64(rw,sync,no_root_squash,no_subtree_check)' | sudo tee -a /etc/exports
Explicação dos Parâmetros NFS:
rw: Permite leitura e escrita.sync: Força a gravação síncrona no disco (mais seguro contra perda de dados em quedas de energia).no_root_squash: Permite que o usuário root do pod atue como root no diretório NFS (necessário para que o provisioner consiga criar pastas).no_subtree_check: Melhora a performance desativando a checagem de subárvores.Habilite, inicie o serviço e exporte as configurações:
sudo systemctl enable nfs-server --now
sudo exportfs -s
Libere as portas no Firewall (firewalld):
sudo firewall-cmd --permanent --add-service={nfs,mountd,rpc-bind}
sudo firewall-cmd --reload
Para que o Kubernetes consiga montar os volumes NFS nos pods, todos os nós workers precisam ter o cliente NFS instalado.
Execute o comando abaixo em todos os workers (kube-worker-01, 02, 03):
sudo apt update
sudo apt install nfs-common
Para validar se o worker consegue enxergar as exportações do storage, execute:
sudo showmount -e storage.geanmartins.net
Saída Esperada:
Export list for storage.geanmartins.net:
/kube/datas fd00:0:b:9::/64,10.48.9.0/24
O provisioner é um pod que roda no cluster. Quando você cria um PersistentVolumeClaim (PVC), este pod detecta o pedido, vai até o servidor NFS, cria uma pasta com o nome do PVC, e devolve um PersistentVolume (PV) apontando para essa pasta.
Execute os comandos abaixo a partir de um control plane (ex: kube-ctrl-01).
Adicione o repositório e instale o provisioner:
helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
helm repo update
Instale o provisioner configurando-o como a StorageClass padrão e com política Retain (se o PVC for deletado, a pasta no NFS não é apagada, apenas arquivada):
helm install nfs-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
--version 4.0.18 \
--namespace nfs-provisioner --create-namespace \
--set nfs.server=storage.geanmartins.net \
--set nfs.path=/kube/datas \
--set storageClass.name=nfs-client \
--set storageClass.defaultClass=true \
--set storageClass.reclaimPolicy=Retain
Verifique se o pod do provisioner está rodando:
kubectl get pod -n nfs-provisioner
A StorageClass instalada acima usa Retain (seguro para bancos de dados). No entanto, para caches ou dados temporários, é útil ter uma classe que apague a pasta do NFS quando o PVC for deletado (Delete).
Crie a StorageClass efêmera:
kubectl apply -f - <<'EOF'
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-client-ephemeral
provisioner: cluster.local/nfs-provisioner-nfs-subdir-external-provisioner
parameters:
archiveOnDelete: "false"
reclaimPolicy: Delete
volumeBindingMode: Immediate
allowVolumeExpansion: true
EOF
Verifique as StorageClasses disponíveis:
kubectl get storageclass
Resumo das StorageClasses:
nfs-client (Padrão): ReclaimPolicy: Retain. Uso: Dados críticos, bancos de dados, configurações. (Dados são preservados ao deletar o PVC).nfs-client-ephemeral: ReclaimPolicy: Delete. Uso: Dados temporários, caches, logs. (Dados são apagados fisicamente do storage ao deletar o PVC).Vamos testar se o cluster consegue criar discos dinamicamente em ambas as StorageClasses.
# 1. PVC Crítico (Usará a StorageClass padrão 'nfs-client' com Retain)
kubectl apply -f - <<EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: example-critical-data
namespace: default
spec:
accessModes:
- ReadWriteMany
storageClassName: nfs-client
resources:
requests:
storage: 10Gi
EOF
# 2. PVC Efêmero (Usará a StorageClass 'nfs-client-ephemeral' com Delete)
kubectl apply -f - <<EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: example-temp-data
namespace: default
spec:
accessModes:
- ReadWriteMany
storageClassName: nfs-client-ephemeral
resources:
requests:
storage: 5Gi
EOF
Verifique se os PVCs mudaram do status Pending para Bound (vinculado). Isso significa que o provisioner conseguiu criar as pastas no storage NFS com sucesso.
kubectl get pvc
Saída Esperada:
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
example-critical-data Bound pvc-7930b89e-42ba-4a5f-97ae-0f6a1fd0f11f 10Gi RWX nfs-client 6m
example-temp-data Bound pvc-a003ea8f-7976-46b4-8487-17b139c988ae 5Gi RWX nfs-client-ephemeral 5s
Dica: O acesso
RWX(ReadWriteMany) significa que o volume pode ser montado por múltiplos pods simultaneamente em modo de leitura e escrita. Esta é a grande vantagem do NFS sobre discos de bloco (como iSCSI/EBS).
Delete os PVCs de teste:
kubectl delete pvc example-temp-data
kubectl delete pvc example-critical-data
O que acontece no storage?
example-temp-data foi deletada fisicamente do servidor NFS.example-critical-data foi renomeada para archived-pvc-... no servidor NFS, preservando os dados para recuperação manual se necessário.