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