Microsserviço 10
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:
- Capturar as informações de span nos microsserviços;
- Enviar as informações capturadas para um coletor (é comum que agentes locais encaminhem esses dados);
- 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.
