Ao trabalhar com métricas e monitoramento em sistemas distribuídos, é essencial ter a capacidade de consultar e analisar dados de desempenho de forma eficaz. Em muitos casos, utilizamos Prometheus e sua linguagem de consulta PromQL, mas também podemos integrar essas métricas com SQL, particularmente quando estamos lidando com dados armazenados em bancos como PostgreSQL ou Promscale. A seguir, abordaremos como consultar e agregar métricas, além de exemplos práticos de consultas, oferecendo uma compreensão profunda das ferramentas disponíveis.

Ao consultar métricas no Prometheus, é crucial saber como agregar os dados para obter insights mais relevantes. Um exemplo simples seria consultar a métrica de uso da CPU. Se quisermos consultar o uso da CPU para um determinado período, como o último ano, agrupando os resultados por namespace, podemos usar a seguinte consulta SQL no Prometheus:

sql
SELECT val(namespace_id) as namespace, percentile_cont(0.5) within group (order by value) AS median FROM "cpu_usage" WHERE time > '2019-01-01' GROUP BY namespace_id;

Esta consulta fornece a mediana de uso da CPU para cada namespace desde o início de 2019. Para casos mais específicos, como consultas que envolvem apenas métricas de um namespace de produção ou métricas de pods cujo nome começa com "ab", podemos usar a cláusula OR:

sql
SELECT avg(value) FROM "cpu_usage"
WHERE labels ? ('namespace' == 'production')
OR labels ? ('pod' ==~ 'ab*');

Essa consulta retornará a média de uso da CPU, considerando apenas as métricas associadas ao namespace de produção ou aos pods que começam com "ab".

Outro exemplo prático é calcular o percentil 99 do uso de memória por contêiner no namespace padrão. Isso pode ser feito com a seguinte consulta:

sql
SELECT val(used.container_id) container,
percentile_cont(0.99) within group(order by used.value) percent_used_p99 FROM container_memory_working_set_bytes used WHERE labels ? ('namespace' == 'default') GROUP BY container ORDER BY percent_used_p99 ASC LIMIT 100;

Aqui, estamos calculando o percentil 99 do uso de memória de cada contêiner no namespace default. Se quisermos identificar contêineres com baixo uso de memória ou com recursos sobreprovisionados, podemos realizar uma junção entre o uso de memória real e o limite de memória permitido, da seguinte forma:

sql
WITH memory_allowed AS ( SELECT labels(series_id) as labels, value,
min(time) start_time, max(time) as end_time
FROM container_spec_memory_limit_bytes total WHERE value != 0 and value != 'NaN' GROUP BY series_id, value ) SELECT val(memory_used.container_id) container, percentile_cont(0.99) within group(order by memory_used.value/memory_allowed.value) AS percent_used_p99, max(memory_allowed.value) max_memory_allowed FROM container_memory_working_set_bytes AS memory_used INNER JOIN memory_allowed ON (memory_used.time >= memory_allowed.start_time AND memory_used.time <= memory_allowed.end_time AND eq(memory_used.labels,memory_allowed.labels)) WHERE memory_used.value != 'NaN' GROUP BY container ORDER BY percent_used_p99 ASC LIMIT 100;

Além disso, se desejarmos visualizar a métrica go_gc_duration_seconds, podemos simplesmente executar a consulta abaixo para ver o comportamento do Garbage Collector:

sql
SELECT jsonb(v.labels)::text as "metric", time AS "time", value as "value"
FROM "go_gc_duration_seconds" v WHERE $__timeFilter("time") ORDER BY 2, 1;

No caso de desejarmos calcular o percentil 99 para a duração do coletor de lixo no contexto de pods, utilizamos a seguinte consulta:

sql
SELECT val(pod_id) as pod, percentile_cont(0.99) within group(order by value) p99 FROM go_gc_duration_seconds
WHERE value != 'NaN' AND val(quantile_id) = '1' AND pod_id > 0
GROUP BY pod_id ORDER BY p99 desc;

Essas consultas oferecem uma maneira eficaz de analisar o desempenho e otimizar o uso de recursos nos sistemas monitorados, tanto para métricas de uso de CPU e memória quanto para tempos de coleta de lixo.

É importante destacar que, ao realizar essas consultas, é fundamental entender a estrutura dos dados e como as agregações funcionam no contexto das métricas. As métricas em Prometheus, por exemplo, são coletadas e armazenadas com um timestamp e podem ser consultadas usando o PromQL, onde todas as consultas devem ser feitas com agregação dos dados dentro de cada série temporal antes de qualquer outra agregação adicional. Isso é crucial para garantir que os dados retornados sejam relevantes e precisos.

Além disso, ao lidar com dados de sistemas distribuídos, como no caso de microserviços, é possível combinar SQL e Prometheus para gerar métricas a partir de traces. O Promscale e o Jaeger, por exemplo, oferecem suporte à coleta e agregação de métricas a partir de spans, e esses dados podem ser usados para criar histogramas de latência ou identificar erros com base no status das requisições. No caso de Jaeger, a consulta SQL para recuperar dados de latência pode ser configurada para realizar agregações, como o cálculo do percentil 95 de latência em um período específico ou a identificação dos traces mais lentos.

Em resumo, a consulta e análise de métricas no contexto de sistemas de monitoramento envolve não apenas o conhecimento das linguagens de consulta, mas também uma compreensão profunda dos dados e das ferramentas utilizadas para agregá-los de forma útil. A integração entre Prometheus, SQL, Jaeger e Promscale oferece uma flexibilidade poderosa para coletar, analisar e visualizar dados de desempenho em tempo real.

Como a Análise de Traces e Correlation pode Melhorar o Diagnóstico de Problemas em Processos Assíncronos

É essencial configurar corretamente a rastreabilidade (trace) e a correlação de eventos (correlation), utilizando ambas de forma conjunta, em vez de depender unicamente dos perfis (profiles). O uso exclusivo de perfis é inadequado para a análise de solicitações individuais, pois eles não fornecem uma visão completa do processo de negócios ou da transação. Embora os perfis sejam úteis para uma análise detalhada de recursos como CPU, memória e outros aspectos em nível de método, eles têm várias limitações. A visualização desses perfis geralmente é feita por gráficos de chama (flame graphs) ou histogramas, que são comparativamente limitados quando confrontados com outros tipos de sinais. Além disso, diferentes linguagens de programação possuem perfis distintos, e alguns perfis podem não ser tão úteis quanto se espera, dependendo da linguagem em questão. Mesmo que o perfil eBPF possa medir com precisão a latência no 99º percentil, o valor médio gerado pelo perfil dificulta a identificação da distribuição exata do problema e da transação específica em que o erro ocorreu.

Quando um erro ocorre durante o processamento de uma transação, é necessário analisar o erro no contexto do span correspondente. Em muitos casos, a ferramenta de rastreamento (trace) não é capaz de instrumentar o servidor da API adequadamente. Nesses cenários, os logs podem ser usados para calcular a falha do usuário. No entanto, embora seja importante entender sinais específicos em detalhe, existem limites na compreensão de um problema utilizando apenas um sinal. A análise de um problema a partir de múltiplas perspectivas, com a combinação de diferentes sinais, é fundamental. Essa abordagem permite uma visão holística do sistema, essencial para lidar com os desafios da observabilidade em larga escala, que envolve milhares de desenvolvedores e não apenas indivíduos.

É papel do Site Reliability Engineer (SRE) fornecer uma variedade de sinais e guiar os desenvolvedores para utilizá-los de maneira eficaz. Não há um único sinal que resolva todos os problemas, e a habilidade de combinar sinais diversos é crucial. A utilização de um conjunto amplo de sinais e a orientação clara sobre como usá-los fazem toda a diferença na rápida identificação e resolução de problemas em sistemas complexos.

Retries e Configurações de Tempo de Espera: O Impacto no Desempenho

O uso de tentativas de reprocessamento (retries) pode ocorrer devido a uma gestão inadequada de threads ou à falta de controle sobre a ordem das chamadas. As tentativas constantes resultam em latência aumentada, o que pode ser prejudicial ao desempenho do sistema. A configuração correta de tentativas e timeouts é crucial, pois as tentativas são úteis apenas em caso de erros técnicos e não devem ser usadas para erros de negócios. O uso desnecessário de tentativas aumenta a latência e deve ser evitado. Além disso, é importante gerenciar adequadamente os processos de reentrega (redeliveries) e timeouts, ajustando-os conforme o comportamento específico do sistema. Não é recomendável configurar timeouts longos sem uma abordagem estruturada. Conhecer a natureza do seu trabalho e definir timeouts apropriados para diferentes processos é essencial para o bom desempenho do sistema.

Em processos assíncronos, a definição de tentativas e timeouts deve ser feita de maneira a garantir que a latência aceitável seja respeitada. Ajustar corretamente as configurações de filas, confirmações, pontos de verificação (checkpoints) e flags de reentrega em servidores de mensagens pode otimizar a performance do sistema. É comum que múltiplas APIs sejam chamadas a partir de uma única tela, sendo o processamento entre elas sequencial e não paralelo. Para garantir um desempenho eficiente, essa sequência de chamadas deve ser projetada cuidadosamente.

Mudanças na ordem de chamadas, como no caso de chamadas XHR (XMLHttpRequest), podem impactar negativamente a execução de microserviços no backend, causando falhas e novas tentativas de execução desde o início. Isso prejudica a experiência do usuário, e, por isso, a latência deve ser priorizada em relação ao throughput, ao uso simultâneo e ao volume de tráfego. O objetivo principal é reduzir erros e melhorar a confiabilidade do sistema.

Processamento Sequencial vs. Paralelo: Desafios e Oportunidades

Existem desafios consideráveis quando se lida com processamento sequencial e paralelo. No caso do processamento paralelo, a alocação inadequada de recursos pode resultar em uma redução do desempenho, mesmo que o sistema possa, teoricamente, processar mais tarefas simultaneamente. Por exemplo, se o número de pools de threads é configurado para 2 em vez de 10, mesmo que o sistema tenha capacidade para maior paralelismo, o desempenho será prejudicado devido a essa configuração incorreta. O processamento sequencial, por sua vez, pode aumentar a latência em determinados cenários, mesmo quando poderia ser realizado de maneira paralela, se os recursos fossem alocados corretamente.

Outro aspecto importante é a ordem de processamento em sistemas que utilizam filas. Em processos sequenciais, é necessário configurar um único assinante para cada fila, para garantir que a ordem das mensagens seja preservada. Caso contrário, a ordem pode ser alterada durante o processamento, o que leva a problemas adicionais, como a falha na execução correta de tarefas. Essas falhas podem resultar em uma cascata de spans sequenciais, como evidenciado na Figura 1-9 do exemplo, onde o processo que deveria ser paralelo acaba sendo processado de forma sequencial, causando um atraso desnecessário.

A implementação de um processamento paralelo eficiente é complexa e está longe de ser trivial. Durante o desenvolvimento e a operação do sistema, diversas dificuldades podem surgir, exigindo uma análise aprofundada de threads, recursos e configurações do sistema. Ao compreender corretamente os conceitos de processamento paralelo e sequencial, bem como as limitações e desafios envolvidos, é possível otimizar o desempenho do sistema e melhorar a experiência do usuário.

O Papel Crucial da Sincronização de Threads e Processos de Entrada e Saída (I/O)

A sincronização de threads, que garante que os recursos sejam acessados de forma sequencial para evitar problemas decorrentes da execução simultânea de múltiplos processos, é uma técnica essencial de gerenciamento de processos. Embora a latência de CPU e I/O não seja exatamente distinta, a complexidade de I/O em clusters, com suas múltiplas implementações de protocolos como offsets, gossip, reequilíbrio, quorum, replicação e cache LRU, dificulta a análise e a resolução de falhas. A falta de uma compreensão detalhada dos processos de I/O pode transformar esse componente do sistema em uma "caixa preta", tornando mais difícil isolar e corrigir falhas.

Em ambientes de clusters, como aqueles baseados em Kafka, Redis ou Elasticsearch, os I/Os são complexos e exigem uma análise especializada. Nesse contexto, o SRE desempenha um papel crucial, sendo necessário possuir experiência tanto nos recursos do sistema quanto nas particularidades dos clusters específicos utilizados pela organização. A compreensão das falhas de I/O, assim como a capacidade de isolar essas falhas, é vital para garantir a estabilidade e a eficiência do sistema.

Como a Observabilidade e AIOps Transformam as Operações de TI

Observabilidade e AIOps (Inteligência Artificial para Operações de TI) são conceitos que têm moldado a forma como as organizações gerenciam a infraestrutura tecnológica. Ambos são fundamentais para compreender como os sistemas operam e identificar rapidamente falhas ou ineficiências. Embora os algoritmos sejam responsáveis por realizar as inferências necessárias, cada tipo de algoritmo tem uma função específica, desempenhando seu papel conforme a sua especialização. A supervisão tradicional, por exemplo, tenta minimizar o ruído configurando limites e enviando alarmes quando ocorre uma falha. Porém, a observabilidade vai além. Ela envolve uma análise mais profunda dos componentes e interações de um sistema, permitindo a identificação precisa do serviço ou recurso falho e das dependências que ele possui.

A observabilidade não se limita apenas à detecção de falhas. Ela deve incluir a capacidade de distinguir claramente entre erros e exceções, identificar com precisão onde ocorre um erro no backend por meio de rastreamentos (traces) e compreender o impacto que o erro tem para o usuário final. Para que essa análise seja efetiva, é necessário coletar dados com precisão, armazená-los de forma acessível e permitir consultas que integrem diferentes sinais, a fim de extrair as informações necessárias para uma análise completa. A detecção de anomalias e a análise de causa raiz (Root Cause Analysis, RCA) devem ser automatizadas, utilizando inteligência artificial. A quantidade de dados gerenciada pela observabilidade pode ser impressionante e muitas vezes maior do que se imagina.

Antes da adoção de AIOps, o essencial era coletar dados de maneira estruturada, para que pudessem ser consultados de forma eficiente. AIOps, por sua vez, exige dados de alta qualidade para ser eficaz. Ter um runbook bem desenvolvido, que descreva as causas e soluções para diferentes falhas, é uma base fundamental. Além disso, é necessário garantir que os dados históricos de falhas estejam organizados de maneira que possam ser reproduzidos quando necessário. Uma abordagem eficaz pode ser a utilização de Event Sourcing, que mapeia eventos, comandos e estados, atendendo de maneira satisfatória aos requisitos de AIOps.

É importante também que a observabilidade se integre com outros sistemas de dados, como o CMDB (Configuration Management Database), permitindo a construção de modelos de dados para análise de causa raiz. Embora ferramentas como Promscale e Druid sejam poderosas para análises de dados, muitas vezes é mais simples começar com armazenamentos de eventos, que se adequam bem às necessidades de uma organização em termos de rastreabilidade e acessibilidade dos dados. Em vez de investir inicialmente em AIOps ou detecção de anomalias, é prudente começar com dashboards. Essas ferramentas visuais permitem uma compreensão mais ampla dos fluxos e processos, facilitando a transição gradual para a automação com AIOps.

Deve-se evitar o erro de pular diretamente para a implementação de AIOps sem antes entender adequadamente os processos e os dados existentes. A análise prévia é fundamental para garantir que a equipe de Engenharia de Confiabilidade de Site (SRE) seja capaz de avaliar corretamente a precisão dos resultados gerados pela AIOps. Muitas vezes, os sistemas de AIOps podem estar errados, o que exige uma capacidade crítica de julgamento por parte dos profissionais envolvidos. Grafana, por exemplo, é uma ferramenta útil para conectar fontes de dados diversas e gerar visualizações que ajudam a detectar problemas, ajustar limiares e alertas, e aprofundar a compreensão sobre o comportamento dos sistemas.

A coleta de dados também deve incluir informações de configuração, controle de acesso, permissões, atualizações e segurança, além de fornecer informações detalhadas sobre a infraestrutura, endpoints de aplicação e processos de negócios. A observabilidade abrange uma ampla gama de dados, como latência, desempenho e erros, e deve ser utilizada em conjunto com AIOps para melhorar a eficácia das operações de TI. O gerenciamento de alertas, por exemplo, é uma tarefa complexa. Embora o conceito de alertas seja simples, reduzir o ruído e torná-los acionáveis é um desafio constante. Para otimizar essa prática, é importante monitorar a frequência com que os alertas são reativados, o tempo de resposta inicial, o tempo necessário para resolver um problema e a precisão dos alertas.

Quando se trata de erros, o processo é semelhante ao de alarmes. A análise de causa raiz é essencial, pois muitas falhas podem ter origens diferentes, propagando-se e criando outros erros. A análise eficaz dos erros exige a separação entre erros que afetam a disponibilidade do serviço e aqueles que ocorrem mais cedo no fluxo de execução. Além disso, é preciso levar em consideração a frequência, os códigos de erro, as dependências dos erros e se eles estão se propagando.

Anomalias, alertas e erros são componentes comuns dentro da observabilidade, enquanto incidentes e problemas são frequentemente gerenciados em sistemas como Jira. Esses elementos possuem status que precisam ser monitorados e analisados para fornecer uma visão precisa do sistema. Recentemente, algumas implementações de AIOps têm incorporado ferramentas como Slack para facilitar a comunicação por meio do ChatOps, otimizando a interação entre operadores e a gestão de incidentes.

Existem dois tipos principais de serviços de IA: ML (Machine Learning) e LLM (Large Language Models). A principal tarefa do aprendizado de máquina (ML) é o pré-processamento de dados, que envolve a estruturação e organização dos dados antes de serem analisados ou utilizados em modelos. O mesmo se aplica aos LLMs, que também exigem um pré-processamento adequado para garantir que os dados sejam estruturados e limpos antes de serem armazenados em vetores. Uma organização eficaz dos dados e um runbook bem estruturado são essenciais para melhorar a precisão e a eficiência desses modelos de IA.

A gestão de mudanças e implantações, como atualizações contínuas, rollbacks e testes AB, também deve ser integrada ao processo de observabilidade. Quando essas mudanças ocorrem, elas geram eventos que precisam ser monitorados e analisados para garantir que o sistema continue operando de maneira otimizada. A organização precisa estar alinhada para que mudanças sejam realizadas de forma coordenada e sincronizada, garantindo uma resposta rápida e eficaz às falhas ou incidentes que possam ocorrer.

No contexto das operações de TI modernas, a previsão de custos e o gerenciamento de recursos também desempenham um papel essencial. A capacidade de antecipar o crescimento de recursos, identificar se um recurso está sendo subutilizado ou alocar recursos de maneira mais eficiente pode resultar em uma economia significativa. A observabilidade também deve incluir o acompanhamento de como os recursos estão sendo usados e se estão sendo distribuídos de maneira equilibrada entre as diferentes partes do sistema.

A integração de dados de várias fontes e a capacidade de realizar análises detalhadas de falhas e dependências tornam a observabilidade um componente indispensável nas operações de TI. À medida que as organizações avançam em sua jornada de transformação digital, a observabilidade e AIOps se tornam ferramentas cada vez mais essenciais para garantir a continuidade dos negócios e a eficiência operacional.