Teste de carga distribuída usando o Kubernetes

O teste de carga é fundamental para o desenvolvimento de qualquer infraestrutura de back-end. Com ele, você verifica a eficácia do sistema quando comparada com as demandas reais. Um aspecto importante é a simulação adequada do comportamento do usuário e do dispositivo para identificar e entender os possíveis gargalos do sistema antes de implantar os aplicativos na produção.

Entretanto, a infraestrutura dedicada de teste pode ser cara e difícil de manter, porque ela não é necessária de modo permanente. Além disso, ela costuma ser uma despesa de capital com capacidade fixa, o que torna difícil de escalonar o teste de carga além do investimento inicial e pode limitar os testes. Isso reduz a produtividade das equipes de desenvolvimento e os aplicativos não são testados de maneira adequada antes das implantações na produção.

Visão geral da solução

Uma opção interessante para vários cenários é fazer o teste de carga distribuída usando computação em nuvem. Com as plataformas de nuvem, você tem um alto grau de flexibilidade na infraestrutura, o que torna fácil o teste de aplicativos e serviços com uma grande quantidade de simulações de clientes. Elas geram tráfego usando usuários ou dispositivos como modelo. Além disso, o modelo de preços da computação em nuvem é bastante apropriada para a natureza flexível desse tipo de teste.

Os contêineres, que são uma alternativa leve para executar instâncias completas de máquina virtual, são adequados para o escalonamento rápido de simulações de clientes. Eles são uma excelente abstração para a execução de testes de clientes, porque são leves, de implantação simples, disponibilidade imediata e perfeitos para tarefas únicas.

O Google Cloud Platform é um ambiente excelente para teste de carga distribuída usando contêineres. A plataforma é totalmente compatível com contêineres via Google Kubernetes Engine, que usa a tecnologia de gerenciador de clusters de contêiner de código aberto, Kubernetes. Com o Kubernetes Engine, é possível provisionar com rapidez a infraestrutura de contêiner e as ferramentas para gerenciar aplicativos e recursos implantados.

Com esta solução, você aprende a usar o Kubernetes Engine para implantar uma estrutura de teste de carga distribuída. Nela, vários contêineres são usados para criar o tráfego desse teste a partir de uma API baseada em REST. Ainda que o aplicativo da Web usado aqui seja simples, o mesmo padrão pode ser usado para criar cenários de teste mais complexos como aplicativos da Internet das coisas (IoT, na sigla em inglês) ou jogos. Neste documento, será discutida a arquitetura geral de uma estrutura de teste de carga baseada em contêineres. Para um tutorial com instruções detalhadas sobre a configuração de uma estrutura de amostra, consulte a seção Tutorial no final deste documento.

Esta solução tem como foco o uso do Kubernetes Engine para criar um tráfego de teste de carga. O sistema em teste é um aplicativo da Web simples que usa uma REST API. A solução utiliza uma estrutura de teste de carga existente para modelar as interações dessa API. Esse processo é discutido com mais detalhes nas seções a seguir. Após implantar o sistema em teste, o Kubernetes Engine é usado pela solução para implantar as tarefas de teste de carga distribuídas.

Sistema em teste

Na terminologia de teste de software, o sistema em teste é aquele que precisa ser avaliado. Nesta solução, esse sistema é um pequeno aplicativo da Web implantado no Google App Engine. No aplicativo, os pontos de extremidade do tipo REST básico estão expostos para capturar as solicitações POST HTTP recebidas (dados de entrada não permanentes). Em um cenário real, os aplicativos da Web podem ser mais complexos e incluir uma variedade de componentes e serviços adicionais como armazenamento em cache, mensagens e persistência. Esses fatores complexos estão fora do escopo dessa solução. Consulte a solução Como criar aplicativos da Web escaláveis e resilientes para mais informações sobre a criação dos aplicativos escaláveis no Google Cloud Platform.

O código-fonte do aplicativo de amostra está disponível como parte do tutorial, no final deste documento.

Cargas de trabalho de exemplo

O aplicativo de exemplo tem como modelo o componente de serviço de back-end encontrado em várias implantações de Internet das coisas. Os dispositivos primeiro são registrados no serviço e as métricas ou leituras do sensor começam a ser reportadas. Ao mesmo tempo, o registro no serviço é refeito periodicamente.

No diagrama a seguir, veja uma interação comum entre os componentes do serviço de back-end.

Um diagrama com a interação comum entre os componentes de um serviço de back-end.

Para modelar essa interação, use o Locust, uma ferramenta de teste de carga baseada em Python capaz de distribuir as solicitações entre vários caminhos de destino. Por exemplo, com o Locust, essa distribuição é feita para os caminhos /login e /metrics. Há diversos pacotes de software de geração de carga disponíveis, como o JMeter, o Gatling e o Tsung. Escolha o mais adequado às necessidades do seu projeto.

A carga de trabalho tem como base a interação descrita anteriormente e é modelado como um conjunto de tarefas no Locust. Para ficar mais próximo das situações reais dos clientes, cada tarefa do Locust tem um peso. Por exemplo, o registro ocorre uma vez a cada mil solicitações de cliente.

Computação baseada em contêineres

Do ponto de vista da arquitetura, há dois componentes principais envolvidos na implantação dessa solução de teste de carga distribuída: a imagem de contêiner do Locust e o mecanismo de orquestração e gerenciamento de contêineres.

A imagem do contêiner do Locust é uma imagem do Docker que contém o software do Locust. Você encontra o Dockerfile no repositório GitHub associado. Consulte o tutorial mais adiante. Nesse Dockerfile, uma imagem Python é usada como base e ela inclui scripts para iniciar o serviço Locust e executar as tarefas.

Nesta solução, o Google Kubernetes Engine atua como mecanismo de gerenciamento e orquestração de contêineres. Baseado na estrutura de código aberto do Kubernetes, ele é o resultado de anos de experiência em execução, orquestração e gerenciamento de implantação de contêineres em todo o Google. Com esse tipo de computação baseado em contêiner, os desenvolvedores se concentram nos aplicativos, sem se preocupar com as implantações e integrações nos ambientes de hospedagem. Isso também facilita a portabilidade do teste de carga, ou seja, os aplicativos em contêiner podem ser executados em vários ambientes de nuvem. O Kubernetes Engine e o Kubernetes apresentam diversos conceitos específicos para orquestração e gerenciamento de contêineres.

Clusters de contêiner

Um cluster de contêiner é um grupo de instâncias do Compute Engine que fornece a base para todo o aplicativo. Na documentação do Kubernetes Engine e do Kubernetes, a essas instâncias são tratadas como nodes. Um cluster compreende um único node mestre e um ou mais worker nodes. O mestre e os workers são executados no Kubernetes. Por esse motivo, os clusters às vezes são chamados de clusters do Kubernetes. Para mais informações sobre clusters, consulte a documentação do Kubernetes Engine.

Pods

Um pod é um grupo de contêineres associados que precisam ser implantados juntos. Alguns pods contêm somente um contêiner. Por exemplo, nessa solução, cada contêiner do Locust é executado no próprio pod. No entanto, geralmente eles contêm vários contêineres que trabalham juntos. Nessa solução, por exemplo, um pod com três contêineres é usado no Kubernetes para fornecer os serviços de DNS. Em um deles, o SkyDNS, a funcionalidade do servidor DNS é executada. O SkyDNS depende de um armazenamento de chave-valor chamado etcd, que fica no outro contêiner. No terceiro contêiner, o kube2sky atua como uma ponte entre o Kubernetes e o SkyDNS.

Controladores de replicação

Com um controlador de replicação, você garante que um número especificado de "réplicas" de pod sempre estejam em execução. Quando há pods demais, o controlador encerra alguns deles. Se há poucos, outros são iniciados. Essa solução tem três controladores: um garante a existência de um único pod de servidor DNS, o outro mantém um único pod mestre do Locust e o terceiro assegura a execução de exatamente 10 pods trabalhadores do Locust.

Serviços

Um pod específico pode desaparecer por vários motivos, incluindo uma falha no node ou a interrupção intencional dele para atualizações ou manutenção. Isso significa que o endereço IP desse pod não fornece uma interface confiável para ele. Em uma abordagem ideal, uma representação abstrata da interface que nunca é alterada é usada, mesmo quando o pod subjacente desaparece e é substituído por outro com IP diferente. Um serviço do Kubernetes Engine fornece esse tipo de interface abstrata, em que são definidos um conjunto lógico de pods e uma política para acessá-los. Nessa solução, há vários serviços que representam pods ou conjunto de pods. Por exemplo, há um serviço para o pod do servidor DNS, outro para o pod mestre do Locust e um terceiro que representa os 10 pods trabalhadores do Locust.

No diagrama a seguir, veja o conteúdo dos nodes mestre e trabalhadores.

Um diagrama que mostra o conteúdo dos nodes mestre e trabalhadores.

Como implantar o sistema em teste

Na solução, o Google App Engine é usado para executar o sistema em teste. Para implantá-lo, você precisa de uma conta ativa do Google Cloud Platform, a fim de instalar e executar o SDK do Cloud Platform. Com esse SDK, você implanta o aplicativo da Web de amostra com um único comando. O código-fonte do aplicativo está disponível como parte do tutorial, no final deste documento.

Como implantar as tarefas de teste de carga

Para implantar as tarefas de teste de carga, em primeiro lugar, implante um mestre e depois um grupo de 10 trabalhadores. Com essa quantidade de trabalhadores, você cria um volume substancial de tráfego para o teste. Lembre-se, entretanto, que o excesso de tráfego em sistemas externos pode ser interpretado como um ataque de negação de serviço. Revise os Termos de Serviço e a Política de uso aceitável do Google Cloud Platform.

O mestre do teste de carga

O primeiro componente da implantação é o mestre do Locust. Ele é o ponto de entrada de execução das tarefas de teste de carga descritas acima. Esse mestre é implantado como um controlador de replicação com uma única réplica, porque somente uma é necessária. O controlador é útil, mesmo quando a implantação envolve apenas um pod, porque ele garante alta disponibilidade.

Na configuração do controlador de replicação, vários elementos são especificados, incluindo o nome dele (locust-master), marcadores para a organização (name: locust, role: master) e as portas que precisam ser expostas pelo contêiner (8089 para a interface da Web, 5557 e 5558 para a comunicação com os trabalhadores). Essas informações serão usadas depois na configuração do controlador dos trabalhadores do Locust. No seguinte snippet, veja as configurações das portas:

...
ports:
  - name: locust-master-web
    containerPort: 8089
    protocol: TCP
  - name: locust-master-port-1
    containerPort: 5557
    protocol: TCP
  - name: locust-master-port-2
    containerPort: 5558
    protocol: TCP

Em seguida, nós implantamos um serviço para garantir que as portas expostas estejam acessíveis para os outros pods por meio do hostname:port dentro do cluster, e também pelo nome descritivo da porta. Com esse serviço, os trabalhadores do Locust conseguem facilmente descobrir e ter uma comunicação confiável com o mestre, mesmo quando uma falha ocorre nesse mestre e ele é substituído por um novo pod no controlador de replicação. O serviço do mestre do Locust também inclui uma diretiva para criar uma regra de encaminhamento externo no nível do cluster, o que permite que os recursos do cluster sejam acessados por tráfego externo. Observe que, mesmo assim, as regras de firewall precisam ser criadas para que as instâncias de destino tenham acesso completo.

Depois de implantar o mestre do Locust, acesse a interface da Web usando o endereço IP público da regra de encaminhamento externo. Após a implantação dos trabalhadores do Locust, inicie a simulação e veja as estatísticas agrupadas nessa interface.

Os trabalhadores do teste de carga

O próximo componente da implantação inclui os trabalhadores do Locust, onde as tarefas de teste de carga descritas acima são executadas. Esses trabalhadores são implantados por um único controlador de replicação, onde 10 pods são criados. Esses pods são distribuídos no cluster do Kubernetes. Cada um deles usa variáveis de ambiente para controlar informações essenciais, por exemplo, os nomes dos hosts do sistema em teste e do mestre do Locust. A configuração do controlador se encontra no tutorial mais adiante. Ela contém o nome dele, locust-worker, marcadores para a organização, name: locust, role: worker, e as variáveis de ambiente descritas anteriormente. No seguinte snippet, veja a configuração do nome, marcadores e número de réplicas:

kind: ReplicationController
  apiVersion: v1
  metadata:
    name: locust-worker
    labels:
      name: locust
      role: worker
  spec:
    replicas: 10
    selector:
      name: locust
      role: worker
...

Para os trabalhadores do Locust, nenhum serviço adicional precisa ser implantado porque os próprios pods deles não precisam de comunicação de entrada, eles se conectam diretamente com o pod mestre.

No diagrama a seguir, veja a relação entre o mestre e os trabalhadores do Locust.

Um diagrama que mostra o conteúdo dos nodes mestre e trabalhadores do Locust.

Depois que os trabalhadores do Locust são implantados pelo controlador de replicação, volte para a interface da Web do mestre e veja se o número de escravos corresponde ao número de trabalhadores implantados.

Como executar tarefas de teste de carga

Como iniciar o teste de carga

A interface da Web do mestre do Locust permite que você execute as tarefas de teste de carga no sistema em teste, conforme mostrado na seguinte imagem:

Uma captura de tela da interface da Web inicial do Locust.

Para começar, especifique o número total de usuários que serão simulados e uma taxa pela qual cada usuário será gerado. Em seguida, clique em Start swarming para iniciar a simulação. À medida que o tempo passa e os usuários são gerados, você verá alterações nas métricas de simulação das estatísticas, por exemplo, no número de solicitações e nas solicitações por segundo, conforme mostrado na seguinte imagem:

Uma captura de tela da interface da Web do Locust enquanto o swarming está em andamento.

Para interromper a simulação, clique em Stop e o teste será encerrado. Faça o download dos resultados completos em uma planilha.

Como escalonar clientes

Para escalonar o número de simulações de usuários, é necessário aumentar o número de pods trabalhadores do Locust. Conforme especificado, 10 pods trabalhadores do Locust são implantados no controlador de replicação. Para aumentar esse número, é possível redimensionar os controladores no Kubernetes sem precisar reimplantá-los. Por exemplo, para alterar o número de trabalhadores, use a ferramenta de linha de comando kubectl. No seguinte comando, o pool deles é escalonado para 20:

$ kubectl scale --replicas=20 replicationcontrollers locust-worker

Depois de executar o comando scale, aguarde alguns minutos para que os pods sejam implantados e iniciados. Quando todos forem iniciados, retorne para a interface da Web do mestre do Locust e reinicie o teste de carga.

Recursos e custo

Nesta solução, você encontra quatro nodes do Kubernetes Engine, cada um apoiado por uma instância padrão da VM do Compute Engine do tipo n1-standard-1. Use a Calculadora de Preços do Google Cloud Platform para ter uma estimativa do custo mensal de execução do cluster de contêiner. Conforme discutido anteriormente, é possível personalizar o tamanho desse cluster para que ele seja escalonado de acordo com as suas necessidades. Com a calculadora, você personaliza as características do cluster para ter uma estimativa do custo quando as dimensões dele são aumentadas ou reduzidas.

Próximas etapas

Agora você sabe usar o Kubernetes Engine para criar uma estrutura de teste de carga para um aplicativo da Web simples. Com ele, especifique o número de nodes de contêineres que fornecem a base para sua estrutura de teste de carga, organize seus workers de teste de carga em pods e declare quantos deles você quer que o Kubernetes Engine mantenha funcionando.

Use esse mesmo padrão para criar estruturas de teste de carga em vários cenários e aplicativos diferentes. Por exemplo, use esse padrão em sistemas de mensagens, de bancos de dados e de gerenciamento de stream de dados. Novas tarefas do Locust podem ser criadas ou mesmo trocadas para uma estrutura diferente.

Outra maneira de estender a estrutura apresentada nesta solução é personalizar as métricas coletadas. Por exemplo, algumas possibilidades são a medição das solicitações por segundo, o monitoramento da latência das respostas à medida que a carga aumenta ou a verificação das taxas de falha de resposta e dos tipos de erros. Há várias opções de monitoramento disponíveis, incluindo o Google Cloud Monitoring.

Tutorial

O conteúdo do tutorial na íntegra, incluindo as instruções e o código-fonte, estão disponíveis no GitHub, em https://github.com/GoogleCloudPlatform/distributed-load-testing-using-kubernetes.

Esta página foi útil? Conte sua opinião sobre:

Enviar comentários sobre…