Por: @eduardoh Publicado em: 2022-07-29

Prometheus exportando métricas do hardware do servidor

Objetivo

Validar o funcionamento do Prometheus exportando métricas do hardware do servidor para que seja possível consolidar essas métricas na interface do FWFLEX exibindo um histórico de consumo dos recursos, como CPU, memória, disco, etc.

Instalação

O Prometheus já vem instalado no FWFLEXv3, então essa etapa não é necessária para aplicação no produto.

Para o laboratório, utilizamos uma imagem limpa do AlmaLinux.

Como não vamos utilizar o produto ainda, vamos executar o Prometheus via Docker, para facilitar a configuração do ambiente, etc.

Preparando o ambiente

Como trata-se de uma instalação limpa do AlmaLinux, lembre-se de desativar o SELinux e também o FirewallD:

setenforce 0
sed -i 's|SELINUX=enforcing|SELINUX=disabled|g' /etc/selinux/config

systemctl stop firewalld
systemctl disable firewalld

Instalando o Docker no AlmaLinux

Para executar o Prometheus, será necessário instalarmos o Docker no AlmaLinux:

dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

dnf install docker-ce docker-ce-cli containerd.io

Garantindo que o serviço será inicializado com o sistema:

systemctl start docker
systemctl enable docker

Executando container do Prometheus

Para o laboratório, iremos executar o Prometheus do formato mais simples possível, sem utilizar volume externo, segmentação de rede ou qualquer configuração avançada, para isso, execute o container do Prometheus.

Antes vamos preparar um arquivo de configuração do Prometheus, seguindo o padrão que utilizamos no produto (lembrando de alterar os targets conforme necessário):

vim /root/prometheus.yml

global:
  scrape_interval: 5s

scrape_configs:
  - job_name: "collectd"
    static_configs:
      - targets: ["localhost:9103"]

Agora vamos de fato criar nosso container utilizando nosso arquivo como padrão:

docker run --name prometheus --rm -d -p 9090:9090 -v /<diretório>/prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheus
  • docker run: Indica que iremos executar um container simples
  • --name prometheus: Indica o nome do container
  • --rm: Quando parado o container, ele irá se excluir automaticamente
  • -d: Define o container para ser executado em background
  • -p 9090:9090: Está mapeando a porta do serviço Prometheus do container para a porta no host, ambas na porta 9090
  • -v /<diretório>/prometheus.yml:/etc/prometheus/prometheus.yml: Significa que estamos mapeando uma arquivo local para dentro do container
  • prom/prometheus: Indica qual imagem iremos utilizar para executar nosso container

Após executar, já podemos ir no navegador e validar se a interface do Prometheus está em execução:

Podemos validar também se as métricas estão sendo expostas corretamente, basta acrescentar /metrics a URL:

Instalando collectd para extração de métricas do host

Hoje já utilizamos o collectd no produto para exposição e extração dos logs, então no nosso laboratório vamos precisar instalar o mesmo pacote.

No produto temos os seguintes pacotes marcados como spec:

Requires: collectd >= 5.9
Requires: collectd-write_prometheus >= 5.9
Requires: collectd-netlink >= 5.9

Este pacote faz parte do repositório epel, então como é uma instalação limpa do AlmaLinux, devemos instalar também:

yum install epel-release
yum update

Instalando os pacotes do collectd:

yum install collectd collectd-write_prometheus collectd-netlink

Para dar sequência no cenário, vamos copiar um exemplo do arquivo /etc/collectd.conf que temos no produto, para sermos fieis ao ambiente de produção do FWFLEX:

#Hostname    "localhost"
#FQDNLookup   true
#BaseDir     "/var/lib/collectd"
#PIDFile     "/var/run/collectd.pid"
#PluginDir   "/usr/lib64/collectd"
#TypesDB     "/usr/share/collectd/types.db"

Interval     5

#MaxReadInterval 86400
#Timeout         2
#ReadThreads     5
#WriteThreads    5

# Logging                                                                    #
#----------------------------------------------------------------------------#

LoadPlugin logfile

<Plugin logfile>
  LogLevel info
  File STDOUT
  Timestamp true
  PrintSeverity false
</Plugin>

# LoadPlugin section                                                         #
#----------------------------------------------------------------------------#

LoadPlugin netlink
LoadPlugin write_prometheus

# Plugin configuration                                                       #
#----------------------------------------------------------------------------#

<Plugin netlink>
  Interface "All"
  QDisc "All"
  Class "All"
  Filter "All"
</Plugin>

<Plugin write_prometheus>
  Port "9103"
</Plugin>

Include "/etc/collectd.d"

Lembre-se de reiniciar e habilitar a inicialização do serviço:

systemctl enable collectd
systemctl restart collectd

Com essas configurações, teremos um padrão de envio de métricas de do plugin netlink que captura informações de rede.

Nosso objetivo é tratar informações do hardware, então vamos ativar alguns outros plugins para termos essa extração personalizada.

No mesmo arquivo /etc/collectd.conf, adicione o seguinte (seguindo a ordem dentro do arquivo de configuração):

[...]
# LoadPlugin section                                                         #
#----------------------------------------------------------------------------#

LoadPlugin load
LoadPlugin memory
LoadPlugin disk

[...]
# Plugin configuration                                                       #
#----------------------------------------------------------------------------#

[...]

<Plugin disk>
       Disk "/^[hs]d[a-f][0-9]?$/"
       IgnoreSelected false
</Plugin>

[...]

Antes de reiniciarmos o serviço, precisamos instalar um plugin adicional que não utilizamos no produto, trata-se do plugin de disco, para isso, execute o seguinte:

yum install collectd-disk -y

Agora podemos reiniciar o collectd.

Certifique-se que o serviço está no ar. Acompanhe os logs, deverá aparecer algo assim:

ago 01 08:43:16 LAB-SPIKE-47630 collectd[4537]: [2022-08-01 08:43:17] plugin_load: plugin "logfile" successfully loaded.
ago 01 08:43:16 LAB-SPIKE-47630 collectd[4537]: [2022-08-01 08:43:17] plugin_load: plugin "load" successfully loaded.
ago 01 08:43:16 LAB-SPIKE-47630 collectd[4537]: [2022-08-01 08:43:17] plugin_load: plugin "memory" successfully loaded.
ago 01 08:43:16 LAB-SPIKE-47630 collectd[4537]: [2022-08-01 08:43:17] plugin_load: plugin "disk" successfully loaded.
ago 01 08:43:16 LAB-SPIKE-47630 collectd[4537]: [2022-08-01 08:43:17] plugin_load: plugin "netlink" successfully loaded.
ago 01 08:43:16 LAB-SPIKE-47630 collectd[4537]: [2022-08-01 08:43:17] plugin_load: plugin "write_prometheus" successfully loaded.
ago 01 08:43:16 LAB-SPIKE-47630 collectd[4537]: [2022-08-01 08:43:17] Systemd detected, trying to signal readiness.
ago 01 08:43:16 LAB-SPIKE-47630 collectd[4537]: [2022-08-01 08:43:17] write_prometheus plugin: Listening on [::]:9103.
ago 01 08:43:16 LAB-SPIKE-47630 collectd[4537]: [2022-08-01 08:43:17] Initialization complete, entering read-loop.

A partir de agora já temos métricas exportadas relacionadas load, memory e disk. Podemos verificar se as métricas estão sendo importadas, acessando pelo navegador a URL http://<IP>:9103/metrics

O produto FWFLEX precisa as métricas expostas dessa maneira, agora ele já consegue interagir extraindo e montando dashboards (como já acontece nas métricas de interfaces de rede).

Exportando métricas da nossa aplicação

Visando o futuro do produto, decidimos inserir na documentação um exemplo de integração de instrumentação de aplicação utilizando bibliotecas nativas do Prometheus.

Com elas é possível exportarmos métricas da nossa aplicação core, para isso deixo aqui uma documentação de exportação de métricas utilizando a lib do Python, mas existem para várias outras linguagens.

Para o nosso exemplo, vamos utilizar uma imagem de Docker do NodeJS, com um código simples, apenas para mostrar os acessos HTTP que tivemos a nossa aplicação.

Vamos criar nosso diretório onde terão os arquivos da nossa aplicação:

mkdir /root/myapp

Agora vamos criar três arquivos dentro dessa pasta:

  • Dockerfile
FROM node:12-alpine

RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app

WORKDIR /home/node/app

COPY package*.json ./

USER node

RUN npm install

COPY --chown=node:node . .

EXPOSE 3000

CMD [ "node", "app.js" ]
  • app.js
var express = require('express');
var promClient = require('prom-client');
const register = promClient.register;

var app = express();

const counter = new promClient.Counter({
	name: 'myapp_request_total',
	help: 'Contador de requests',
	labelNames: ['statusCode']
});

const gauge = new promClient.Gauge({
	name: 'myapp_free_bytes',
	help: 'Exemplo de gauge'
});

const histogram = new promClient.Histogram({
	name: 'myapp_request_time_seconds',
	help: 'Tempo de resposta da API',
	buckets: [0.1, 0.2, 0.3, 0.4, 0.5]
});

const summary = new promClient.Summary({
	name: 'myapp_summary_request_time_seconds',
	help: 'Tempo de resposta da API',
	percentiles: [0.5, 0.9, 0.99]
});

app.get('/', function (req, res) {
	counter.labels('200').inc();
	counter.labels('300').inc();
	gauge.set(100 * Math.random());
	const tempo = Math.random();
	histogram.observe(tempo);
	summary.observe(tempo);

	res.send('Hello World!');
});

app.get('/metrics', async function (req, res) {
	res.set('Content-Type', register.contentType);
	res.end(await register.metrics());
})

app.listen(3000);
  • package.json
{
  "name": "app",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.16.4",
    "prom-client": "^13.0.0"
  }
}

Próximo passo será gerar uma imagem Docker que contenha nossa aplicação de exemplo:

cd /root/myapp/

docker build -t myapp:latest .

Agora podemos executar nossa imagem e ver o resultado pelo navegador:

docker run --name myapp -p 3000:3000 -d --rm myapp:latest

No navegador, basta colocar o IP do servidor seguido da porta 3000, e para acessar as métricas exportadas pela lib do Prometheus, basta indicar a URI /metrics:

Lembrando, que isso é um exemplo de como extrair métricas de uma aplicação. Sugestão seria avaliar se teríamos dados necessários e interessantes para exportar num futuro.

A última etapa é adicionar um novo target ao Prometheus, apontando nossa aplicação NodeJS. Para isso, no arquivo prometheus.yml criado lá no início do tutorial para rodar nosso Prometheus, iremos adicionar ao final dele as seguintes linhas:

  - job_name: "myapp"
    static_configs:
      - targets: ["172.28.60.10:3000"]

Agora teremos mais um target disponível em nosso Prometheus e novas métricas sendo coletadas:

Já podemos inclusive utilizar nossas métricas nos filtros do Prometheus (veremos detalhes de filtros no próximo tópico):

Estruturando uma Query em PromQL

PromQL é a linguagem que o Prometheus utiliza para executar suas queries no TSDB. Um ponto importante de salientar é que o Prometheus exibe suas métricas em UTC, então para exibição dos dados coletados devemos considerar esse aspecto. Caso necessário, no laboratório, utilize uma calculadora de timestamp online aqui!

Assim que compreendemos o que é o Prometheus e como aplicar no formato mais simples possível, conseguimos entender suas partes, e uma das mais importantes é o PromQL. Precisamos documentar alguns conceitos importantes para o entendimento mais aprofundado e como iremos estruturar nossas queries no futuro.

Tipos de dados

O Prometheus classifica alguns tipos de dados coletados e o que eles significam para termos uma maior calibragem no que filtrar, são eles:

  • Vector: retorna número inteiro.
Exemplo:

-> Chave: http_requests_total
-> Resultado: http_requests_total{method="POST", code "200"}
  • Instant Vector: retorna valores de uma série temporal num instante de tempo.
Exemplo:

-> Chave: http_requests_total (@31/12/2019 23:58:00)
-> Resultado: http_requests_total{method="POST", code "200"} -> 274 acessos
  • Range Vector: retorna valores de uma série temporal num range de tempo.
Exemplo:

-> Chave: http_requests_total[1m] (@31/12/2019 23:58:00)
-> Resultado: 
23:57:00 -> http_requests_total{method="POST", code "200"} -> 2 acessos
23:57:15 -> http_requests_total{method="POST", code "200"} -> 5 acessos
23:57:30 -> http_requests_total{method="POST", code "200"} -> 7 acessos
23:57:45 -> http_requests_total{method="POST", code "200"} -> 8 acessos
23:58:00 -> http_requests_total{method="POST", code "200"} -> 10 acessos

Filtros

O Prometheus nos permite efetuar uma gama grande de filtros, como vimos no passo anterior, filtramos dados de acesso ao nosso app baseado no código de retorno HTTP 200.

Acesse a documentação oficial para verificar outros exemplos.

Outro filtro bastante comum de ser utilizado, são as labels. O exemplo do código HTTP 200 é um exemplo de filtro por label. Vamos demonstrar agora alguns filtros utilizando o collectd e visualizando dados do nosso hardware.

Uma métrica que vamos utilizar do collectd é o load do servidor. Para isso temos a disposição as seguintes métricas coletadas:

Sendo:

  • shortterm: Load de 1m
  • midterm: Load de 5m
  • longterm: Load de 15m

Filtrando por um deles, podemos notar que é coerente com o atual load do servidor:

Outro exemplo interessante seria utilizarmos uma concatenação nos filtros, igual demonstrado no exemplo do tópico Tipos de dados.

Vamos coletar agora os dados de memória RAM do servidor, livre ou em uso. Para isso vamos utilizar expressões regulares:

Lembrando que os dados precisam ser calculados para o formado necessário antes de exibir para o usuário.

Análise geral

nonono

Critérios levados em consideração:

  1. Licenciamento: nonono

  2. Suporte: nonono

  3. Instalação: nonono

  4. Configuração: nonono

  5. Integração com o produto: nonono

  6. Performance: nonono

  7. Limitações/impedimentos/riscos: nonono

  8. Referência de soluções alternativas: nonono