4.15 Monitoramentos Específicos de uma Aplicação

No capítulo anterior, construímos um sidecar genérico capaz de coletar métricas de infraestrutura — uso de CPU, memória, rede e outros indicadores importantes do ambiente. Esse tipo de monitoramento é extremamente valioso, pois nos dá visibilidade sobre a saúde do container e do sistema como um todo. No entanto, ele responde apenas parte do problema.

Quando trabalhamos com aplicações de Machine Learning, especialmente em produção, não estamos interessados apenas em saber se o container está saudável — queremos entender se o modelo está se comportando corretamente.

Um sidecar genérico, por melhor que seja, não tem conhecimento do domínio da aplicação. Ele não sabe quando uma inferência começa ou termina, não entende o que é uma predição, nem consegue distinguir se uma operação demorou porque o modelo é pesado ou porque houve contenção de CPU. Em outras palavras, ele observa o “corpo”, mas não entende o “comportamento”.

É aqui que entram as métricas específicas da aplicação. Ao instrumentarmos diretamente o código do serviço, conseguimos medir aspectos fundamentais como:

  • Tempo de inferência do modelo

  • Quantidade de requisições processadas

  • Distribuição de latência

  • Taxa de erro da aplicação ou métricas de qualidade do modelo (quando disponíveis)

Essas métricas trazem uma visão muito mais próxima do problema de negócio. Por exemplo, uma CPU em 30% pode parecer saudável, mas se o tempo de inferência estiver crescendo gradualmente, isso pode indicar problemas no modelo, no volume de dados ou até um indício de data drift.

Outro ponto importante é que sistemas de Machine Learning frequentemente possuem características que não aparecem em aplicações tradicionais. Modelos diferentes possuem custos computacionais muito distintos, pipelines podem envolver etapas complexas de pré-processamento, e a própria natureza probabilística das predições exige um olhar mais cuidadoso sobre comportamento ao longo do tempo.

Portanto, o monitoramento em MLOps precisa ir além da infraestrutura: ele deve capturar sinais do próprio modelo. E isso só é possível quando instrumentamos explicitamente a aplicação, expondo métricas que fazem sentido para aquele contexto específico.

É exatamente isso que começaremos a fazer agora.

Uma coisa básica que precisamos fazer é medir a latência de acesso ao modelo, ou seja, quanto tempo demora uma inferência.

Vamos começar alterando o projeto http-api-classificacao-produtos-container-unico.

Modifique o requirements.txt:

Flask==3.1.3
scikit-learn==1.8.0
gunicorn==25.1.0
+prometheus-client==0.24.1

E agora altere app.py:

Por padrão, o Prometheus espera que as métricas estejam disponíveis no caminho /metrics. Como nossa API está atrás do nginx (portanto apareceria no caminho /api/metrics), precisamos expor essa rota corretamente.

Agora podemos reconstruir a imagem e subir o container novamente.

Agora vamos adicionar essa nova fonte de métricas ao Prometheus. Na pasta prometheus, modifique o arquivo prometheus.yml:

Reconstrua a imagem e suba-o novamente:

Acessando a interface do Prometheus (http://localhost:9090), você verá que agora existem 3 novas métricas para serem consultadas. Isso aconteceu porque, quando utilizamos um Summary no Prometheus, ele não gera apenas uma métrica — ele gera um conjunto de métricas relacionadas. Neste caso, são as seguintes:

  • product_classifier_model_inference_seconds_count: Representa o total de inferências observadas. É basicamente um contador. Você pode usar para calcular vazão/throughput (inferências por segundo).

  • product_classifier_model_inference_seconds_sum: Representa a soma total do tempo de todas as requisições. Com isso, você pode calcular a média.

  • product_classifier_model_inference_seconds_created: Indica o timestamp de criação da métrica. Na prática, raramente é usada em dashboards, sendo mais útil para debug ou inspeção interna.

O tipo Summary é útil para medições locais, mas possui limitações em ambientes distribuídos, pois seus quantis não podem ser agregados entre múltiplas instâncias. Em cenários com múltiplos containers, o uso de Histogram costuma ser mais recomendado.

Criando um dashboard

Agora que nossa API expõe métricas e o Prometheus já está coletando, podemos visualizar no Grafana. Vamos criar um dashboard focado em métricas de inferência:

  1. Acesse o Grafana

  2. Clique em Dashboards

  3. Clique em New → New Dashboard (para o nome, pode escolher "Métricas de inferência")

Agora crie um painel:

  • Title: Latência do classificador de produtos (Média)

  • Unit: seconds (s)

  • Visualization: Time series

Testando com o K6

Acesse a pasta do K6 e vamos executar nosso teste de carga que configuramos na seção anterior:

Veja como o Grafana começa a mostrar a latência da inferência, que é bem baixa. Se compararmos com os valores exibidos pelo K6, podemos observar que os valores observados no Grafana são bem menores. Isso acontece porque estamos medindo exclusivamente o tempo de inferência, enquanto o K6 mede o tempo de ponta-a-ponta, incluindo a requisição, o tratamento que passa pelo nginx/wsgi, até chegar ao código da inferência.

De fato, este modelo é para ser rápido mesmo, pois se trata de um classificador simples.

Mas isso também evidencia uma decisão importante: ao instrumentar apenas o tempo de inferência, estamos ignorando latências de rede, serialização, fila e proxy. Dependendo do objetivo, pode ser necessário medir múltiplas etapas separadamente. Por exemplo, em arquiteturas assíncronas, como pipelines baseados em filas ou sistemas de streaming, essa diferença se torna ainda mais relevante. Nesses cenários, o tempo de inferência isolado representa apenas uma parte do processamento total, sendo necessário monitorar também métricas como o tamanho da fila (lag), o tempo total de processamento end-to-end e a taxa de produção e consumo de mensagens. Essas métricas permitem identificar gargalos que não estão diretamente relacionados ao modelo, mas sim à infraestrutura ao redor.

Para efeitos de comparação, vamos medir o tempo gasto para inferência do modelo baseado em BERT.

Vá até a pasta analise-sentimentos.

Modifique o requirements.txt:

E agora altere app.py:

Agora basta construir a imagem e subir o container:

Note que estamos rodando o consumidor em modo iterativo para ver as mensagens sendo consumidas. Será útil para a demonstração que faremos. No entanto, essa configuração acrescenta uma demora considerável na execução.

Vamos adicionar mais essa fonte de métricas ao Prometheus. É o mesmo que fizemos antes. Altere o arquivo prometheus.yml dentro da pasta prometheus:

Reconstrua a imagem e suba-o novamente:

Agora adicione mais um painel ao mesmo dashboard do Grafana:

  • Title: Latência do classificador de sentimentos (Média)

  • Unit: seconds (s)

  • Visualization: Time series

Para testar essa nova métrica, você pode usar o chatbot, mas aí caímos no problema de gerar testes manualmente. Vamos fazer uma abordagem diferente.

Vá até a pasta k6 e crie um arquivo requirements.txt:

Agora crie um arquivo app.py:

Crie um Dockerfile

Basta agora construir a imagem e executar o container:

Ainda na mesma pasta, crie o arquivo load-test-kafka.js:

Para executar, basta rodar:

Aguarde as métricas serem exibidas. Uma curiosidade desse teste é que o consumidor irá continuar executando mesmo depois que o K6 terminar seu trabalho. Isso acontece porque essa solução é baseada em uma comunicação assíncrona, ou seja, o K6 apenas enfileira (via proxy) as requisições, que serão tratadas posteriormente. Esse comportamento é característico de sistemas assíncronos: o produtor (k6) apenas envia mensagens para a fila, enquanto o consumidor processa no seu próprio ritmo. Isso desacopla geração e processamento, mas também pode introduzir latência adicional.

Para ver isso acontecendo, basta abrir o dashboard que criamos anteriormente e ver como a CPU do container que analisa sentimentos continua ativa mesmo depois que o K6 termina seu trabalho, o mesmo acontecendo com o broker, que fica entregando as mensagens.

Note também como a latência do modelo BERT é bem maior, na média, do que a latência do modelo estatístico mais simples que classifica produtos.

Para finalizar este exemplo, exporte o dashboard como JSON e inclua-o no provisionamento do Grafana como fizemos antes.

Considerações Finais

Ao longo deste capítulo, demos um passo importante ao sair do monitoramento puramente de infraestrutura e avançar para a instrumentação da aplicação, capturando métricas diretamente relacionadas ao comportamento do modelo. A medição da latência de inferência, por exemplo, já nos permite compreender melhor o custo computacional do modelo e comparar diferentes abordagens — como vimos ao contrastar um classificador simples com um modelo baseado em BERT.

No entanto, é fundamental entender que essa métrica, por si só, ainda representa apenas uma parte da história. Em sistemas reais, especialmente aqueles baseados em arquiteturas distribuídas e assíncronas, o tempo de inferência é apenas um dos componentes do tempo total percebido pelo usuário ou pelo sistema.

Por isso, é comum que soluções mais completas de monitoramento incluam outras dimensões importantes. Um exemplo é o tamanho da fila (lag do Kafka), que indica quantas mensagens ainda aguardam processamento. Mesmo que o modelo seja rápido, um aumento no lag pode sinalizar gargalos no consumo ou picos de demanda que o sistema não está conseguindo absorver em tempo real.

Outra métrica essencial é o tempo total de processamento end-to-end, que considera toda a jornada da requisição: desde o momento em que ela é gerada até a entrega do resultado final. Essa visão é particularmente importante porque reflete a experiência real do sistema como um todo, incorporando latências de rede, filas, proxies e processamento.

Além disso, acompanhar a taxa de produção versus taxa de consumo permite avaliar o equilíbrio do sistema. Quando a taxa de entrada de dados supera consistentemente a capacidade de processamento, cria-se um acúmulo progressivo de mensagens — o que, inevitavelmente, leva ao aumento de latência e possível degradação do serviço.

Essas métricas adicionais reforçam um ponto central: observabilidade não é apenas coletar dados, mas compreender o sistema em sua totalidade. Em aplicações de Machine Learning, isso significa ir além da inferência isolada e considerar o comportamento do pipeline como um todo, incluindo suas interações com outros componentes da arquitetura.

À medida que evoluímos nossas práticas de monitoramento, passamos a construir não apenas dashboards mais completos, mas também uma visão mais fiel do funcionamento do sistema em produção — condição essencial para tomar decisões informadas, diagnosticar problemas com precisão e garantir a qualidade contínua das soluções baseadas em Machine Learning.

Nota sobre o desenvolvimento do material

O conteúdo desta seção contou com o apoio do ChatGPT como ferramenta auxiliar na estruturação de explicações, revisão de conceitos e refinamento técnico. Todas as decisões de conteúdo, exemplos e direcionamento didático foram conduzidas pelo autor.

Atualizado