Analisar o comportamento de um sistema que emprega uma arquitetura de microsserviços em seu backend. O foco é entender como o sistema reage quando um ou mais de seus microsserviços recebem uma carga elevada. Especial atenção é dada à maneira como o sistema gerencia essa sobrecarga, comparando cenários com e sem a implementação de autoscaling.
O objetivo é realizar um teste de carga em um sistema com arquitetura de microsserviços, observando seu comportamento e as métricas resultantes. Em seguida, o mesmo teste será aplicado ao sistema, mas desta vez em um cenário que conta com um serviço de autoscaling ativo. A comparação desses dois cenários proporcionará insights importantes sobre o desempenho do sistema sob diferentes condições.
O sistema é um backend para e-commerce com API em Node.js, organizado em microsserviços encapsulados em containers Docker. Segue os princípios da Clean Architecture, com um proxy server direcionando as solicitações. Utiliza MongoDB para gestão de dados, com modelos definidos pelo Mongoose. A comunicação entre certos microsserviços é feita através do RabbitMQ e do protocolo AMQP.
O código fonte deste sistema, que foi modificado para a realização do experimento, está disponível em um artigo detalhado no Medium. Para uma compreensão mais profunda do projeto e das modificações realizadas, recomendamos a leitura deste artigo. O repositório completo do código fonte pode ser acessado aqui.
A arquitetura do sistema, com as modificações aplicadas, é apresentada abaixo:
O experimento envolveu a utilização de várias ferramentas:
- Docker - Plataforma para desenvolvimento, deploy e execução de aplicações utilizando containers.
- Kubernetes - Sistema de orquestração de containers.
- Kind - Ferramenta para criação de clusters Kubernetes locais.
- Kubectl - Ferramenta de linha de comando para interagir com o cluster Kubernetes.
- Heml - Gerenciador de pacotes para Kubernetes.
- Make - Ferramenta para automatizar a execução de tarefas.
- k6 - Ferramenta para testes de carga.
A migração completa da arquitetura do projeto, originalmente implementada no Docker, para o Kubernetes foi necessária. O Kubernetes oferece recursos avançados como Autoscaling e Load Balancer. Essas funcionalidades foram fundamentais para a decisão de migração.
A arquitetura completa oferece uma visão detalhada de todos os componentes e como eles interagem entre si.
A arquitetura simplificada fornece uma visão geral do sistema, focando nos componentes principais.
- Limite de CPU: 70%
- Número de réplicas mínimas: 1
- Número de réplicas máxima: 10
Realizamos três testes em paralelo durante um período de 15 minutos. Cada teste focou em um microsserviço específico e usou uma quantidade diferente de usuários virtuais (VUs).
-
Microsserviço de Usuários
- Endpoin: GET /users
- Carga: Constante
- Usuários Virtuais (VUs): 15
-
Microsserviço de Ordens de Compras
- Endpoint: GET /orders
- Carga: Constante
- Usuários Virtuais (VUs): 10
-
Microsserviço de Produtos
- Endpoint: GET /products
- Carga: Variável, dividida em 3 estágios:
- Estágio 1: 30 VUs por 5 minuto
- Estágio 2: 60 VUs por 5 minutos
- Estágio 3: 90 VUs por 5 minutos
Para realizar o deploy da aplicação no Kubernetes, siga os passos abaixo:
-
Criar o cluster Kubernetes local:
kind create cluster
-
Configurar o monitoramento da aplicação instalando o kube-prometheus-stack:
make add-prometheus-stack
-
Construir as imagens Docker dos microsserviços:
make docker-build-all
-
Fazer o load das imagens Docker no cluster Kubernetes (Processo demorado):
make kind-load-docker-images
-
Realizar o deploy da aplicação:
make kube-up
-
Verificar se a aplicação e o monitoramento estão funcionando corretamente. Todos os serviços devem estar com o status
Running
:6.1 Verificando aplicação
kubectl get pods
6.2 Verificando o monitoramento
kubectl get pods -n monitoring
- Levantar o ambiente para a execução:
make k6-up
-
Expor o api-gateway para acesso externo:
make kube-expose-app
-
Expor o Grafana do Kubernetes para acesso externo:
make kube-expose-grafana
-
Importar o dashboard do Kubernetes no grafana:
3.1 Acessar o grafana do Kubernetes
3.2 Importar o dashboard Kubernetes Horizontal Pod Autoscaler
-
Importar o dashboard do K6 no grafana:
4.1 Acessar o grafana do K6
4.1 Configurar a conexão com o InfluxDB:
- URL:
http://influxdb:8086
- Database:
k6
4.2 Importar o dashboard k6 Load Testing Results
- URL:
-
Iniciar o teste de carga:
make k6-run
-
Acompanhar o dashboard do K6 e do Kubernetes para verificar o comportamento da aplicação e os resultados do teste.
-
Ao finalizar o teste de carga devemos:
7.1 Derrubar o ambiente de execução do K6:
make k6-down
7.2 Derrubar o ambiente de execução da aplicação:
make kube-down
Microsserviço de Usuários
Microsserviço de Ordens de Compras
Microsserviço de Produtos
Resultados K6
Microsserviço de Usuários
Microsserviço de Ordens de Compras
Microsserviço de Produtos
Resultados K6
- A utilização de CPU para os microsserviços de usuários e ordens de compras se manteve relativamente constante entre 40%-60% e 20%-40%, respectivamente.
- Para o microsserviço de produtos, a utilização de CPU cresceu até 200% com a carga crescente.
- No K6, a maioria das requisições foi bem-sucedida com uma taxa de falha de 0,00%. A média da duração da requisição foi de 222,18 ms.
- A utilização de CPU para os microsserviços de usuários e ordens de compras se manteve relativamente constante entre 40%-50% e 20%-40%, respectivamente.
- Para o microsserviço de produtos, a utilização de CPU cresceu até 92% com a carga crescente, depois se manteve constante entre 50% e 70%.
- Durante o teste, apenas 5 de 10 réplicas máximas foram provisionadas, permitindo um uso de CPU variável entre 50% e 70%.
- No K6, todas as requisições foram bem-sucedidas com uma taxa de falha de 0,90%. A média da duração da requisição foi de 125,60 ms.
Os resultados indicam que o autoscaling foi eficaz em manter a utilização da CPU na média desejada e resolver o problema de sobrecarga do sistema. Isso é evidenciado pela redução na utilização máxima da CPU no microsserviço de produtos de 200% para 92%. Além disso, a duração média da requisição diminuiu de 222,18 ms para 125,60 ms com o autoscaling, indicando uma melhoria no desempenho. No entanto, houve um ligeiro aumento na taxa de falha das requisições de 0,00% para 0,90% com o autoscaling. Isso pode ser um ponto a ser investigado para futuras otimizações. Em geral, o autoscaling parece ter contribuído positivamente para a gestão da carga e o desempenho do sistema.