Adicionando NFS como Volume Storage no Kubernetes

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: site-nginx-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 automatico 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 Comment

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s