Pitch: colocamos o Prometheus na mesma aba do Postgres. O que aprendemos levando PromQL para uma IDE SQL
Em quase todo time de microsserviços que conheci, a lista é parecida: um Postgres, um Mongo, um Redis de cache, um Elasticsearch para busca ou logs, e um Prometheus olhando tudo isso. Cada um vem com seu próprio cliente, seu próprio login e sua própria aba.
Num incidente, a pergunta quase nunca é sobre um só deles. "A latência da API subiu às 14h10. Foi lock no Postgres ou o Redis começou a expirar chave?" Para responder, você abre o Grafana de um lado, o cliente do banco do outro, copia horário de um para o outro e torce para os fusos baterem.
Sou um dos mantenedores do LibreDB Studio, uma IDE SQL open source (MIT) que roda no navegador e é implantada perto dos dados, em Docker ou Kubernetes (tem Helm chart e operator). Ela já falava com Postgres, MySQL, Mongo, Redis, Elasticsearch e outros. Na versão mais recente o Prometheus entrou como mais uma conexão: você cadastra como cadastra um Postgres, escreve PromQL no mesmo editor e o resultado cai na mesma grade.
Antes de tudo: isso não substitui o Grafana. Não tem dashboard, não tem alerta, não tem seletor de tempo. O Grafana é onde você acompanha métricas. Aqui é onde você faz uma pergunta pontual ao Prometheus ao lado das perguntas que já faz aos bancos.
Fui ver se as IDEs de banco já faziam isso. O pedido existe no DBeaver desde 2022 (#15412) e no DataGrip desde 2023 (DBE-18115, 20 votos), os dois ainda abertos. Então este post é menos sobre a feature e mais sobre as decisões que ela exigiu, porque PromQL não é SQL e quase nada encaixa de primeira.
PromQL não tem tabela
A IDE pensa em tabelas, linhas e colunas. O Prometheus tem métricas, séries e rótulos. O mapeamento que ficou:
- a métrica faz o papel da tabela, e os rótulos das séries viram colunas, mais
timestampevalue - cada série é uma linha
- a árvore lateral tem seis pastas: Metrics, Rule groups, Recording rules, Alerting rules (com firing e pending marcados), Scrape pools e Targets (com down e unknown marcados)
A árvore acabou sendo uma das partes mais úteis. Para ver quais targets estão down, normalmente você vai na UI do próprio Prometheus. Aqui eles ficam na mesma árvore das tabelas do Postgres.
Só instant query, de propósito
Toda consulta vai para /api/v1/query. Não usamos query_range, não existe $__interval, não existe max data points. O intervalo você escreve no próprio PromQL:
up[1h] # amostras cruas da última hora
rate(http_requests_total[5m])[1h:1m] # subquery com passo de 1 minuto
O motivo: no Grafana o passo é calculado a partir do intervalo e da largura do painel (max data points), então a mesma consulta pode dar números diferentes em painéis diferentes. Aqui o resultado depende só do texto que você escreveu. Se alguém cola a consulta no canal do incidente, quem abrir vê o mesmo número. O preço é que o passo passa a ser problema seu.
Uma matriz vira uma tabela larga
Um resultado matrix vira uma linha por timestamp e uma coluna por série, e o nome da coluna são os rótulos que distinguem a série ({job="api"}). Com isso a aba de gráfico que já existia para SQL desenha várias linhas sem mudar nada.
Um detalhe que rendeu discussão: NaN e Inf continuam como texto. Em JSON um NaN vira null, e nessa tabela null já quer dizer "não havia amostra neste instante". Trocar um pelo outro seria mentir sobre o dado.
Não derrubar o Prometheus de ninguém
As consultas da API disputam os mesmos slots de --query.max-concurrency (padrão 20) que a avaliação das regras de alerta. Uma IDE que qualquer pessoa do time abre não pode atrasar um alerta. Por isso cada conexão tem no máximo 4 consultas em andamento, e o resto espera numa fila. Medimos contra o Prometheus 3.13.3: 4 consultas pesadas durante 60 segundos, zero avaliações de regra perdidas.
O cancelamento também é de verdade. Quando você cancela no editor, a requisição HTTP é abortada e o Prometheus para de avaliar. Na medição, a consulta cancelada terminou em 1,02 s segundo o log do próprio servidor, e a mesma consulta sem cancelar levou 3,62 s.
Só leitura por construção
Não é só "o usuário não tem permissão". O provider conhece 15 caminhos de leitura fixos e nenhum outro. Admin API, /-/reload, /-/quit e remote write simplesmente não existem no código. Mais três coisas:
- o navegador nunca fala com o Prometheus: a requisição sai do servidor da IDE, a mesma topologia de proxy que o Grafana usa
- a expressão vai no corpo do POST, não na URL, então um proxy no meio do caminho não grava sua consulta no access log
- redirecionamento não é seguido, porque um 307 levaria o corpo e o
Authorizationpara outro host
E nenhuma dependência nova: nada de biblioteca cliente do npm, só fetch e node:https do próprio runtime.
O que ainda não funciona
Prefiro que você saiba antes de testar:
- sem autocomplete de métricas e rótulos, só destaque de sintaxe (os nomes vêm da árvore)
- sem seletor de tempo e sem
query_range - o gráfico desenha amostra ausente como 0, então no
upum buraco parece um target down - sem prefixo de caminho, então Mimir e o Prometheus hospedado do Grafana Cloud ficam de fora por enquanto
- sem SigV4, então nada de AWS Managed Prometheus
- VictoriaMetrics funciona pela API compatível, mas em parte: editor e árvore de métricas sim, algumas telas de status não
Para testar
docker run -p 3000:3000 libredb/libredb-studio:latest
Abra http://localhost:3000. A senha do admin aparece no log na primeira execução. Nova conexão, tipo Prometheus, porta 9090.
Número da versão planejada: 0.17.0, atualmente disponível como prévia (tag mais recente da imagem Docker).
O código e a documentação do provider, com todas as medições, estão em https://github.com/libredb/libredb-studio (arquivo docs/providers/prometheus.md).
Queria ouvir quem opera Prometheus em produção:
- Para uma pergunta pontual durante um incidente, vocês abrem o Explore do Grafana, a UI do Prometheus ou outra coisa?
- Escrever o intervalo no PromQL em vez de usar um seletor de tempo: ajuda ou atrapalha no dia a dia?
- O que mais vocês gostariam de consultar no mesmo lugar que os bancos?
Fonte: https://libredb.org