Implemente um cluster Redis no GKE

Este tutorial apresenta práticas recomendadas para criar uma aplicação com estado e atualizar o cluster do Google Kubernetes Engine (GKE) que está a executar a aplicação. Este tutorial usa o Redis como exemplo para implementar uma aplicação com estado, mas os mesmos conceitos são aplicáveis a outros tipos de aplicações com estado implementadas no GKE.

Crie um cluster do GKE inscrito num canal de lançamento

Para criar o cluster do GKE, conclua os seguintes passos:

  1. Crie um cluster com o nome redis-test com três nós:

    gcloud container clusters create redis-test \
        --location CONTROL_PLANE_LOCATION \
        --num-nodes=3 \
        --release-channel regular
    

    Substitua CONTROL_PLANE_LOCATION pela localização do Compute Engine do plano de controlo do seu cluster. Indique uma região para clusters regionais ou uma zona para clusters zonais.

    Depois de criar o cluster, deve ver um resultado semelhante ao seguinte exemplo:

      NAME: redis-test
      LOCATION: us-central1-c
      MASTER_VERSION: 1.22.10-gke.600
      MASTER_IP: 34.69.67.7
      MACHINE_TYPE: e2-medium
      NODE_VERSION: 1.22.10-gke.600
      NUM_NODES: 3
      STATUS: RUNNING
    
  2. Configure o kubectl para comunicar com o cluster:

    gcloud container clusters get-credentials redis-test
    

Crie um cluster do Redis no GKE

Nesta secção, adiciona um cluster Redis sobre o cluster GKE que criou anteriormente implementando um ConfigMap, um StatefulSet e um serviço sem cabeça.

Para criar um cluster Redis, conclua estes passos:

  1. Consulte o ficheiro ConfigMap (redis-configmap.yaml) que armazena a configuração do Redis. O fragmento abaixo mostra os scripts da sonda de prontidão e da sonda de atividade.

    readiness.sh: |-
      #!/bin/sh
    
      pingResponse="$(redis-cli -h localhost ping)"
      if [ "$?" -eq "124" ]; then
        echo "PING timed out"
        exit 1
      fi
    
      if [ "$pingResponse" != "PONG"]; then
        echo "$pingResponse"
        exit 1
      fi
    liveness.sh: |-
      #!/bin/sh
    
      pingResponse="$(redis-cli -h localhost ping | head -n1 | awk '{print $1;}')"
      if [ "$?" -eq "124" ]; then
        echo "PING timed out"
        exit 1
      fi
    
      if [ "$pingResponse" != "PONG"] && [ "$pingResponse" != "LOADING" ] && [ "$pingResponse" != "MASTERDOWN" ]; then
        echo "$pingResponse"
        exit 1
      fi

    Os scripts readiness.sh e liveness.sh usam redis-cli ping para verificar se o servidor redis está em execução ou não. Se devolver PONG, o servidor Redis está a funcionar. Estes scripts vão ser usados no redis-cluster.yaml.

    Para saber mais acerca dos parâmetros do Redis neste ConfigMap, consulte a secção de parâmetros de configuração do cluster Redis no tutorial do cluster Redis.

  2. Implemente o ConfigMap:

    kubectl apply -f redis-configmap.yaml
    
  3. Consulte o fragmento do StatefulSet (redis-cluster.yaml) abaixo, que mostra a utilização da sondagem de disponibilidade e da sondagem de atividade.

    Para saber como configurar sondas no Kubernetes, consulte o artigo Configure sondas.

    startupProbe:
      periodSeconds: 5
      timeoutSeconds: 5
      successThreshold: 1
      failureThreshold: 20
      tcpSocket:
        port: redis
    livenessProbe:
      periodSeconds: 5
      timeoutSeconds: 5
      successThreshold: 1
      failureThreshold: 5
      exec:
        command: ["sh", "-c", "/probes/liveness.sh"]
    readinessProbe:
      periodSeconds: 5
      timeoutSeconds: 1
      successThreshold: 1
      failureThreshold: 5
      exec:
        command: ["sh", "-c", "/probes/readiness.sh"]

    Recomendamos vivamente que use sondas de prontidão e de atividade quando atualizar conjuntos de nós. Isto garante que os seus pods estão prontos durante uma atualização.

  4. Implemente o StatefulSet:

    kubectl apply -f redis-cluster.yaml
    
  5. O serviço sem interface chamado redis-service.yaml destina-se à ligação dos nós do Redis. O campo clusterIP está definido como None para criar um serviço sem interface.

    Implemente o serviço:

    kubectl apply -f redis-service.yaml
    
  6. Aguarde aproximadamente dois minutos e verifique se todos os pods estão em execução através do seguinte comando:

    kubectl get pods
    

    Deverá ver uma saída semelhante ao seguinte exemplo:

    NAME      READY   STATUS              RESTARTS   AGE
    redis-0   1/1     Running             0          2m29s
    redis-1   1/1     Running             0          2m8s
    redis-2   1/1     Running             0          107s
    redis-3   1/1     Running             0          85s
    redis-4   1/1     Running             0          54s
    redis-5   1/1     Running             0          23s
    
  7. Verifique se os volumes persistentes foram criados executando o seguinte comando:

    kubectl get pv
    

    Deverá ver uma saída semelhante ao seguinte exemplo:

    NAME       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                  STORAGECLASS   REASON   AGE
    pvc-HASH   1Gi        RWO            Delete           Bound    default/data-redis-5   standard                75s
    pvc-HASH   1Gi        RWO            Delete           Bound    default/data-redis-1   standard                2m59s
    pvc-HASH   1Gi        RWO            Delete           Bound    default/data-redis-3   standard                2m16s
    pvc-HASH   1Gi        RWO            Delete           Bound    default/data-redis-2   standard                2m38s
    pvc-HASH   1Gi        RWO            Delete           Bound    default/data-redis-0   standard                3m20s
    pvc-HASH   1Gi        RWO            Delete           Bound    default/data-redis-4   standard                104s
    

    Neste resultado, HASH representa um hash anexado a cada nome de volume persistente.

Atribua funções ao seu Redis Cluster

Assim que a configuração estiver concluída, atribua funções ao cluster Redis.

O script seguinte obtém os endereços IP dos pods e, em seguida, atribui as funções de líder e seguidor transmitindo cada um dos endereços IP dos pods para o comando:

#!/bin/bash
# Usage: ./roles.sh

urls=$(kubectl get pods -l app=redis -o jsonpath='{range.items[*]}{.status.podIP} ')
command="kubectl exec -it redis-0 -- redis-cli --cluster create --cluster-replicas 1 "

for url in $urls
do
    command+=$url":6379 "
done

echo "Executing command: " $command
$command

Para atribuir funções ao seu cluster Redis, conclua estes passos:

  1. Execute o script:

    chmod +x ./roles.sh
    ./roles.sh
    
  2. Escreva yes quando lhe for pedido.

  3. Inicie sessão num nó do Redis para verificar a respetiva função. Por exemplo, para verificar se redis-0 tem uma função de líder, execute o seguinte comando:

    kubectl exec -it redis-0 -- redis-cli role
    

    Deverá ver uma saída semelhante ao seguinte exemplo:

    1) "master"
    2) (integer) 574
    3) 1) 1) "10.28.2.3"
           2) "6379"
           3) "574"
    

Implemente a aplicação cliente Redis

Para implementar a sua aplicação no cluster do GKE que criou, defina uma implementação para a sua aplicação. O ficheiro denominado app-deployment.yaml contém a definição de implementação da aplicação.

Para saber mais sobre as sondas e as regras de afinidade de pods usadas nesta implementação, consulte o artigo Práticas recomendadas do GKE: conceber e criar clusters de elevada disponibilidade.

Para criar a implementação, conclua os seguintes passos:

  1. Aplique a implementação:

    kubectl apply -f app-deployment.yaml
    
  2. Exponha a aplicação através de um balanceador de carga:

    kubectl expose deployment hello-web \
        --type=LoadBalancer \
        --port 80 \
        --target-port 8080
    
  3. Aguarde aproximadamente um minuto e obtenha o endereço IP externo da aplicação executando o seguinte comando:

    kubectl get service
    

    Na saída, copie o valor indicado na coluna hello-web's EXTERNAL-IP:

    NAME             TYPE           CLUSTER-IP    EXTERNAL-IP    PORT(S)              AGE
    hello-web        LoadBalancer   10.13.10.55   EXTERNAL_IP   80:30703/TCP         166m
    
  4. Valide se a aplicação está a funcionar colando o EXTERNAL_IP no navegador de Internet. Deverá ver uma saída semelhante ao seguinte exemplo:

    I have been hit [1] times since deployment!
    

    Tome nota do número da visita. Tem de a usar na secção Testar a interrupção da aplicação.

  5. Defina uma variável para o EXTERNAL_IP que acabou de copiar. Use este valor quando criar scripts para testar a sua aplicação na secção seguinte:

    export IP=EXTERNAL_IP
    

Configure as práticas recomendadas para atualizações de node pools

Siga estas práticas recomendadas para aplicações com estado para otimizar a disponibilidade durante as atualizações do conjunto de nós.

Configure o Pod Disruption Budget (PDB)

Crie um orçamento de interrupção de pods para limitar o número de pods replicados que estão inativos em simultâneo durante uma interrupção voluntária. Isto é útil para aplicações com estado em que tem de existir um quórum para o número de réplicas estar disponível durante uma atualização.

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: redis-pdb
spec:
  minAvailable: 3
  selector:
    matchLabels:
      app: redis

Numa definição de PDB:

  • app especifica a que aplicação se aplica este PDB.
  • minAvailable define o número mínimo de pods a estar disponível durante uma interrupção. Pode ser um valor ou uma percentagem (por exemplo, 30%).
  • maxUnavailable define o número máximo de agrupamentos que podem estar indisponíveis durante uma interrupção. Também pode ser um valor ou uma percentagem.

Para configurar a PDB, conclua estes passos:

  1. Implemente o PDB:

    kubectl apply -f pdb-minavailable.yaml
    
  2. Verifique se o PDB foi criado:

    kubectl get pdb
    

Configure os períodos de manutenção e as exclusões

As atualizações automáticas de nós simplificam o processo de atualização e mantêm os nós no cluster atualizados quando o painel de controlo é atualizado automaticamente. Esta funcionalidade está ativada por predefinição. Para saber mais, consulte o artigo Atualizar automaticamente os nós.

Use janelas de manutenção e exclusões de manutenção para configurar intervalos de tempo e controlar quando a manutenção pode e não pode ocorrer em clusters do GKE:

  1. Configure uma janela de manutenção que comece às 02:00 UTC de 19 de agosto de 2022 e termine quatro horas mais tarde. Este período de manutenção é executado diariamente. Durante este período, é permitida a manutenção automática.

    gcloud container clusters update redis-test \
       --maintenance-window-start 2022-08-19T02:00:00Z \
       --maintenance-window-end 2022-08-19T06:00:00Z \
       --maintenance-window-recurrence FREQ=DAILY
    
  2. Configure um período de exclusão que impeça a manutenção durante o feriado do Ano Novo. Esta exclusão de manutenção usa o âmbito no_upgrades. Durante este período, não é permitida qualquer manutenção automática. Para saber mais, consulte o artigo Âmbito da manutenção a excluir.

    gcloud container clusters update redis-test \
       --add-maintenance-exclusion-name new-year \
       --add-maintenance-exclusion-start 2022-12-26T00:00:00Z \
       --add-maintenance-exclusion-end 2023-01-02T02:00:00Z \
       --add-maintenance-exclusion-scope no_upgrades
    
  3. Confirme se a janela de manutenção e as exclusões foram aplicadas. Procure em maintenancePolicy:

    gcloud container clusters describe redis-test
    

Para saber mais, consulte o artigo Configure janelas de manutenção e exclusões.

Configure uma estratégia de atualização de nós

Existem duas estratégias de atualização de nós que pode usar para os conjuntos de nós no seu cluster do GKE: atualizações azul-verde e atualizações de picos. Para saber mais, consulte o artigo Estratégias de atualização de nós.

Atualizações azul-verde

Escolha atualizações azul-verde se as cargas de trabalho forem menos tolerantes a interrupções e um aumento temporário do custo devido a uma utilização mais elevada de recursos for aceitável.

Execute o seguinte comando para alterar os conjuntos de nós atuais para a estratégia de atualização azul-verde.

gcloud container node-pools update default-pool \
--cluster=redis-test \
--enable-blue-green-upgrade \
--location CONTROL_PLANE_LOCATION \
--node-pool-soak-duration=120s

A duração da imersão do conjunto de nós está definida como dois minutos para poupar tempo durante a fase de imersão do conjunto de nós para efeitos deste tutorial. Esta fase é usada para validar o estado de funcionamento da carga de trabalho depois de os nós do conjunto azul terem sido esvaziados. Recomendamos que defina a duração da preparação do conjunto de nós para uma hora (3600 segundos) ou uma duração mais adequada à aplicação.

Para mais informações sobre a gestão da atribuição de pods, consulte os artigos Implemente um pod num conjunto de nós específico e Implementar serviços em conjuntos de nós específicos.

Para mais informações sobre a configuração de atualizações azul-verde, consulte o artigo Configure atualizações azul-verde.

Atualizações de aumentos

Escolha atualizações rápidas se a otimização de custos for importante e se as cargas de trabalho puderem tolerar um encerramento normal em menos de 60 minutos (o GKE respeita o PDB até 60 minutos).

Execute o seguinte comando para alterar os conjuntos de nós atuais para a estratégia de atualização rápida.

gcloud container node-pools update default-pool \
--max-surge-upgrade=1 \
--max-unavailable-upgrade=0 \
--cluster=redis-test

Com esta configuração (maxSurge=1 e maxUnavailable=0), só é possível adicionar um nó de pico ao conjunto de nós durante uma atualização, pelo que só é possível atualizar um nó de cada vez. Esta definição acelera os reinícios de pods durante as atualizações, ao mesmo tempo que progride de forma conservadora.

Para mais informações sobre a configuração de aumentos súbitos, consulte o artigo Configure aumentos súbitos.

Verifique a configuração atual do conjunto de nós:

   gcloud container node-pools describe default-pool \
   --cluster redis-test \
   --location CONTROL_PLANE_LOCATION

Para mais informações sobre a visualização de pools de nós, consulte o artigo Veja pools de nós num cluster.

Teste a aplicação

Nesta secção, usa dois scripts: um que envia pedidos para a sua aplicação e outro que mede a taxa de êxito dos pedidos. Use estes scripts para medir o que acontece quando atualiza o cluster.

Para criar os scripts:

  1. Altere para o diretório que contém os scripts:

    cd
    cd kubernetes-engine-samples/quickstarts/hello-app-redis/scripts
    
  2. Consulte o script denominado generate_load.sh que envia um pedido de consultas por segundo (CPS) à sua aplicação. O script guarda o código de resposta HTTP no diretório atual num ficheiro denominado output. O valor de output é usado no script que criar no passo seguinte.

    #!/bin/bash
    # Usage: ./generate_load.sh <IP> <QPS>
    
    IP=$1
    QPS=$2
    
    while true
      do for N in $(seq 1 $QPS)
        do curl -I -m 5 -s -w "%{http_code}\n" -o /dev/null http://${IP}/ >> output &
        done
      sleep 1
    done
  3. Consulte o script denominado print_error_rate.sh que calcula a taxa de êxito com base no resultado gerado por generate_load.sh.

    #!/bin/bash
    # Usage: watch ./print_error_rate.sh
    
    TOTAL=$(cat output | wc -l);
    SUCCESS=$(grep "200" output |  wc -l);
    ERROR1=$(grep "000" output |  wc -l)
    ERROR2=$(grep "503" output |  wc -l)
    ERROR3=$(grep "500" output |  wc -l)
    SUCCESS_RATE=$(($SUCCESS * 100 / TOTAL))
    ERROR_RATE=$(($ERROR1 * 100 / TOTAL))
    ERROR_RATE_2=$(($ERROR2 * 100 / TOTAL))
    ERROR_RATE_3=$(($ERROR3 * 100 / TOTAL))
    echo "Success rate: $SUCCESS/$TOTAL (${SUCCESS_RATE}%)"
    echo "App network Error rate: $ERROR1/$TOTAL (${ERROR_RATE}%)"
    echo "Resource Error rate: $ERROR2/$TOTAL (${ERROR_RATE_2}%)"
    echo "Redis Error rate: $ERROR3/$TOTAL (${ERROR_RATE_3}%)"
  4. Conceda autorização para executar os scripts:

    chmod u+x generate_load.sh print_error_rate.sh
    
  5. Defina uma variável para o número de CPS. Este valor é usado no script generate_load.sh, tal como a variável que definiu para EXTERNAL_IP. Recomendamos que defina um valor de 40.

    export QPS=40
    
  6. Execute o script generate_load.sh para começar a enviar QPS:

    ./generate_load.sh $IP $QPS 2>&1
    
  7. Deixe o script generate_load.sh em execução e abra um novo terminal. No novo terminal, execute o script print_error_rate.sh para verificar a taxa de erros:

    cd
    cd kubernetes-engine-samples/quickstarts/hello-app-redis/scripts
    watch ./print_error_rate.sh
    

    Deve ver uma taxa de êxito de 100% e taxas de erro de 0% à medida que as QPS são feitas.

  8. Deixe ambos os scripts em execução e abra um terceiro terminal em preparação para a secção seguinte.

Atualize o cluster

Para atualizar o cluster, conclua estes passos:

  1. Determine a versão do GKE que o cluster redis-test está a usar:

    V=$(gcloud container clusters describe redis-test | grep "version:" | sed "s/version: //")
    echo $V
    

    Deverá ver uma saída semelhante ao seguinte exemplo: 1.22.9-gke.2000.

  2. Apresente uma lista das versões do Kubernetes disponíveis:

    gcloud container get-server-config
    
  3. Na lista de versões, localize a secção validMasterVersions: e procure a versão redis-test que obteve no passo anterior. Para evitar violar a política de variação da versão do GKE, escolha uma versão incompatível com os nós. Copie a versão da lista apresentada imediatamente antes da versão redis-test.

  4. Atualize o painel de controlo do cluster para a versão que selecionou e escreva y quando lhe for pedido:

    gcloud container clusters upgrade redis-test \
        --master \
        --cluster-version VERSION
    

    Substitua VERSION pela versão que selecionou na lista no passo anterior.

    A atualização do plano de controlo demora vários minutos.

  5. Atualize os nós do cluster para a versão que selecionou e escreva y quando lhe for pedido:

    gcloud container clusters upgrade redis-test \
        --cluster-version=VERSION \
        --node-pool=default-pool
    

    Substitua VERSION pela versão que selecionou na lista.

Teste a interrupção da carga de trabalho

Nesta secção, testa o estado da sua aplicação e observa a interrupção da carga de trabalho.

  1. Regresse à janela do terminal que está a executar ./print_error_rate.sh e observe como a taxa de êxito mudou durante a atualização. Deve notar uma ligeira diminuição na taxa de êxito e um ligeiro aumento na taxa de erros da rede de apps à medida que os nós são desativados para serem atualizados.

    No campo Success rate, vê quantas visitas foram feitas com êxito ao Website. Tome nota deste valor.

  2. Impeça a execução de ambos os scripts introduzindo CTRL+C nos terminais relevantes.

  3. Regresse ao Website da sua aplicação introduzindo o respetivo endereço IP (este é o EXTERNAL_IP que copiou durante a secção Implemente a aplicação cliente Redis) no seu navegador.

  4. Observe o número de visitas da sua aplicação. O número que vê deve ser igual a:

    ORIGINAL_VISIT_NUMBER + SUCCESSFUL_VISIT_NUMBER

    onde ORIGINAL_VISIT_NUMBER é o número que registou no passo final de Implemente a aplicação cliente Redis e SUCCESSFUL_VISIT_NUMBER é o valor que registou no primeiro passo desta secção.