No mundo do desenvolvimento, é necessário saber como a aplicação que estamos trabalhando está se comportando e a maneira mais conhecida de realizarmos isso é por meio de métricas. Elas podem ser de diversos tipos, como, por exemplo, de desempenho, de produto ou de saúde. Atualmente, o Prometheus é amplamente utilizado pelo mercado a fim de coletar essas métricas.
Ele é um serviço open-source mantido pela CNCF , a Cloud Native Computing Foundation. Ele funciona da seguinte maneira: um endpoint é exposto na aplicação. Esse endpoint retorna um texto no formato esperado, e o Prometheus acessa esse endpoint de tempos em tempos coletando as informações dali.
# HELP failure_rate The total number of failed events
# TYPE failure_rate counter
failure_rate 3280
O formato é bem simples, para cada métrica, você vai ter pelo menos três entradas:
- O #HELP mostra a descrição da métrica.
- O #TYPE define o tipo da métrica.
- A terceira linha mostra o valor da métrica.
Em aplicações escritas com Go, temos uma biblioteca que facilita ainda mais a exposição dessas métricas. Ela implementa um handler que expõe por padrão as principais métricas relacionadas ao software, como, por exemplo, goroutines, memória, heap, etc. O exemplo abaixo demonstra melhor como se usa:
package main
import (
"fmt"
"net/http"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
func main() {
fmt.Println("Monitoring service...")
http.Handle("/metrics", promhttp.Handler())
http.ListenAndServe(":8080", nil)
}
Pode-se notar que com poucas linhas temos o endpoint de métricas e um servidor web funcionando! Agora, como configurar um Prometheus para coletá-las? O primeiro passo é subir ambas as aplicações, para isso o melhor é utilizar o docker compose
.
services:
myapp:
image: myapp
build: .
container_name: myapp
ports:
- 8080:8080
restart: unless-stopped
prometheus:
image: prom/prometheus
container_name: prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yaml'
ports:
- 9090:9090
restart: unless-stopped
volumes:
- ./prometheus:/etc/prometheus
- prom_data:/prometheus
volumes:
prom_data:
No arquivo compose, é definida que a aplicação vai escutar na porta 8080
e o Prometheus na porta 9090
. Ele também espera um arquivo de configuração. Este arquivo define onde e com qual frequência ele deve fazer a varredura.
global:
scrape_interval: 15s
scrape_timeout: 10s
evaluation_interval: 15s
scrape_configs:
- job_name: myapp
honor_timestamps: true
scrape_interval: 15s
scrape_timeout: 10s
metrics_path: /metrics
scheme: http
static_configs:
- targets:
- 'myapp:8080'
Também definimos o Dockerfile da nossa aplicação falsa:
# copied from the internet
# syntax=docker/dockerfile:1
FROM golang:1.23
# Set destination for COPY
WORKDIR /app
# Download Go modules
COPY go.mod go.sum ./
RUN go mod download
# Copy the source code. Note the slash at the end, as explained in
# https://docs.docker.com/reference/dockerfile/#copy
COPY ./ ./
# Build
RUN CGO_ENABLED=0 GOOS=linux go build -o /myapp
# Optional:
# To bind to a TCP port, runtime parameters must be supplied to the docker command.
# But we can document in the Dockerfile what ports
# the application is going to listen on by default.
# https://docs.docker.com/reference/dockerfile/#expose
EXPOSE 8080
# Run
CMD ["/myapp"]
Se quiser verificar se tudo está funcionando, é só acessar http://localhost:9090
e ver se deu certo a configuração. Contudo, muitas vezes as métricas padrões não são suficientes para representar o comportamento da nossa aplicação e é necessário definir métricas customizadas. Ao utilizar a biblioteca padrão do Prometheus para Go, essa tarefa se torna trivial e simples, como o exemplo abaixo:
successRate := promauto.NewCounter(prometheus.CounterOpts{
Name: "success_rate",
Help: "The total number of succeded events",
})
successRate.Inc()
Desta forma, uma nova métrica será retornada ao acessar o endpoint /metrics
. Porém, nem sempre é possível ter um servidor web para ter as métricas expostas dessa forma. A partir dessa premissa, foi desenvolvido o Pushgateway
. Ele funciona da seguinte forma: você envia suas métricas por chamadas HTTP e ele armazena e expõe o endpoint /metrics
para a coleta do Prometheus. Todavia, nem sempre é uma boa ideia utilizar esta estratégia, pois, segundo a própria documentação oficial:
- Quando se monitora múltiplas instâncias por meio de um único Pushgateway, ele se torna um ponto único de falha e um potencial gargalo.
- Você perde o monitoramento automático da saúde da sua aplicação, gerada em cada varredura.
- O Pushgateway nunca esquece nenhum dado que foi enviado para ele e vai sempre os expor para o Prometheus, exceto caso seja manualmente deletado.
Para mais informações, recomendo a leitura dessa documentação . Mas, como existem casos de uso, vamos ver como podemos usar a biblioteca do Prometheus para fazer um Push. O exemplo abaixo demonstra que muda muito pouco do exemplo anterior. Só adicionamos a instrução referente ao Push e para onde ele deve ser realizado.
successRate := promauto.NewCounter(prometheus.CounterOpts{
Name: "success_rate_pg",
Help: "The total number of succeded events",
})
p.successRate.Inc()
err := push.New("http://pushgateway:9091", "pg").
Collector(p.successRate).
Grouping("myapp", "success_rate_pg").
Push()
if err != nil {
fmt.Println("Could not push completion time to Pushgateway:", err)
}
Também é necessário configurar o compose para iniciar o Pushgateway.
services:
# ... myapp and prometheus already exposed
pushgateway:
image: prom/pushgateway
container_name: pushgateway
restart: unless-stopped
expose:
- 9091
ports:
- "9091:9091"
E por fim, vamos dizer ao Prometheus que ele deve varrer o Pushgateway também.
global:
scrape_interval: 15s
scrape_timeout: 10s
evaluation_interval: 15s
scrape_configs:
- job_name: pushgateway
honor_timestamps: true
scrape_interval: 15s
scrape_timeout: 10s
metrics_path: /metrics
scheme: http
static_configs:
- targets:
- 'pushgateway:9091'
Vale a pena mencionar que, para o exemplo, eu utilizei somente o tipo de métrica Counter, ou em português Contador. Contudo, existem diversos outros tipos que podem ser encontrados aqui ! Para mais exemplos utilizando o Prometheus com Go, você pode checar os exemplos oficiais da biblioteca ou a PoC onde implemento um simulador de monitoramento sintético e gero métricas através do endpoint e do Pushgateway.
Se você gostou do post, me diz: quais métricas você geralmente monitora na sua aplicação? Elas são métricas padrões ou customizadas?