Pular para o conteúdo principal

Microsserviço 10

· 13 min para ler
Leandro Andrade
Leandro Andrade
Software Developer

Dividir os nossos sistemas em microsserviços menores e mais especializados traz diversas vantagens. Entretanto, surgem também novas fontes de complexidade, principalmente no que tange ao comportamento dos sistemas em ambiente de produção.

A partir desse momento, passamos a ter vários servidores para monitorar, diversos logs para filtrar e múltiplos pontos em que a latência pode causar problemas. Considerando esse contexto, a primeira coisa de que precisamos é monitorar partes menores do sistema e disponibilizar um recurso de agregação que permita enxergar o quadro geral, sem perder a capacidade de fazer uma análise minuciosa dos dados obtidos.

Quando temos um único microsserviço em um único servidor, queremos informações de host, como CPU e memória, além de logs da instância do microsserviço e de mecanismos para monitorar a própria aplicação — observando-a “de fora” — por exemplo, tempos de resposta.

Quando temos um único microsserviço distribuído em vários servidores, a complexidade aumenta. Queremos continuar monitorando tudo o que monitorávamos antes, mas agora deve ser possível isolar problemas com mais precisão. As métricas de cada host devem ser coletadas e agregadas, mas também devem permitir análise de forma isolada. Além disso, métricas do load balancer precisam ser obtidas para que seja possível monitorar os tempos de resposta aos serviços downstream e identificar um possível gargalo no balanceador.

Quando passamos a ter vários serviços em vários servidores, a agregação de métricas e logs passa a ter um papel fundamental para um troubleshooting eficiente.

No fim do dia, o que precisamos é garantir que todas as informações disponíveis em todos os hosts e serviços sejam enviadas para uma ferramenta de agregação que permita ver o todo e permita analisar cada caso individualmente.

Observabilidade e monitoramento

Considerando essas informações, precisamos entender a diferença entre observabilidade e monitoramento.

Quando falamos de observabilidade, estamos tratando da capacidade de compreender o estado interno dos serviços a partir de suas saídas externas (logs, métricas, traces etc.). Quanto mais observável é um sistema, mais fácil será entender a causa de um problema quando ele ocorrer.

Já o monitoramento é uma atividade contínua realizada por pessoas e equipes, que define o que queremos observar, como coletar essas informações e quais ações tomar a partir delas.

Em resumo:

  • Observabilidade: é uma propriedade do sistema, o que o sistema tem; o quanto ele expõe sinais que permitem inferir seu estado interno.
  • Monitoramento: é uma atividade realizada pela equipe a partir dos sinais expostos pelo sistema, o que o sistema faz.

Ainda em observabilidade, é comum defini-la a partir de três pilares: métricas, logging e tracing distribuído, que, em conjunto, ajudam a tornar o sistema observável. Para isso, precisamos pensar nas saídas que serão coletadas e em como elas poderão ser consultadas depois. Quando um problema acontecer, queremos descobri-lo antes que os usuários o percebam.Assim, podemos seguir alguns passos que ajudam a melhorar a observabilidade da arquitetura como um todo.

Agregação de logs

Coletar informações de vários microsserviços e disponibilizá-las de forma centralizada é um requisito básico em arquiteturas distribuídas.

Em linhas gerais, os microsserviços registram seus logs e um daemon coleta esses registros, encaminhando-os para um repositório central. Podemos dizer que a agregação de logs é o mínimo necessário em uma arquitetura de microsserviços.

Com a ferramenta de agregação em operação, o próximo passo é padronizar o formato de envio dos logs. Por exemplo, JSON costuma ser um formato mais comum, pois permite estruturar o log com diferentes campos e torná-los pesquisáveis. Além disso, podemos adicionar um correlation id ou request id aos logs para possibilitar a pesquisa de uma cadeia de chamadas gerada a partir de um serviço, correlacionando todas as chamadas subsequentes relacionadas à mesma requisição. Evidentemente, cada serviço pertencente à cadeia de chamadas deve ser capaz de “passar adiante” o ID de correlação.

Um exemplo desse tipo de ferramenta para agregação e visualização de logs é o uso combinado de Elasticsearch e Kibana.

Agregação de métricas

O objetivo aqui é capturar números dos microsserviços e da infraestrutura subjacente para ajudar a detectar problemas e apoiar o planejamento de capacidade.

O primeiro passo é coletar métricas sobre o comportamento do sistema durante um período suficientemente longo, de forma que padrões claros surjam. A partir do momento em que conhecemos os padrões de uso, torna-se mais fácil identificar se teremos infraestrutura suficiente para atender às nossas necessidades.

O que é “bom” dependerá das métricas obtidas, como cargas de CPU, taxas de erro HTTP, filas, latência p95/p99, entre outras. Além disso, quanto maior o sistema, mais detalhadas e específicas deverão ser as métricas, somadas a ferramentas que permitam analisar esses dados de modo minucioso.

Tracing distribuído

O objetivo do tracing distribuído é rastrear o fluxo de chamadas entre serviços.

Como microsserviços são um conjunto de serviços que atuam em colaboração para executar algum tipo de tarefa, queremos ser capazes de visualizar o relacionamento entre eles e seguir o caminho de uma requisição de ponta a ponta.

A implementação costuma ser dividida em três partes:

  1. Capturar as informações de span nos microsserviços;
  2. Enviar as informações capturadas para um coletor (é comum que agentes locais encaminhem esses dados);
  3. O coletor recebe as informações, as armazena e as torna consultáveis.

Exemplos de ferramentas nessa categoria incluem: Jaeger, Lightstep e Honeycomb.

Como estamos?

Aqui entram conceitos como error budgets, SLA, SLO e SLI. O desafio é saber se os sistemas estão funcionando de forma aceitável.

Em um monólito, a tarefa tende a ser mais simples, pois tudo roda em um único processo. Já em sistemas distribuídos, precisamos olhar para o todo e definir o que seria um comportamento aceitável.

Alguns conceitos balizadores:

  • SLA (service-level agreement): no nível de empresa, é o acordo entre quem cria o sistema e quem o utiliza. Define não apenas o que os usuários podem esperar, mas também o que acontece se o sistema não atingir o nível de comportamento aceitável;
  • SLO (service-level objective): no nível de equipe, define o que aquela equipe se compromete a entregar. O conjunto de SLOs das equipes deve ser compatível com o SLA da empresa, embora também existam SLOs de caráter interno. Exemplos de SLO incluem uptime esperado ou tempos de resposta aceitáveis;
  • SLI (service-level indicator): são indicadores medidos a partir de dados reais sobre algo que o software faz. Exemplos: tempos de resposta de um processo, taxa de cadastro de clientes, número de pedidos concluídos, entre outros.

Resumindo:

  • SLA: foco na empresa/negócio;
  • SLO: foco na equipe;
  • SLI: foco no produto/serviço medido.

O error budget é uma forma de deixar clara a quantidade de erro aceitável em um sistema. Por exemplo: suponha que o uptime do serviço deva ser 99,9% por trimestre, 24x7. Isso significa que o sistema poderia ficar inativo por até aproximadamente 2h11 por trimestre. Se estivermos abaixo do error budget esperado, pode ser mais interessante adiar o rollout de um microsserviço escrito em uma nova linguagem de programação e focar, antes disso, na melhoria da confiabilidade do sistema.

Basicamente, o error budget é sobre dar espaço de manobra para que as equipes possam experimentar algo novo sem comprometer a experiência do usuário além do que foi acordado.

Alertas

Aqui o foco é definir o que deve gerar alertas.

Ocasionalmente, algo acontecerá nos sistemas e exigirá intervenção humana. O desafio, em arquiteturas de microsserviços, é descobrir quais tipos de problema devem acionar alguém e de que forma essa pessoa deve ser informada.

À medida que aumentam as possíveis causas de incidentes, torna-se necessário priorizar quais ocorrências geram quais tipos de alertas, para que tenhamos capacidade de separar o trivial do urgente.

O que devemos evitar é a sobrecarga de alertas. Deve ficar clara a separação entre o trivial, o urgente e o prioritário. Quando geramos alertas demais, impedimos que os operadores foquem no que realmente importa. O propósito do sistema de alertas é direcionar a atenção do operador para os aspectos significativos da operação e da infraestrutura que exijam atenção imediata.

Um bom alerta tende a ser:

  • relevante;
  • único (evitando duplicidade para o mesmo problema);
  • disparado na hora certa (entregue com rapidez suficiente);
  • priorizado (dá contexto adequado para avaliação de impacto);
  • compreensível (claro e legível);
  • explícito quanto ao que está errado;
  • acompanhado de orientações sobre próximos passos;
  • focado (chamando atenção para os problemas mais importantes).

Monitoramento semântico

O objetivo é pensar de maneira diferente sobre a “saúde” dos sistemas.

Precisamos verificar quais propriedades o sistema deve apresentar para ser considerado funcional dentro do esperado. Em vez de apenas analisar a presença de erros, devemos pensar se o sistema está se comportando conforme modelos de comportamento previamente definidos.

Exemplos de afirmações em alto nível:

  • clientes estão conseguindo se cadastrar;
  • e-mails estão sendo enviados;
  • saldos estão sendo atualizados corretamente.

A ideia não é ter um pensamento low-level, mas sim trabalhar com afirmações de alto nível sobre o comportamento do sistema. Muitas dessas afirmações podem — e devem — ser definidas pelo Product Owner (PO). O consolidado dessas afirmações compõe o modelo de comportamento esperado do sistema.

Com o modelo definido, temos duas maneiras principais de validar se o comportamento do sistema está conforme esse modelo:

  • Monitoramento de usuários reais: observamos o que realmente acontece em produção e comparamos com o modelo semântico. Como estamos avaliando fatos já ocorridos, um problema só será identificado depois de ter acontecido;
  • Transações sintéticas: introduzimos no sistema, em produção, o comportamento de um usuário simulado com entradas conhecidas e saídas esperadas. Essas transações são executadas regularmente para que problemas sejam identificados o mais rápido possível.

Testes em produção

Aqui o objetivo é observar o todo em execução, com todas as partes envolvidas.

Testes em produção podem ser muito úteis e seguros, a ponto de permitir identificar problemas antes que os usuários percebam, desde que realizados com cuidado para evitar efeitos colaterais indesejados. Algumas abordagens comuns:

  • Testes A/B: implantamos duas versões diferentes da mesma funcionalidade, com usuários vendo a versão “A” ou “B”. Assim, podemos comparar desempenho, experiência e resultados para decidir entre duas abordagens;
  • Canary release: uma parcela reduzida de usuários passa a enxergar uma nova funcionalidade. Se ela se comportar bem, aumentamos gradualmente essa parcela até que todos os usuários a utilizem. Caso não funcione como esperado, o impacto ficará restrito a um grupo menor;
  • Execução paralela: executamos duas implementações equivalentes da mesma funcionalidade, em paralelo. As requisições dos usuários são encaminhadas para as duas versões, e os resultados são comparados;
  • Smoke test: após o sistema ser implantado em produção, mas antes de ser liberado para todos, são executados testes rápidos para garantir que o software está funcionando conforme o esperado;
  • Transações sintéticas: uma interação completa de um usuário simulado é introduzida no sistema em produção, para verificar se o fluxo essencial está íntegro;
  • Engenharia do caos: inserção deliberada de falhas no ambiente de produção a fim de garantir que o serviço é capaz de lidar com problemas imprevistos. Exemplo de ferramenta: Chaos Monkey.

Considerando todos esses blocos de construção de monitoramento e observabilidade, torna-se necessário criar padronizações, por exemplo:

  • formatos padrão de logs;
  • agregação de logs em um só lugar;
  • labels pré-definidas para métricas;
  • convenções para correlation id e trace id.

Deve ser fácil fazer o certo e difícil fugir do padrão.

Ao escolher ferramentas para apoiar essa padronização, alguns critérios importantes devem ser observados:

  • Precisam ser democráticas: considerar as necessidades de todas as pessoas que as utilizarão, garantindo que sejam úteis para toda a equipe. Idealmente, devem ser utilizadas não só em produção, mas também em ambientes de desenvolvimento e testes;
  • Devem ser fáceis de integrar: ferramentas que aceitam padrões abertos facilitam o trabalho de integração e reduzem o risco de dependência excessiva de um fornecedor;
  • Devem fornecer o máximo de contexto: temporal, relativo, relacional e proporcional (quem será impactado?);
  • Devem fornecer acesso às informações: o mais rápido possível, para aumentar a chance de identificar problemas antes que o usuário perceba, além de permitir investigação posterior quando alguém relatar um incidente;
  • Devem considerar seu contexto e sua escala: à medida que sua escala aumenta, o ideal é que a ferramenta acompanhe, tanto em volume de dados quanto em complexidade de uso.

Comece por aqui

Como primeiros passos práticos, podemos seguir:

  • obter métricas de CPU, E/S e memória;
  • medir o response time de cada serviço, com correlation id registrado nos logs;
  • em cenários de maior escala, implementar tracing distribuído;
  • configurar transações sintéticas para operações essenciais;
  • ser capaz de responder com confiança: “o sistema está funcionando de modo adequado?”.

Assim, o entendimento do comportamento, no contexto de sistemas distribuídos, está diretamente relacionado ao quão observável o ecossistema é. Sem isso, resolver problemas em ambiente de produção pode se tornar extremamente difícil. O primeiro passo geralmente deve ser a agregação de logs com correlation id desde o início. O tracing distribuído pode vir em seguida, à medida que a arquitetura amadurece. E, por fim, não deixe que cada pequeno problema gere um alerta.

Riscos e pontos de atenção arquiteturais

  • Ausência de padronização
    Sem formatos consistentes de log, convenções de correlation id e labels de métricas, a agregação perde efetividade e aumenta o custo de operação.

  • Coleta excessiva e pouco foco
    Coletar “tudo” sem objetivos claros pode gerar alto custo de armazenamento e ferramentas lentas, sem necessariamente melhorar o diagnóstico. É importante priorizar métricas e logs alinhados a SLOs e SLIs relevantes.

  • Sobrecarga de alertas (alert fatigue)
    Alertas demais, especialmente sem prioridade clara, levam à dessensibilização da equipe, aumentando o risco de incidentes críticos passarem despercebidos.

  • Dependência forte de fornecedor (vendor lock-in)
    Ferramentas proprietárias sem suporte a padrões abertos podem dificultar migrações futuras, aumentar custos e limitar a evolução da arquitetura.

  • Falta de monitoramento semântico
    Ficar restrito apenas a métricas de infraestrutura e erros técnicos, sem modelos de comportamento de negócio, prejudica a detecção de falhas que afetam diretamente o usuário.

  • Testes em produção sem governança
    Uso inadequado de canary releases, testes A/B e engenharia do caos pode gerar impactos graves em usuários reais se não houver limites bem definidos, monitoramento adequado e plano de rollback.

  • Escala subestimada
    À medida que o número de microsserviços cresce, soluções de observabilidade que funcionavam em pequena escala podem deixar de ser viáveis. É necessário planejar desde cedo como escalar armazenamento, consultas e visualização.

Conclusão

Em arquiteturas baseadas em microsserviços, a complexidade operacional aumenta significativamente, especialmente em produção. Por isso, observabilidade e monitoramento deixam de ser apenas preocupações operacionais e passam a ser componentes essenciais da própria arquitetura do sistema. Agregação de logs, métricas bem definidas, tracing distribuído, uso adequado de SLA/SLO/SLI, error budgets, alertas bem desenhados, monitoramento semântico e testes em produção formam, em conjunto, a base para um ecossistema distribuído saudável.

Ao investir nesses elementos de forma estruturada e padronizada, as equipes ganham maior capacidade de diagnóstico, reduzem o tempo de resposta a incidentes, conseguem evoluir a solução com mais segurança e aumentam a confiabilidade percebida pelos usuários. Em última análise, uma boa estratégia de observabilidade e monitoramento não só melhora a operação diária, como também potencializa a evolução sustentável da arquitetura ao longo do tempo.