Quase todas as aplicações do mundo real em algum momento precisarão persistir informações em algum lugar. Seja em um banco de dados ou em um arquivo o fato é que os containers são efêmeros. Depois que o seu ciclo de vida termina tudo que estiver em memória ou em arquivos temporários irá desaparecer, e esse é o funcionamento esperado. Para que os containers possam persistir de forma perene as informações que queremos, utilizamos volumes.
Persistência no Kubernetes
Os volumes, de forma bem simplificada, são análogos a compartilhamentos de pastas de rede. Você a partir do seu notebook acessa o diretório de um outro notebook através da rede e, partir daí, pode ler e escrever dados no outro notebook. Como disse, apenas para reforçar essa analogia é uma forma bem simplificada de se explicar o conceito do uso de Volumes porém de maneira alguma estou dizendo que eles são a mesma coisa, ok?
O Kubernetes por ser um orquestrador de containers e ter que lidar de forma mais abstrata com os padrões não traz nenhuma implementação de Volume nativa. Contudo possui suporte para uma infinidade de padrões e tecnologias diferentes de Provisionadores (provisioners) que você pode conferir na documentação do Kubernetes.
Para que possamos utilizar os volumes no Kubernetes temos dois objetos que interagem com os provisionadores que mencionei acima. Isso permite que os desenvolvedores criei e removam volumes de acordo com a sua necessidade sem a necessidade de que ele tenha o conhecimento da tecnologia de Storage que a companhia está utilizando, o que traz liberdade para a empresa utilizar aquela que melhor lhe convier ou, que precise solicitar para a área responsável o provisionamento deste espaço o que reduz drasticamente o tempo de desenvolvimento.
Além disso, como o provisionamento é realizado de forma dinâmica quando um Volume é removido aquele espaço instantaneamente passa a estar disponível para uso.
PV e PVC
Agora que falamos da teoria e dos benefícios do uso de Volumes podemos falar um pouco o que são esses dois objetos.
PV – Persistent Volume
É um recurso dentro do Cluster assim como um nó. Os PVs são plugins de armazenamento assim como os Volumes porém se diferenciam por terem um ciclo de vida independente de qualquer pod que o utilize. O seu objetivo é fazer uma ponte entre um dispositivo de armazenamento físico podendo ser NFS, iSCSI, ou um armazenamento específico de um provedor de cloud pública e o Cluster.
PVC – Persistent Volume Claim
É uma solicitação de armazenamento feito pelo usuário. Os PVCS consomem recursos que foram criados pelos PVs. Isso quer dizer que um PVC irá sempre consumir um PV e nunca o contrário.
Para que um volume possa ser disponibilizado primeiro precisamos criar um PV que irá fazer a ponte com o nosso dispositivo físico. Depois utilizamos o PVC para dizer o tamanho e o associamos a um volume que utilizaremos dentro de um Pod.

Arquitetura de Referência
Para nossa arquitetura de referência iremos utilizar um típico cluster Kubernetes composto de 3 máquinas fazendo o Control Plane e mais 3 máquinas como Worker Nodes. A api do Kubernetes é exposta por duas máquinas rodando o HA Proxy fazendo o Load Balance e o KeepAlived fazendo a troca do IP VIP caso algum dos nós fique indisponível.

Como Storage externo foi adicionado o servidor nfs-server-001 que está rodando o NFS Server expondo um disco separado que será utilizado pelo Cluster para se criar novos volumes de forma dinâmica para os pods.
Todas as máquinas são virtuais e foram provisionadas no KVM e estão conectadas a um Switch de Rede Virtual que cria uma rede NAT. O sistema operacional de todas as máquinas é o Debian 11.
Configuração do servidor NFS
No meu post anterior eu já havia criado um cluster do Kubernetes porém este motivo irei somente colocar aqui a parte relativa ao servidor adicional do Storage.
A configuração em si é muito simples, primeiro precisamos instalar os pacotes do NFS-Server, para isso faça um SSH no servidor nfs-server-001 e rode os seguintes comandos:
sudo apt install -y nfs-kernel-server nfs-common portmap
Agora vamos iniciar o serviço do NFS:
sudo systemctl start nfs-server
Agora vamos criar um diretório que será exposto pelo NFS.
sudo mkdir -p /srv/nfs/data
Edite o arquivo /etc/exports e adicione na última linha do arquivo:
/srv/nfs/data *(rw,sync,no_subtree_check,no_root_squash,insecure)
Você precisará adicionar no seu arquivo a linha destacada no arquivo acima. Nela os parâmetros configuram:
- rw – habilita a leitura-escrita do volume.
- sync – as alterações nos arquivos são aplicadas instantaneamente.
- no_subtree_check – utilizado quando somente um subdiretório do sistema de arquivo local é exportado.
- no_root_squash – faz o mapeamento usuário e grupo do client NFS para o usuário e grupo local root.
- insecure – permite o uso de qualquer porta disponível para ser utilizada pelo NFS.
Após alterado vamos informar ao NFS para disponibilizar o diretório:
sudo exportfs -rv
Para confirmar que a alteração foi aplicada:
$ sudo showmount -e
Export list for nfs-server-001:
/srv/nfs/data *
A parte do servidor NFS a configuração está concluída agora precisaremos apenas instalar o cliente do NFS nos Worker Nodes. Para isso, basta nos conectar a cada um deles e rodar o seguinte comando:
sudo apt install -y nfs-client
Criação de um PV e PVC manual
Depois de termos configurado toda nossa infraestrutura de armazenamento já podemos fazer uso dela. Primeiro vamos criar o PV, para isso crie um arquivo com o nome manual-nfs-pv.yaml com o conteúdo abaixo:
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-manual-pv
spec:
capacity:
storage: 10Gi
volumeMode: Filesystem
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs
mountOptions:
- hard
nfs:
path: /srv/nfs/data
server: 192.168.122.40
Agora aplique o manifesto com o comando:
kubectl apply -f manual-nfs-pv.yaml
Agora crie um arquivo chamado manual-nfs-pvc.yaml com esse conteúdo:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-manual-pvc
spec:
storageClassName: nfs
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
Aplique o manifesto:
kubectl apply -f manual-nfs-pvc.yaml
Para confirmarmos que tudo foi criado corretamente execute o comando
kubectl get pv,pvc
Teste com PV e PVC provisionados de forma manual
Agora vamos fazer alguns testes para que você possa entender como podemos utilizar os volumes de forma prática.
Cenário de teste
Vamos disponibilizar um site web em que as suas páginas são obtidas a partir de um volume criado apontando para um Storage NFS no servidor nfs-server-001. Como já criamos o volume no passo anterior somente é necessário a criação do manifesto de deploy do nosso Site.
Crie um arquivo chamado nginx-site-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: site-qa
namespace: site
spec:
replicas: 1
selector:
matchLabels:
app: site-nginx
template:
metadata:
labels:
app: site-nginx
env: qa
spec:
volumes:
- name: site-nginx-html
persistentVolumeClaim:
claimName: nfs-manual-pvc
containers:
- image: nginx
name: nginx
ports:
- containerPort: 80
volumeMounts:
- name: site-nginx-html
mountPath: /usr/share/nginx/html
resources:
requests:
memory: "64Mi"
cpu: "150m"
limits:
memory: "128Mi"
cpu: "250m"
- Linha 20 – claimName: aqui colocamos o mesmo nome do PVC que foi criado
- Linha 27 – name: o nome precisa ser igual ao informado no claimName volume
- Linha 28 – mountPath: ponto de montagem dentro do container
Aplique o manifesto
$ kubectl apply -f nginx-site-deployment.yaml
deployment.apps/deploy-site created
Vamos aguardar até que o pod esteja em Running para que possamos testar, para isso rode o comando abaixo e aguarde até que o status do pod seja Running 1/1.
kubectl get pods
NAME READY STATUS RESTARTS AGE
site-qa-64df7d8c9d-52k77 1/1 Running 0 3m20s
Agora vamos criar um serviço simples para que possamos ver as páginas que estão no volume. Para isso crie o arquivo nginx-service.yaml com o seguinte conteúdo:
apiVersion: v1
kind: Service
metadata:
name: svc-qa
spec:
selector:
app: site-nginx
ports:
- protocol: TCP
port: 80
Aplique o manifesto
$ kubectl apply -f nginx-service.yaml
service/svc-qa created
Vamos ver se o serviço foi criado corretamente:
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc-qa ClusterIP 10.100.133.117 <none> 80/TCP 2m59s
Estamos quase lá! Para realizarmos verdadeiramente um teste precisamos enviar para o volume uma página HTML personalizada. Com isso podemos confirmar que ele realmente está lendo esta página de um volume e que quando o container deixar de existir a página não será perdida quando ele reiniciar novamente.
Primeiro vamos criar a nossa página de validação, para isso crie um novo arquivo com o nome index.html e cole o conteúdo abaixo:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta charset="utf-8">
<title>NFS VOLUME</title>
<meta name="description" content="Simple page demonstration NFS Volume">
<meta name="author" content="QuickwinsIT">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<p>Hello NFS Volume <strong>Manual</strong>.</p>
</body>
</html>
Para enviarmos o arquivo para o volume podemos fazer de duas maneiras. A primeira seria o de montar o volume NFS na nossa estação de trabalho. A outra opção seria a de mandar diretamente o arquivo para o POD que está rodando o NGINX.
Montando volume NFS em uma estação Linux
Montar um volume NFS é bem simples, contudo você precisa ter instalado na sua estação de trabalho o cliente do NFS. Para fazer a instalação caso não tenha rode o seguinte comando:
sudo apt update && sudo apt install -y nfs-client
Depois de instalado o pacote crie um diretório onde você ira montar o volume NFS do servidor:
sudo mkdir -p /mnt/nfs
Para finalizar só precisamos monta no mount point que acabamos de criar. Para isso execute o comando:
sudo mount -t nfs <Endereço-IP-Servidor-NFS>:/srv/nfs/data /mnt/nfs
Agora é só copiar o arquivo index.html que você havia criado:
sudo cp index.html /mnt/nfs
Copiando o arquivo diretamente para o Pod.
Para realizarmos a cópia precisamos apenas saber o nome do pod que está rodando a instancia do nginx que iniciamos lá no passo 1 do cenário de teste. Para isso basta executar o seguinte comando:
kubectl get pods -l app=site-nginx
NAME READY STATUS RESTARTS AGE
site-qa-64df7d8c9d-52k77 1/1 Running 0 7m6s
Anote o nome do pod da coluna NAME e em seguida rode o comando abaixo:
kubectl cp index.html site-qa-64df7d8c9d-52k77:/usr/share/nginx/html/index.html
Executando os testes
Para verificarmos se o Nginx está carregando a página index.html que criamos precisamos realizar um port-foward do serviço para o nosso computador local. Isto é necessário porque não estamos utilizando um Load Balancer que, neste caso, nos forneceria um IP válido o qual poderíamos acessar.
Primeiro vamos obter novamente as informações do serviço:
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc-qa ClusterIP 10.100.133.117 <none> 80/TCP 6m27s
Agora vamos fazer o port-forward para alguma porta disponível no nosso computador local.
$ kubectl port-forward service/svc-qa :80
Forwarding from 127.0.0.1:43891 -> 80
Forwarding from [::1]:43891 -> 80
Note que ele nos retorna uma porta, nesse caso 43891, provavelmente na sua máquina será retornado um outro valor. Para o nosso teste iremos utilizar então a URL: htt://127.0.0.1:43891
Caso você tenha executado um dos passos anteriores para enviar o arquivo index.html é só acessar o browser que veremos a página abaixo.

Caso prefira pode também utilizar o curl para verificar:
$ curl http://localhost:43891
Agora vamos fazer um último teste para verificarmos que efetivamente o arquivo que acabamos de criar está sendo persistido corretamente no volume NFS. Para validarmos isso é muito simples, vamos remover o deployment, pod e o serviço que acabamos de criar. Porém deixaremos intacto o volume.
$ kubectl delete deployment deploy-site
deployment.apps "deploy-site" deleted
Quando removemos o deployment o kubernetes automaticamente ira remover também o pod associado. Agora só falta remover o serviço:
$ kubectl delete svc svc-qa
service "svc-qa" deleted
Reaplicando os manifestos :
$ kubectl apply -f .
service/svc-qa created
deployment.apps/site-qa created
Realize o port-foward:
$ kubectl port-forward svc/svc-qa :80
Forwarding from 127.0.0.1:35189 -> 80
Forwarding from [::1]:35189 -> 80
Note que porta agora mudou e por isso a url do teste também foi alterada para: http://127.0.0.1:35189

Com esse último passo encerramos a primeira parte desse tutorial em que utilizamos o NFS como provedor de Storage. O funcionamento está correto porém, ainda necessitando que os PVs tenham que ter ser criados de forma manual, para depois criarmos o PVC. Além disso, precisamos informar o tipo de Storage que está sendo utilizado deixando uma parte importante da Governança do Cluster aberta a mais de um padrão, o que não é recomendado.
Na segunda parte iremos habilitar o provisionamento automático de volumes fazendo com que seja necessário apenas a criação de um PVC e nada mais. Tanto a classe de Storage quanto a criação do PV estarão configuradas previamente.
Não perca!
1 comentário Adicione o seu