Como fazer upgrade de um Cluster do GKE executando uma carga de trabalho com estado

Neste tutorial, apresentamos as práticas recomendadas para criar um aplicativo com estado e fazer upgrade do cluster do Google Kubernetes Engine (GKE) que executa o aplicativo. Neste tutorial, apresentamos exemplos de como implantar um aplicativo Redis, mas os mesmos conceitos também valem para outros tipos de aplicativos com estado implantados no GKE.

Glossário

Termos usados neste tutorial:

  • O Redis (em inglês) é um armazenamento de estrutura de dados na memória de código aberto (licenciado para BSD), usado como banco de dados, cache e agente de mensagens.
  • Um cluster do Redis (em inglês) é uma implementação distribuída do Redis que oferece alta disponibilidade. Os clusters do Redis consistem em nós líderes e nós seguidores.
  • Um aplicativo com estado (em inglês) lembra as informações sobre o estado dele sempre que é executado. O Redis é um banco de dados na memória famoso para aplicativos com estado.

Objetivos

Este tutorial inclui as etapas a seguir:

  • No GKE, crie um cluster do Redis com três líderes e três seguidores, além dos três nós do GKE.
  • Implante um aplicativo cliente do Redis. O aplicativo conta o número de solicitações feitas a um site.
  • Faça upgrade do cluster com o upgrade súbito.
  • Teste a interrupção da carga de trabalho e do status do aplicativo.

No diagrama a seguir, mostramos uma visão geral da arquitetura de cluster que você cria ao concluir estes objetivos:

Diagrama da arquitetura

Custos

Neste tutorial, há componentes faturáveis do Google Cloud, inclusive estes:

  • GKE

Use a calculadora de preços para gerar uma estimativa de custos baseada na projeção de uso. Novos usuários do Google Cloud podem estar qualificados para uma avaliação gratuita.

Antes de começar

Siga estas etapas para ativar a API do Kubernetes Engine:
  1. Acesse a página do Kubernetes Engine no Console do Google Cloud.
  2. Crie ou selecione um projeto.
  3. Aguarde a ativação da API e dos serviços relacionados. Isso pode levar alguns minutos.
  4. Verifique se o faturamento está ativado para seu projeto na nuvem. Saiba como confirmar se o faturamento está ativado para o projeto.

Instale as ferramentas de linha de comando a seguir usadas neste tutorial:

  • gcloud é usado para criar e excluir clusters do Kubernetes Engine. gcloud está incluído no SDK do Google Cloud.
  • O kubectl é usado para gerenciar o Kubernetes, o sistema de orquestração de cluster usado pelo Kubernetes Engine. É possível instalar kubectl usando gcloud:
    gcloud components install kubectl

Como criar um cluster do GKE

Nesta seção, você cria um cluster no GKE com três nós e verifica se ele está funcionando.

Como definir padrões para a ferramenta de linha de comando gcloud

Para economizar o tempo de digitar o ID do projeto e as opções da zona do Compute Engine na ferramenta de linha de comando gcloud, defina os seguintes padrões:

gcloud config set project PROJECT-ID
gcloud config set compute/zone COMPUTE-ZONE

Como criar um cluster do GKE

Para criar o cluster do GKE, siga estas etapas:

  1. Crie um cluster chamado redis-test que tenha três nós:

    gcloud container clusters create redis-test \
        --num-nodes=3
    

    Depois que o cluster for criado, você verá uma saída semelhante ao seguinte exemplo:

    NAME        LOCATION      MASTER_VERSION  MASTER_IP     MACHINE_TYPE   NODE_VERSION    NUM_NODES  STATUS
    redis-test  COMPUTE-ZONE  1.15.12-gke.20  35.232.77.38  n1-standard-1  1.15.12-gke.20  3          RUNNING
    
  2. Configure kubectl para se comunicar com o cluster:

    gcloud container clusters get-credentials redis-test
    
  3. Verifique se o cluster está em execução:

    kubectl get nodes
    

    Você verá uma saída semelhante ao seguinte exemplo:

    NAME                                        STATUS   ROLES    AGE     VERSION
    gke-redis-test-default-pool-c4e4225c-mw3w   Ready    <none>   2m1s    v1.15.12-gke.20
    gke-redis-test-default-pool-c4e4225c-pv51   Ready    <none>   2m1s    v1.15.12-gke.20
    gke-redis-test-default-pool-c4e4225c-whl5   Ready    <none>   2m1s    v1.15.12-gke.20
    

Como criar um cluster do Redis no GKE

Nesta seção, você cria um cluster do Redis, além do cluster do GKE criado na seção anterior. Para isso, crie um ConfigMap, StatefulSet e um serviço headless (em inglês).

  1. Clone os manifestos de amostra:

    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
    cd kubernetes-engine-samples/hello-app-redis/manifests
    
  2. Um ConfigMap chamado redis-configmap.yaml armazena a configuração do Redis.

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: redis-cluster
    data:
      redis.conf:  |+
        cluster-enabled yes
        cluster-node-timeout 15000
        cluster-config-file /data/nodes.conf
        appendonly yes
        protected-mode no
        dir /data
        port 6379
    
    

    Para saber mais sobre os parâmetros do Redis neste ConfigMap, consulte a seção de parâmetros de configuração do cluster do Redis no tutorial do cluster do Redis (em inglês).

  3. Implante o ConfigMap executando o seguinte comando:

    kubectl apply -f redis-configmap.yaml
    
  4. O Statefulset chamado redis-cluster.yaml tem os seguintes campos principais:

    • O campo replicas está definido como 6. Isso significa que cada nó do GKE tem dois pods para os três líderes do Redis e os três seguidores do Redis.
    • O campo volumeClaimTemplates fornece armazenamento estável usando PersistentVolumes.
    • O campo affinity cria uma regra de antiafinidade de pod para distribuir pods entre os nós do Kubernetes.
    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      name: redis
    spec:
      serviceName: "redis-service"
      replicas: 6
      selector:
        matchLabels:
          app: redis
      template:
        metadata:
          labels:
            app: redis
            appCluster: redis-cluster
        spec:
          terminationGracePeriodSeconds: 20
          affinity:
            podAntiAffinity:
              preferredDuringSchedulingIgnoredDuringExecution:
              - weight: 100
                podAffinityTerm:
                  labelSelector:
                    matchExpressions:
                    - key: app
                      operator: In
                      values:
                      - redis
                  topologyKey: kubernetes.io/hostname
          containers:
          - name: redis
            image: "redis"
            command:
              - "redis-server"
            args:
              - "/conf/redis.conf"
              - "--protected-mode"
              - "no"
            resources:
              requests:
                cpu: "100m"
                memory: "100Mi"
            ports:
                - name: redis
                  containerPort: 6379
                  protocol: "TCP"
                - name: cluster
                  containerPort: 16379
                  protocol: "TCP"
            volumeMounts:
            - name: conf
              mountPath: /conf
              readOnly: false
            - name: data
              mountPath: /data
              readOnly: false
          volumes:
          - name: conf
            configMap:
              name: redis-cluster
              defaultMode: 0755
      volumeClaimTemplates:
      - metadata:
          name: data
        spec:
          accessModes: [ "ReadWriteOnce" ]
          resources:
            requests:
              storage: 1Gi
    
    
  5. Implante o StatefulSet executando o seguinte comando:

    kubectl apply -f redis-cluster.yaml
    
  6. O serviço headless chamado redis-service.yaml é para a conexão dos nós do Redis. Para criar um serviço headless, defina o campo clusterIP como None.

    apiVersion: v1
    kind: Service
    metadata:
      name: redis-cluster
    spec:
      clusterIP: None
      ports:
      - name: redis-port
        port: 6379
        protocol: TCP
        targetPort: 6379
      selector:
        app: redis
        appCluster: redis-cluster
      sessionAffinity: None
      type: ClusterIP
    
    
  7. Implante o serviço executando o seguinte comando:

    kubectl apply -f redis-service.yaml
    
  8. Aguarde cerca de dois minutos e verifique se todos os pods estão em execução usando o seguinte comando:

    kubectl get pods
    

    Você 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
    
  9. Verifique se os volumes permanentes foram criados executando o seguinte comando:

    kubectl get pv
    

    Você 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
    

    Nessa saída, HASH representa um hash anexado a cada nome de volume permanente.

Como atribuir papéis ao cluster do Redis

Após a conclusão da configuração, você atribui papéis ao cluster do Redis.

Para atribuir os papéis, siga estas etapas:

  1. Defina os três primeiros nós do Redis como líderes e os três últimos como seguidores:

    1. Recupere e faça uma cópia dos endereços IP do pod:

      kubectl get pods -l app=redis -o jsonpath='{range.items[*]}{.status.podIP} '
      
    2. Atribua os papéis de líder e seguidor colando cada um dos endereços IP do pod no seguinte comando e digite yes quando solicitado:

      kubectl exec -it redis-0 -- redis-cli --cluster create --cluster-replicas 1 \
      POD-IP-1:6379 POD-IP-2:6379 POD-IP-3:6379 \
      POD-IP-4:6379 POD-IP-5:6379 POD-IP-6:6379
      
  2. Verifique se o cluster do Redis está em execução:

    kubectl exec -it redis-0 -- redis-cli cluster info
    

    Você verá uma saída semelhante ao seguinte exemplo:

    cluster_state:ok
    # ...other output...
    
  3. Faça login em um nó do Redis para verificar o papel dele. Por exemplo, para verificar se redis-0 tem um papel de líder, execute o seguinte comando:

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

    Você verá uma saída semelhante ao seguinte exemplo:

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

Como criar um aplicativo cliente Redis

Nesta seção, você cria um aplicativo chamado hello-app-redis que usa o Redis como banco de dados em cache, conta o número de solicitações recebidas e imprime o número em um site. Se o serviço do Redis funcionar, o número continuará aumentando.

Faça o download da imagem diretamente do gcr.io/google-samples/hello-app-redis:1.0 no Console do Google Cloud.

Para receber a imagem, execute o seguinte comando:

docker pull gcr.io/google-samples/hello-app-redis:1.0

Para saber mais sobre como criar imagens, consulte Criar a imagem do contêiner.

Como implantar o aplicativo cliente Redis no GKE

Para implantar o aplicativo no cluster do GKE que você criou, é preciso ter uma implantação para definir o aplicativo.

Para criar a implantação, conclua as etapas a seguir:

  1. O arquivo chamado app-deployment.yaml contém os detalhes do aplicativo.

    # Copyright 2020 Google LLC
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    #     http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      labels:
        app: hello-web
      name: hello-web
      namespace: default
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: hello-web
      template:
        metadata:
          labels:
            app: hello-web
        spec:
          # Pod anti affinity config START
          affinity:
            podAntiAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
              - labelSelector:
                  matchExpressions:
                  - key: app
                    operator: In
                    values:
                    - hello-web
                topologyKey: kubernetes.io/hostname
          # Pod anti affinity config END
          containers:
          - image: gcr.io/google-samples/hello-app-redis:1.0  # change to the image name you built
            name: hello-app
            # Readiness probe config START
            readinessProbe:
              failureThreshold: 1
              httpGet:
                path: /healthz
                port: 8080
                scheme: HTTP
              initialDelaySeconds: 1
              periodSeconds: 1
              successThreshold: 1
              timeoutSeconds: 1
    

    Para saber mais sobre as sondagens e as regras de afinidade de pod usadas nesta implantação, consulte Práticas recomendadas do GKE: como projetar e criar clusters altamente disponíveis (em inglês).

  2. Aplique a implantação executando o comando a seguir:

    kubectl apply -f app-deployment.yaml
    

    Certifique-se de executar esse comando no mesmo diretório em que está o app-deployment.yaml.

  3. Exponha o aplicativo por meio de um balanceador de carga:

    kubectl expose deployment hello-web \
        --type=LoadBalancer \
        --port 80 \
        --target-port 8080
    
  4. Aguarde cerca de um minuto e recupere o endereço IP externo do aplicativo executando o seguinte comando:

    kubectl get service
    

    Na saída, copie o valor listado 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
    
  5. Para verificar se o aplicativo está funcionando, cole EXTERNAL_IP no navegador da Web. O resultado será semelhante a:

    I have been hit [1] times since deployment!
    

    Anote o número da visita. Você precisa usá-lo na seção Como testar a interrupção do aplicativo.

  6. Defina uma variável para o EXTERNAL_IP que você acabou de copiar. Use esse valor quando criar scripts para testar o aplicativo na próxima seção:

    export IP=EXTERNAL_IP
    

Como fazer upgrade do cluster do GKE e testar a interrupção da carga de trabalho

Nas seções a seguir, você faz upgrade do cluster do GKE e observa o que acontece usando os scripts criados.

Como testar o aplicativo

Nesta seção, você usa dois scripts, um que envia solicitações ao aplicativo e outro que mede a taxa de sucesso das solicitações. Eles são usados para medir o que acontece quando você faz upgrade do cluster.

Para criar os scripts:

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

    cd
    cd kubernetes-engine-samples/hello-app-redis/scripts
    
  2. O script chamado generate_load.sh envia uma solicitação de consultas por segundo (QPS) para seu aplicativo. Esse script salva o código de resposta HTTP no diretório atual em um arquivo chamado output. O valor de output é usado no script que você criará na próxima etapa.

    # Copyright 2020 Google LLC
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    #     http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    
    #!/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. Um segundo script chamado print_error_rate.sh calcula a taxa de sucesso com base na saída gerada por generate_load.sh.

    # Copyright 2020 Google LLC
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    #     http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    
    #!/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. Dê a si mesmo a permissã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 QPS. Esse valor é usado no script generate_load.sh como a variável definida para EXTERNAL_IP. Recomendamos que você 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:

    watch ./print_error_rate.sh
    

    Você verá uma taxa de sucesso de 100% e taxas de erro de 0% durante a realização de QPS.

  8. Deixe os dois scripts em execução e abra um terceiro terminal a fim de se preparar para a próxima seção.

Como fazer upgrade do cluster

Nesta seção, você faz upgrade das cargas de trabalho:

  1. No terminal que você acabou de abrir, defina as configurações do upgrade usando o upgrade súbito:

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

    Com essa configuração (maxSurge=1 e maxUnavailable=0), apenas um nó súbito pode ser adicionado ao pool de nós durante um upgrade, para que apenas um nó possa ser atualizado por vez. Essa configuração acelera as reinicializações do pod durante os upgrades e avança de maneira moderada.

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

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

    Você verá uma saída semelhante ao seguinte exemplo: 1.15.12-gke.20.

  3. Recupere uma lista de versões disponíveis do Kubernetes:

    gcloud container get-server-config
    
  4. Na lista de versões, localize a seção validMasterVersions: e procure a versão redis-cluster recuperada na etapa anterior. Para evitar o conflito de versão, copie a versão na lista que está logo acima de redis-cluster.

  5. Faça upgrade do plano de controle do cluster para a versão selecionada e digite y quando solicitado:

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

    Substitua VERSION pela versão selecionada na lista na etapa anterior.

    O upgrade do plano de controle leva vários minutos.

  6. Faça upgrade dos nós do cluster para a versão selecionada e digite y quando solicitado:

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

    Substitua VERSION pela versão selecionada na lista.

Como testar a interrupção da carga de trabalho

Nesta seção, você testa a interrupção do status e da carga de trabalho do aplicativo.

  1. Retorne à janela do terminal executando ./print_error_rate.sh e observe como a taxa de sucesso mudou durante o upgrade. Você observará uma ligeira redução na taxa de sucesso e um pequeno aumento na taxa de erros de rede do app, já que os nós são reduzidos para o upgrade.

    No campo Success rate, você verá quantas visitas foram feitas ao site. Anote esse valor.

  2. Para interromper a execução de ambos os scripts, insira CTRL+C nos terminais relevantes.

  3. Retorne ao site do seu aplicativo inserindo o endereço IP dele (que é o EXTERNAL_IP que você copiou durante a seção Como implantar no GKE) no seu navegador.

  4. Observe o número de visitas do aplicativo. O número exibido precisa ser igual a:

    ORIGINAL_VISIT_NUMBER + SUCCESSFUL_VISIT_NUMBER

    em que ORIGINAL_VISIT_NUMBER é o número registrado na etapa final de Como implantar no GKE e SUCCESSFUL_VISIT_NUMBER é o valor registrado na primeira etapa desta seção.

Como fazer a limpeza

Depois de concluir este tutorial, limpe os recursos criados no Google Cloud para que eles não consumam cotas e você não seja cobrado por eles no futuro. Veja como excluir ou desativar esses recursos nas seções a seguir.

Como excluir o projeto

O jeito mais fácil de evitar cobranças é excluindo o projeto que você criou para o tutorial.

Para excluir o projeto:

  1. No Console do Cloud, acesse a página Gerenciar recursos:

    Acessar "Gerenciar recursos"

  2. Na lista de projetos, selecione o projeto que você quer excluir e clique em Excluir .
  3. Na caixa de diálogo, digite o ID do projeto e clique em Encerrar para excluí-lo.

Como excluir clusters

Para excluir o cluster criado neste tutorial, execute o seguinte comando:

gcloud container clusters delete redis-test

A seguir