Implemente uma carga de trabalho com estado com o Filestore

Este tutorial mostra como implementar uma carga de trabalho com estado de leitura/gravação simples usando um volume persistente (PV) e uma reivindicação de volume persistente (PVC) no Google Kubernetes Engine (GKE). Siga este tutorial para saber como criar designs para escalabilidade usando o Filestore, o sistema de ficheiros de rede gerido da Google Cloud.

Contexto

Por natureza, os pods são efémeros. Isto significa que o GKE destrói o estado e o valor armazenados num pod quando este é eliminado, removido ou reagendado.

Como operador de aplicações, pode querer manter cargas de trabalho com estado. Alguns exemplos destas cargas de trabalho incluem aplicações que processam artigos do WordPress, apps de mensagens e apps que processam operações de aprendizagem automática.

Ao usar o Filestore no GKE, pode realizar as seguintes operações:

  • Implemente cargas de trabalho com estado que sejam escaláveis.
  • Permitir que vários pods tenham o ReadWriteMany como o respetivo accessMode, para que vários pods possam ler e escrever em simultâneo no mesmo armazenamento.
  • Configure o GKE para montar volumes em vários pods em simultâneo.
  • Persistir o armazenamento quando os pods são removidos.
  • Permitir que os pods partilhem dados e sejam facilmente dimensionados.

Configure o armazenamento de ficheiros gerido com o Filestore através da CSI

O GKE oferece uma forma de implementar e gerir automaticamente o controlador CSI do Kubernetes Filestore nos seus clusters. A utilização do CSI do Filestore permite-lhe criar ou eliminar dinamicamente instâncias do Filestore e usá-las em cargas de trabalho do Kubernetes com um StorageClass ou um Deployment.

Pode criar uma nova instância do Filestore criando um PVC que aprovisiona dinamicamente uma instância do Filestore e o PV, ou aceder a instâncias do Filestore pré-aprovisionadas em cargas de trabalho do Kubernetes.

Nova instância

Crie a classe de armazenamento

apiVersion: storage.k8s.io/v1 kind: StorageClass metadata:   name: filestore-sc provisioner: filestore.csi.storage.gke.io volumeBindingMode: Immediate allowVolumeExpansion: true parameters:   tier: standard   network: default
  • O valor de volumeBindingMode está definido como Immediate, o que permite que o aprovisionamento do volume comece imediatamente.
  • tier está definido como standard para um tempo de criação de instância do Filestore mais rápido. Se precisar de um armazenamento NFS disponível mais elevado, instantâneos para cópia de segurança de dados, replicação de dados em várias zonas e outras funcionalidades ao nível empresarial, defina tier como enterprise. Nota: a política de recuperação para PVs criadas dinamicamente é predefinida como Delete se o reclaimPolicy no StorageClass não estiver definido.
  1. Crie o recurso StorageClass:

    kubectl create -f filestore-storageclass.yaml 
  2. Verifique se a classe de armazenamento foi criada:

    kubectl get sc 

    O resultado é semelhante ao seguinte:

    NAME                     PROVISIONER                    RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE filestore-sc             filestore.csi.storage.gke.io   Delete          Immediate              true                   94m 

Instância pré-aprovisionada

Crie a classe de armazenamento

apiVersion: storage.k8s.io/v1 kind: StorageClass metadata:   name: filestore-sc provisioner: filestore.csi.storage.gke.io volumeBindingMode: Immediate allowVolumeExpansion: true

Quando volumeBindingMode está definido como Immediate, permite que o aprovisionamento do volume comece imediatamente.

  1. Crie o recurso StorageClass:

      kubectl create -f preprov-storageclass.yaml 
  2. Verifique se a classe de armazenamento foi criada:

      kubectl get sc 

    O resultado é semelhante ao seguinte:

      NAME                     PROVISIONER                    RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE   filestore-sc             filestore.csi.storage.gke.io   Delete          Immediate              true                   94m 

Crie um volume persistente para a instância do Filestore

apiVersion: v1 kind: PersistentVolume metadata:   name: fileserver   annotations:     pv.kubernetes.io/provisioned-by: filestore.csi.storage.gke.io spec:   storageClassName: filestore-sc   capacity:     storage: 1Ti   accessModes:     - ReadWriteMany   persistentVolumeReclaimPolicy: Delete   volumeMode: Filesystem   csi:     driver: filestore.csi.storage.gke.io     # Modify this to use the zone, filestore instance and share name.     volumeHandle: "modeInstance/<LOCATION>/<INSTANCE_NAME>/<FILE_SHARE_NAME>"     volumeAttributes:       ip: <IP_ADDRESS> # Modify this to Pre-provisioned Filestore instance IP       volume: <FILE_SHARE_NAME> # Modify this to Pre-provisioned Filestore instance share name
  1. Verifique se a instância do Filestore pré-existente está pronta:

      gcloud filestore instances list 

    A saída é semelhante à seguinte, em que o valor STATE é READY:

      INSTANCE_NAME: stateful-filestore   LOCATION: us-central1-a   TIER: ENTERPRISE   CAPACITY_GB: 1024   FILE_SHARE_NAME: statefulpath   IP_ADDRESS: 10.109.38.98   STATE: READY   CREATE_TIME: 2022-04-05T18:58:28 

    Tome nota do INSTANCE_NAME, LOCATION, FILE_SHARE_NAME e IP_ADDRESS da instância do Filestore.

  2. Preencha as variáveis da consola da instância do Filestore:

      INSTANCE_NAME=INSTANCE_NAME   LOCATION=LOCATION   FILE_SHARE_NAME=FILE_SHARE_NAME   IP_ADDRESS=IP_ADDRESS 
  3. Substitua as variáveis dos marcadores de posição pelas variáveis da consola obtidas acima no ficheiro preprov-pv.yaml:

      sed "s/<INSTANCE_NAME>/$INSTANCE_NAME/" preprov-pv.yaml > changed.yaml && mv changed.yaml preprov-pv.yaml   sed "s/<LOCATION>/$LOCATION/" preprov-pv.yaml > changed.yaml && mv changed.yaml preprov-pv.yaml   sed "s/<FILE_SHARE_NAME>/$FILE_SHARE_NAME/" preprov-pv.yaml > changed.yaml && mv changed.yaml preprov-pv.yaml   sed "s/<IP_ADDRESS>/$IP_ADDRESS/" preprov-pv.yaml > changed.yaml && mv changed.yaml preprov-pv.yaml 
  4. Crie a PV

      kubectl apply -f preprov-pv.yaml 
  5. Verifique se o STATUS do PV está definido como Bound:

      kubectl get pv 

    O resultado é semelhante ao seguinte:

      NAME        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                STORAGECLASS    REASON   AGE   fileserver  1Ti        RWX            Delete           Bound    default/fileserver   filestore-sc             46m 

Use um PersistentVolumeClaim para aceder ao volume

O manifesto pvc.yaml seguinte faz referência ao StorageClass do controlador CSI do Filestore denominado filestore-sc.

Para que vários Pods leiam e escrevam no volume, o accessMode está definido como ReadWriteMany.

kind: PersistentVolumeClaim apiVersion: v1 metadata:   name: fileserver spec:   accessModes:   - ReadWriteMany   storageClassName: filestore-sc   resources:     requests:       storage: 1Ti
  1. Implemente o PVC:

    kubectl create -f pvc.yaml 
  2. Verifique se o PVC foi criado:

    kubectl get pvc 

    O resultado é semelhante ao seguinte:

    NAME         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS        AGE fileserver   Bound    pvc-aadc7546-78dd-4f12-a909-7f02aaedf0c3   1Ti        RWX            filestore-sc        92m 
  3. Verifique se a instância do Filestore recém-criada está pronta:

    gcloud filestore instances list 

    O resultado é semelhante ao seguinte:

    INSTANCE_NAME: pvc-5bc55493-9e58-4ca5-8cd2-0739e0a7b68c LOCATION: northamerica-northeast2-a TIER: STANDARD CAPACITY_GB: 1024 FILE_SHARE_NAME: vol1 IP_ADDRESS: 10.29.174.90 STATE: READY CREATE_TIME: 2022-06-24T18:29:19 

Crie um Pod de leitor e um Pod de escritor

Nesta secção, cria um Pod de leitura e um Pod de escrita. Este tutorial usa implementações do Kubernetes para criar os pods. Uma implementação é um objeto da API Kubernetes que lhe permite executar várias réplicas de pods distribuídas entre os nós num cluster.

Crie o pod leitor

O leitor Pod lê o ficheiro que está a ser escrito pelos escritores Pods. Os pods de leitura veem a hora e a réplica do pod de gravação que escreveu no ficheiro.

apiVersion: apps/v1 kind: Deployment metadata:   name: reader spec:   replicas: 1   selector:     matchLabels:       app: reader   template:     metadata:       labels:         app: reader     spec:       containers:       - name: nginx         image: nginx:stable-alpine         ports:         - containerPort: 80         volumeMounts:         - name: fileserver           mountPath: /usr/share/nginx/html # the shared directory            readOnly: true       volumes:       - name: fileserver         persistentVolumeClaim:           claimName: fileserver

O agrupamento de leitores vai ler a partir do caminho /usr/share/nginx/html, que é partilhado entre todos os agrupamentos.

  1. Implemente o pod de leitor:

    kubectl apply -f reader-fs.yaml 
  2. Verifique se as réplicas de leitura estão em execução consultando a lista de pods:

    kubectl get pods 

    O resultado é semelhante ao seguinte:

    NAME                      READY   STATUS    RESTARTS   AGE reader-66b8fff8fd-jb9p4   1/1     Running   0          3m30s 

Crie o grupo de escritores

O pod de gravação escreve periodicamente num ficheiro partilhado ao qual outros pods de gravação e leitura podem aceder. O Pod de gravação regista a sua presença escrevendo o nome do anfitrião no ficheiro partilhado.

A imagem usada para o pod do escritor é uma imagem personalizada do Alpine Linux, que é usada para utilitários e aplicações de produção. Inclui um script indexInfo.html que obtém os metadados do autor mais recente e mantém a contagem de todos os autores únicos e o total de escritas.

apiVersion: apps/v1 kind: Deployment metadata:   name: writer spec:   replicas: 2 # start with 2 replicas   selector:     matchLabels:       app: writer   template:     metadata:       labels:         app: writer     spec:       containers:       - name: content         image: us-docker.pkg.dev/google-samples/containers/gke/stateful-workload:latest         volumeMounts:         - name: fileserver           mountPath: /html # the shared directory         command: ["/bin/sh", "-c"]         args:         - cp /htmlTemp/indexInfo.html /html/index.html;           while true; do           echo "<b> Date :</b> <text>$(date)</text> <b> Writer :</b> <text2> ${HOSTNAME} </text2> <br>  " >> /html/indexData.html;           sleep 30;             done       volumes:       - name: fileserver         persistentVolumeClaim:           claimName: fileserver

Para este tutorial, o pod de gravação escreve a cada 30 segundos no caminho /html/index.html. Modifique o valor do número sleep para ter uma frequência de gravação diferente.

  1. Implemente o pod do escritor:

    kubectl apply -f writer-fs.yaml 
  2. Verifique se os pods de gravação estão em execução consultando a lista de pods:

    kubectl get pods 

    O resultado é semelhante ao seguinte:

    NAME                      READY   STATUS    RESTARTS   AGE reader-66b8fff8fd-jb9p4   1/1     Running   0          3m30s writer-855565fbc6-8gh2k   1/1     Running   0          2m31s writer-855565fbc6-lls4r   1/1     Running   0          2m31s 

Exponha e aceda à carga de trabalho do leitor a um balanceador de carga de serviço

Para expor uma carga de trabalho fora do cluster, crie um serviço do tipo LoadBalancer. Este tipo de serviço cria um balanceador de carga externo com um endereço IP acessível através da Internet.

  1. Crie um serviço do tipo LoadBalancer com o nome reader-lb:

    kubectl create -f loadbalancer.yaml 
  2. Monitorize a implementação para ver se o GKE atribui um EXTERNAL-IP ao serviço reader-lb:

    kubectl get svc --watch 

    Quando o Service estiver pronto, a coluna EXTERNAL-IP apresenta o endereço IP público do balanceador de carga:

      NAME         TYPE           CLUSTER-IP    EXTERNAL-IP     PORT(S)        AGE   kubernetes   ClusterIP      10.8.128.1    <none>          443/TCP        2d21h   reader-lb    LoadBalancer   10.8.131.79   34.71.232.122   80:32672/TCP   2d20h 
  3. Prima Ctrl+C para terminar o processo de visualização.

  4. Use um navegador de Internet para navegar para o EXTERNAL-IP atribuído ao equilibrador de carga. A página é atualizada a cada 30 segundos. Quanto mais escritores de podcasts e mais curta for a frequência, mais entradas são apresentadas.

Para ver mais detalhes sobre o serviço de balanceamento de carga, consulte loadbalancer.yaml.

Aumente a escala do compositor

Uma vez que o PV accessMode foi definido como ReadWriteMany, o GKE pode aumentar o número de pods para que mais pods de gravação possam escrever neste volume partilhado (ou mais leitores possam lê-los).

  1. Aumente a escala do writer para cinco réplicas:

    kubectl scale deployment writer --replicas=5 

    O resultado é semelhante ao seguinte:

    deployment.extensions/writer scaled 
  2. Valide o número de réplicas em execução:

    kubectl get pods 

    O resultado é semelhante ao seguinte:

    NAME                      READY   STATUS    RESTARTS   AGE reader-66b8fff8fd-jb9p4   1/1     Running   0          11m writer-855565fbc6-8dfkj   1/1     Running   0          4m writer-855565fbc6-8gh2k   1/1     Running   0          10m writer-855565fbc6-gv5rs   1/1     Running   0          4m writer-855565fbc6-lls4r   1/1     Running   0          10m writer-855565fbc6-tqwxc   1/1     Running   0          4m 
  3. Use um navegador de Internet para navegar novamente para o EXTERNAL-IP atribuído ao equilibrador de carga.

Neste ponto, configurou e dimensionou o cluster para suportar cinco pods de gravação com estado. Quando vários pods de gravação estão a escrever no mesmo ficheiro em simultâneo. Os leitores Pods também podem ser facilmente expandidos.

Opcional: aceda aos dados do pod de gravação

Esta secção demonstra como usar uma interface de linha de comandos para aceder a um pod de leitura ou escrita. Pode ver o componente partilhado no qual o escritor está a escrever e o leitor está a ler.

  1. Obtenha o nome do agrupamento do escritor:

    kubectl get pods 

    O resultado é semelhante ao seguinte:

    NAME                      READY   STATUS    RESTARTS   AGE writer-5465d65b46-7hxv4   1/1     Running   0          20d 

    Tome nota do nome de anfitrião de um pod de gravação (exemplo: writer-5465d65b46-7hxv4).

  2. Execute o seguinte comando para aceder ao pod de gravação:

    kubectl exec -it WRITER_HOSTNAME -- /bin/sh 
  3. Veja o componente partilhado no ficheiro indexData.html:

    cd /html cat indexData.html 
  4. Limpe o ficheiro indexData.html:

    echo '' > indexData.html 

    Atualize o navegador de Internet que aloja o endereço EXTERNAL-IP para ver a alteração.

  5. Saia do ambiente:

    exit