Artigos e tutoriais
   RRDTOOLS - Criando gráficos
Autor: Patrick Brandão, patrickbrandao@gmail.com
Cópia autorizada somente quando preservar a referência ao autor.


Fontes:

Introdução

Ao final deste artigo você entenderá como o gráfico acima foi criado e como criar gráficos semelhantes ou mais bonitos!

Criado por Tobias Oetiker, o RRDTOOLs é um sistema de registro de dados estatísticos (números, velocidades, médias, máximas, mínimas, etc…) armazenados em séries de tempos.

O objetivo do sistema, composto de vários programas (comandos) que se relacionam é receber os dados e armazená-los em um banco de dados sequencial e linear, os RRDBs (Roudin Robin Databases) e usar tais dados para gerar gráficos em imagem (PNG principalmente).

Todo processo é executado em server-side. O cliente recebe apenas o gráfico pronto. Não há nada além dos mecanismos básicos (estrutura, armazenamento e plotagem).
A coleta dos dados que você deseja armazenar e plotar não é responsabilidade do RRDTOOLs, dessa forma ele não pode ser chamado de software de monitoramento, ele é uma ferramenta utilizada por softwares de monitoramento para armazenar e apresentar os dados coletados.

Sequência básica: RRDCREATE -> RRDUPDATE -> RRDGRAPH

Antes de iniciarmos os estudos do RRDTOOLs vou lhe ensinar alguns pre-requisitos necessários para operá-lo.


Data-Hora, Unix Timetamp e NTP

Por armazenar, atualizar e gerar os gráficos o RRDTOOLs sempre utiliza a referência do tempo e dos períodos em UNIX TIMESTAMP (TS), é importante entender o que é um TS.

TS é uma forma de armazenar data e hora em número inteiro.
O ponto inicial da contagem é 01/01/1970 00:00:00, quando o TS tem valor 0 (zero).

Assim:

  • 01/01/1970 00:00:00 UTC → 0
  • 01/01/1970 00:00:25 UTC → 25
  • 01/01/1970 00:01:00 UTC → 60
  • 01/01/1970 01:00:00 UTC → 3 600
  • 02/01/1970 00:00:00 UTC → 86 400
  • 31/12/1998 23:59:58 UTC → 915 148 798
  • 01/01/1999 00:00:01 UTC → 915 148 801
  • 16/09/2004 23:59:58 UTC → 1 095 379 198

Para obter o TS de uma data/hora, utilize o comando:

date "+%s" --date="2010-01-01 00:00:01"

Para obter o TS corrente, utilize: date "+%s"

date "+%s"

É preciso um cuidado especial ao trabalhar com timestamp devido o fuso horário.

A data/hora UTC irá gerar um TS universal, enquanto que ao simplesmente obter o TS pelo comando "date" puro irá retornar o TS com o timezone (TZ) em que seu servidor/computador se encontra configurado.

Um teste simples é observar o TS 1000000000 (um bilhão), que equivale a 01:46:40 de 9 de setembro de 2001, ou seja, 2001-09-09 01:46:40

Obtendo TS UTC:

date -u "+%s" --date="2001-09-09 01:46:40"
1000000000

Obtendo TS usando timezone local:

date "+%s" --date="2001-09-09 01:46:40"
1000010800

Pode parecer estranho que o número de segundos tenha aumentado. Na verdade, o comando acima informou a data "2001-09-09 01:46:40" no timezone local, logo, se estamos a -3 horas do horário meridiano de Greenwich e esta é a data local, em Greenwich (UTC) já se passaram 3 horas e lá ja são "2001-09-09 04:46:40", ou seja, 10800 segundos (60s * 60m * 3h) a mais.

O arquivo /etc/localtime determinar qual TZ seu sistema utiliza:

ls -lah /etc/localtime
lrwxrwxrwx 1 root root 37 Jan 31 21:27 /etc/localtime -> /usr/share/zoneinfo/America/Sao_Paulo

Você pode alterar o timezone local apontando o arquivo /etc/localtime para o arquivo de configuração de timezone desejado. Sistemas debian-like você pode usar os comandos abaixo:

timedatectl list-timezones
timedatectl set-timezone America/Sao_Paulo

Conferindo:

date +"%Z %z"
-03 -0300

Manter o relógio do sistema finamente sincronizado com a data/hora mundial pode ser difícil manualmente. O relógio quase sempre se atrasa ou se adianta por fatores como clock de cpu, bateria da BIOS, reset de BIOS, quedas de energia repentina, etc...

Use o NTP para fazer esse ajuste com precisão. Um servidor NTP mantem a data/hora sincronizada com um relógio nuclear, o protocolo NTP transmite esse timbre de data/hora com precisão de bilionésimo de segundos e compensa o atraso na transmissão calculando a latência entre seu sistema e o servidor NTP.

Exemplo de sincronia de data/hora usando NTP:

ntpdate a.ntp.br
1 Mar 00:17:44 ntpdate[149284]: adjust time server 200.160.0.8 offset -1.000062 sec

Observe que ao executar o ntpdate, meu relógio estava 1.000062 segundos a frente do presente, ele foi atrasado -1.000062 segundos para ficar sincronizado com o relógio nuclear do servidor NTP a.ntp.br.

Agora você já sabe o que é um TIMESTAMP.


RGBA - Cores e transparência

Ao lidar com cores na internet (navegadores, html, css) normalmente utilizamos a codificação RGB, ou seja, RED GREEN BLUE.

De uma forma mais intuitiva, é como se você tivesse 3 tubos de tinta, um tubo vermelho, um tubo verde e um azul, todos eles com 255 mililitros de tinta.

Para criar novas cores você precisa derramar um pouco de cada cor em um recipiente.
Se colocar 255 ml de vermelho e 255 ml de verde e nada do azul, você terá a cor amarela.

O nível dessas cores variando de 0 a 255 podem ser representadas em hexadecimal com 2 dígitos.

  • 255 decimal = FF hexadecimal
  • FF vermelho (100%)
  • FF verde (100%)
  • 00 azul (0%)

Resultaria na cor RGB: #FFFF00 (amarelo)

Algumas cores interessantes em RGB:
#FF0000 (vermelho)
#00FF00 (verde)
#0000FF (azul)
#00FFFF (ciano)
#FF00FF (magenta)

No entanto você tem uma cor opaca.
Podemos controlar a transparência (alpha) para ela se comportar como uma película, podendo variar entre 0% opaco (100% transparente) e 100% opaco (0% transparente), representado em hexadecimal com 2 dígitos, onde 00 significa 100% transparente e FF significa 100% opaco.

Temos agora uma definição de cor RGBA: RED GREEN BLUE ALPHA
A definição: #FF000080 seria:

  • FF = vermelho em 100% (decimal 255)
  • 00 = verde em 0% (decimal 0)
  • 00 = azul em 0% (decimal 0)
  • 80 = transparencia 50% (decimal 128)

Temos uma película vermelha semi-transparente!

Observe as 3 películas abaixo em RGBA, a saber: #FF000080, #00FF0080, #0000FF80

Agora as 3 películas em RGB: #FF0000, #00FF00, #0000FF

Os sites abaixo podem ser usados para construir cores fazendo essas misturas:

Estes sites em especial permitem obter a paleta de cores de uma imagem:



Instalação

Ubuntu / Debian

apt-get -y install rrdtool


Coletando estatísticas de PING ICMP

Para facilitar o entendimento, explicarei os comandos e argumentos enquanto construo um exemplo real de uso do RRDTOOLs.

Vou iniciar criando um gráfico de latência e perdas de pacotes de um ping para o www.registro.br (200.160.2.3)

Instale o FPING:

apt-get -y install fping

Efetuaremos 1 ping de 10 pacotes a cada 60 segundos, informaremos ao arquivo RRD a latência mínima, média e máxima do ping e o número de pacotes enviados e recebidos.

Exemplo de ping com 100% de respostas:

date -u "%s"; fping -C 10 -q -B1 -r1 -i1 200.160.2.3
1583020800
200.160.2.3 : 25.31 27.88 26.09 25.78 25.73 26.40 37.34 26.42 26.03 25.07

Converteremos os valores coletados para microsegundos (1s = 1000ms = 1000000us) para armazená-los como inteiros.

Resumo do ping 1:
  • Menor latência: 25.07 (25070 us)
  • Latência média: 27.20 (27200 us)
  • Maior latência: 37.34 (37340 us)
  • Pacotes enviados: 10
  • Pacotes recebidos: 10

Exemplo de ping com 80% de respostas:

date -u "%s"; fping -C 10 -q -B1 -r1 -i1 200.160.2.3
1583020860
200.160.2.3 : 25.23 27.37 - 26.87 25.31 26.05 25.99 25.69 - 25.32

Resumo do ping 2:
  • Menor latência: 25.23 (25230 us)
  • Latência média: 25.97 (25970 us)
  • Maior latência: 27.37 (27370 us)
  • Pacotes enviados: 10
  • Pacotes recebidos: 8

Coletamos o TS e o resumo do ping, se fossemos armazenar em um arquivo CSV teríamos 2 registros assim:

# ping-registro-br-ipv4.csv
1583020800:25070:27200:37340:10:10
1583020860:25230:25970:27370:10:8

Vou criar um script para realizar o teste e retornar o registro resumido do teste.

Todos os arquivos serão criados na pasta /var/www/htdocs.

# Arquivo /var/www/htdocs/_libping.sh
#!/bin/sh

# Converter valor em MS para US
ping_ms2us(){
    n="$1"
    v1=$(echo "$n" | cut -f1 -d.)
    v2=$(echo "${n}000" | cut -f2 -d. | cut -b1-3)
    echo "$v1$v2" | sed 's#^[0]*##g'
}

# Efetuar ping e retornar registro
ping_icmp(){
    dst="$1"
    tsping=$(date "+%s")
    pingstdout=$(fping -C 10 -q -B1 -r1 -i1 $dst 2>&1 | sed 's#\ :\ #@#g' | grep '@' | cut -f2 -d'@')
    latmin=0; latavg=0; latmax=0; sent=0; received=0
    total=0
    for mspart in $pingstdout; do
        sent=$(($sent+1))
        if [ "$mspart" = "-" ]; then
            # perdido
            continue
        else
            # recebido
            uspart=$(ping_ms2us "$mspart")
            received=$(($received+1))
            total=$(($total+$uspart))
            [ "$uspart" -gt "$latmax" ] && latmax="$uspart"
            [ "$latmin" = "0" -o "$uspart" -lt "$latmin" ] && latmin="$uspart"
        fi
        # calcular latencia media
        if [ "$received" -gt "0" -a "$total" -gt "0" ]; then
            latavg=$(($total/$received))
        fi
    done
    echo "$tsping:$latmin:$latavg:$latmax:$sent:$received"
}



Vamos testar, execute na linha de comando (ponto, espaço e caminho do arquivo com as funções):

 . /var/www/htdocs/_libping.sh
ping_icmp 200.160.2.3
1583036965:24480:25478:26430:10:10

Já conseguimos gerar registros dos testes. Vamos enfim começar a estudar o RRDTOOLs.



Criando um arquivo RRDB

A natureza RR dos dados limita a ordem das coletas, que devem obedecer uma sequência temporal linear, assim, cada dado coletado é armazenado após o último dado armazenado, mantendo sempre a flecha do tempo avançando e apontando para o futuro. Todo registro inserido é associado ao seu TIMESTAMP (TS).

Não é possível armazenar dados no passado. Aqui reside um problema que você deve evitar: NUNCA mude o relógio do servidor para o futuro. Se estamos em 2020 e você armazena um dado com timestamp de 2040, seu banco de dados RRD ficará impossibilitado de armazenar novas entradas até que você chegue de fato em 2040, ou mantenha o relógio propositalmente no futuro, o que seria incoerente com a plotagem dos dados nos gráficos. O computador deve manter seu relógio sempre sincronizado com um servidor NTP.

Crie um banco de dadas RRD (RRDB), vou explicar todos os argumentos mais a frente.

Execute:

# Arquivo /var/www/htdocs/create-rrdb-ping-registrobr-ipv4.sh

# Entrar na pasta de trabalho:
cd /var/www/htdocs/

# Criar banco de dados RRD:
rrdtool create \
    /var/www/htdocs/_ping-registrobr-ipv4.rrd \
    --step 60 \
    --start 1000000000                 \
    DS:latmin:GAUGE:600:0:30000000     \
    DS:latavg:GAUGE:600:0:30000000     \
    DS:latmax:GAUGE:600:0:30000000     \
    DS:sent:GAUGE:600:0:100            \
    DS:received:GAUGE:600:0:100        \
    \
    RRA:AVERAGE:0.5:1:576              \
    RRA:AVERAGE:0.5:6:672              \
    RRA:AVERAGE:0.5:24:732             \
    RRA:AVERAGE:0.5:144:1460           \
    \
    RRA:MIN:0.5:1:576                  \
    RRA:MIN:0.5:6:672                  \
    RRA:MIN:0.5:24:732                 \
    RRA:MIN:0.5:144:1460               \
    \
    RRA:MAX:0.5:1:576                  \
    RRA:MAX:0.5:6:672                  \
    RRA:MAX:0.5:24:732                 \
    RRA:MAX:0.5:144:1460

Usamos o comando rrdtool para a função create, que obviamente cria um arquivo RRD para armazenamento de dados:

/var/www/htdocs/_ping-registrobr-ipv4.rrd

O RRDB é criado vazio, o TS atual é usado como ponto é partida.

Você pode informar o TS inicial desejado com o argumento opcional --start STARTTS, exemplo:

--start now: TS do momento de criação do arquivo.
--start now-10: Padrão. TS de 10 segundos atras.
--start now-2h: Cria o ponto inicial 2 horas no passado.
--start 1000000000: Cria o ponto inicial em 2001-09-09 01:46:40 UTC.
--start 2000000000: Cria o ponto inicial em 2033-05-18 03:33:20 UTC.

Nenhum dado poderá ser inserido em um TS inferior ao ponto de criação.

--step 60: Informa que são esperados novos dados a cada 60 segundos. Você pode entrar com dados com intervalos menores ou maiores, mas os valores armazenados irão respeitar o intervalo de 60 segundos ao consolidar o valor final. Quando o argumento step é omitido assume-se o valor 300 (5 minutos).
DS:latmin:GAUGE:600:0:30000000: DS: DS é um datasource, também chamado de INPUT, fonte de dados.
latmin: determinar o nome do datasource (nome da coluna), nome da variável que faz referência a série de dados desejada;
GAUGE: maneira como os dados numéricos da série serão tratados no armazenamento, explicarei os tipos de séries posteriormente.
600: intervalo de tempo em que os dados ainda serão retornados baseado nos valores anteriores para os casos em que faltam dados no período.
Se você informou que irá alimentar o arquivo a cada 60 segundos e mesmo assim ficar 600 segundos (10 minutos) sem alimentar o arquivo, esse período será auto-completado com o último valor consolidado, se passar de 600 segundos os dados não serão consolidados e seu gráfico ficará cortado (dado retornado como UNKNOW);
0: determina o valor mínimo aceitável para a série (zero), nosso propósito aqui é ter a latência mínima e é impossível ter latências negativas, para valores mínimos desconhecidos use a letra U (unknow);
30000000: determina o número máximo aceitável para a série, 30000000 (três milhões de microsegundos, 3 segundos), pois não efetuaremos pings com mais de 3 segundos de timeout, para valores máximos desconhecidos use a letra U (unknow).

RRA:AVERAGE:0.5:1:576: RRA: Um RRA (Round Robin Archive) armazena dados consolidados baseado num número de coletas (input em um datasource). Ao declarar um RRA você estará criando uma tabela de valores para cada datasource (DS).
Se você criar 5 DSs e 5 RRAs, 25 tabelas serão criadas.
AVERAGE: o valor consolidado será baseado na média dos valores, as opções aqui são MIN, AVERAGE e MAX.
0.5: xff, consolida 50% dos valores coletados para obter o valor utilizável, varia entre 0 (0%) e 1 (100%) baseado em valores decimais.
A 50%, se você está coletando dados para gerar um gráfico de 6 meses, precisará de 3 meses de dados coletados para ter uma média utilizável nesse RRA.
GAUGE: maneira como os dados numéricos da série serão tratados no armazenamento, explicarei os tipos de séries posteriormente.
1: steps, determina o número de entradas necessárias para consolidar o valor a ser armazenado.
O valor 1 aqui elimina esse efeito de média e faz com que o valor coletado seja de fato armazenado.
Se aqui você informar o valor 6 isso significará que a cada 6 entradas (rrdtool update) no RRDB apenas 1 valor será consolidado e armazenado no RRA: a média (AVERAGE) dos 6 valores.
576: rows, determina o número de registros a serem armazenados com o tipo de cáuculo determinado no steps.
Não estamos aqui limitando a coleta a apenas 576 registros!
Estamos dizendo que ao completar 576 registros teremos uma tabela fechada, e outra será iniciada.
O arquivo RRD será criado com 576 registros vazios (NaN) nos RRAs (1 para cada DS).

Exemplos de RRA para melhor compreensão:
RRA:AVERAGE:0.5:6:672: A cada 6 atualizações um novo registro será armazenado com a média desses 6 valores.
Como estamos criando um banco RRD para atualizações a cada 60 segundos (--steps),
esse RRA receberá um registro a cada 6 minutos.
Quando completar 672 registros (672 x 6m = 4032 = 67 horas = 2 dias e 19 horas) a base é consolidada e uma nova coletânea de registros será criada.
Se as últimas 6 coletas informadas forem: 20 + 25 + 22 + 24 + 30 + 29 = 150 => 150/6 = média 25 (valor consolidado).

RRA:AVERAGE:0.5:24:732: A cada 24 atualizações um novo registro será armazenado com a média desses 24 valores.
Esse RRA receberá um registro a cada 24 minutos.
Quando completar 732 registros (732 x 24m = 17568 = 292 horas = 12 dias e 4 horas) a base é consolidada.

RRA:MIN:0.5:6:672: A cada 6 atualizações um novo registro será armazenado com o menor dos 6 valores.
Esse RRA receberá um registro a cada 6 minutos.
Quando completar 672 registros a base é consolidada.
Se as últimas 6 coletas informadas forem: 20 + 25 + 22 + 24 + 30 + 29 = MIN(*) = menor valor é 20 (valor consolidado).

RRA:MAX:0.5:144:1460: A cada 144 atualizações um novo registro será armazenado com o maior dos 144 valores.
Esse RRA receberá um registro a cada 2 horas e 24 minutos.
Quando completar 1460 registros (1460 x 2:24 = 146 dias) a base é consolidada.
Se as últimas 144 coletas retornarem números entre 20 e 49, o maior valor é 49 (valor consolidado).

Tipos de fontes de dados (datasources)

O tipo do dado armazenado em um datasource serve para ajudar o RRD a calcular alguns valores prontos para plotagem no gráfico.
Se você deseja por exemplo, coletar dados de temperatura ou de uma latência de resposta (como o ping), os valores informados ao RRD podem aumentar ou diminuir, e devem ser plotados literalmente no gráfico.

Se você desejar medir o número de bytes enviados ou recebidos de uma interface para plotar um gráfico de consumo de banda, o RRDTOOL precisará calcular essa velocidade dividindo a diferença entre os bytes armazenados pela diferença de tempo em que eles foram coletados.

Os tipos são:

GAUGE Números absolutos, usado para valores como temperatura, latência, perda de pacotes em um ping, luminosidade, densidade, nível de água em um tanque, nível pluviométrico, RPM. Em suma, valor exato do que foi aferido, e o valor pode livremente variar para cima e para baixo.

COUNTER Números incrementais, usados para determinar fluxos. O valor realmente armazenado é a velocidade incremental do fluxo. Esse tipo de datasource é usado para litros de água que passam em um cano para plotar litros por segundo, número de bytes enviados em uma interface de rede para obter os kbits por segundo.
Considera-se que o valor sempre aumenta, mas na eventualidade de um overflow ou reset no update o RRDTOOL tenta calcular o limite até o overflow ou números acima de zero para acertar (adivinhar) o valor do fluxo.

DCOUNTER Semelhante ao COUNTER mas para valores de ponto flutuante como sensores de precisão.
O valor armazenado segue baseado em incremento ou decremento. Se valores que sempre são incrementados passam a ser decrementados o fluxo é resetado e valor do fluxo será obtido após o segundo update a medida em que vai reduzindo.

DERIVE Semelhante ao COUNTER mas sem verificações de overflow ou reset, usado por exemplo para medir o fluxo de pessoas que entram ou saem por uma porta de uma loja, ou de litros de água que passam por um cano cujo fluxo pode ser invertido. É muito utilizado também como alternativa ao COUNTER no cálculo de velocidade de bytes enviados ou recebidos em aplicações de rede (dados provenientes da interface ou coletados via SNMP).

DDERIVE Semelhante ao DERIVE/COUNTER mas para valores de ponto flutuante mas sem verificações de overflow ou reset, usado por exemplo para medir o fluxo de pessoas que entram ou saem por uma porta de uma loja, ou de litros de água que passam por um cano cujo fluxo pode ser invertido.

ABSOLUTE Valores que são reiniciados a cada leitura.
Com exemplo fica mais fácil de entender: uma certa aplicação informa o número de objetos mas reinicia esses números sempre que você realiza a consulta. Dessa forma, se a primeira leitura retornar 10, e a segunda retornar 20, significa que 30 objetos foram contados pela aplicação (quando você consultou a primeira vez ele retornou 10 e zerou o contador, então quando voce consultou pela segunda vez o contador já estava em 20, totalizando 20 novos incrementos, e 30 incrementos desde o inicio da base de dados).

COMPUTE Usado em um datasource que não recebe os dados durante o update mas calcula o valor baseado em dados já inseridos ou entrante. Um exemplo prático seria o cálculo do jitter de um ping, onde o jitter seria computador entre a latência mínima e a latência máxima, as médias seriam armazenadas já calculadas (outra alternativa é calcular em tempo de plotagem, mas isso consome mais recursos computacionais).

Populando e analisando base de dados

Vamos inserir dois registros, sendo o segundo com um TS menor:

rrdtool update _ping-registrobr-ipv4.rrd 1000007777:25030:26584:30900:10:10
rrdtool update _ping-registrobr-ipv4.rrd 1000008888:23120:24124:31350:10:9
rrdtool update _ping-registrobr-ipv4.rrd 1000005555:25030:26584:30900:10:10
ERROR: _ping-registrobr-ipv4.rrd: illegal attempt to update using time 1000005555 when last update time is 1000007777 (minimum one second step)

Observe também que o intervalo mínimo entre cada registro inserido é de 1 segundo.

Para fazer uma analise do banco de dados RRD use o comando:

rrdtool dump _ping-registrobr-ipv4.rrd

A quantidade de dados retornados em XML no comando acima é relevante!
A medida que você vai preenchendo (rrdtool update) os valores NaN (Not a Number) vão sendo substituídos pelos valores consolidados.

Para extrair os dados consolidados (ignora NaN) use o comando:

rrdtool fetch _ping-registrobr-ipv4.rrd AVERAGE

Para descobrir o timestamp do primeiro registro, use:

rrdtool first _ping-registrobr-ipv4.rrd
999974340

Para descobrir o timestamp do último registro, use:

rrdtool last _ping-registrobr-ipv4.rrd
1000008888

Para recuperar o último registro inserido, use:

rrdtool lastupdate _ping-registrobr-ipv4.rrd
 latmin latavg latmax sent received

1000008888: 23120 24124 31350 10 9

Para exibir as informações de um arquivo RRD, use:

rrdtool info _ping-registrobr-ipv4.rrd


Preenchendo para desenhar

Sem dados no banco de dados não é possível desenhar nada tela, antes de iniciarmos os capítulo de construção de gráficos vamos preencher nosso RRD com dados gerados aleatoriamente.

Vamos preencher 1 dia, iniciando em 2020-01-01 00:00:00 UTC (timestamp 1577836800) e gerando um registro por minuto até 2020-01-01 23:59:00 UTC (timestamp 1577923140).

Simularemos:

  • 10% de perda de pacotes (enviados 10 recebidos 9) apartir de 2020-01-01 12:00:00 UTC (timestamp 1577880000);
  • 20% de perda de pacotes (enviados 10 recebidos 8) apartir de 2020-01-01 13:00:00 UTC (timestamp 1577883600);
  • 30% de perda de pacotes (enviados 10 recebidos 7) apartir de 2020-01-01 14:00:00 UTC (timestamp 1577887200);
  • 40% de perda de pacotes (enviados 10 recebidos 7) apartir de 2020-01-01 15:00:00 UTC (timestamp 1577890800);
  • 0.1 ms de latência variando a cada 1 hora.

date -u "+%s" --date="2020-01-01 00:00:00"
1577836800

date -u "+%s" --date="2020-01-01 23:59:00"
1577923140

Gerando dados:

# Arquivo /var/www/htdocs/generate-fake-data-ping-registrobr-ipv4.sh
tstart=1577836800
tend=1577923140

# Faixa de us para gerar latencia minima
minbaselat=18000
mintoplat=20000

# Faixa de us para gerar latencia media
avgbaselat=20000
avgtoplat=28000

# Faixa de us para gerar latencia maxima
maxbaselat=28000
maxtoplat=30000

# Pacotes enviados
sent=10

# Pacotes recebidos
received=10

(
    timestamp=$tstart
    for day in 1; do
        [ "$day" -le 9 ] && day="0$day"
        xdate="2020-01-$day"
        echo "#> Day...: $xdate"

        for hour in $(seq 0 1 23); do
            [ "$hour" -le 9 ] && hour="0$hour"
            xdate="2020-01-$day $hour"

            received=10
            [ "$hour" = 12 ] && received=9
            [ "$hour" = 13 ] && received=8
            [ "$hour" = 14 ] && received=7


            echo "# - Hour.: $xdate"

            for min in $(seq 0 1 59); do
                [ "$min" -le 9 ] && min="0$min"
                xdate="2020-01-$day $hour:$min:00"

                # gerar latencia minima, media e maxima aleatoriamente
                # dentro das faixas de operacao base/top
                latmin=$(shuf -i$minbaselat-$mintoplat -n1)
                latavg=$(shuf -i$avgbaselat-$avgtoplat -n1)
                latmax=$(shuf -i$maxbaselat-$maxtoplat -n1)

                pingreg="$timestamp:$latmin:$latavg:$latmax:$sent:$received"

                echo "#        : $xdate - $timestamp == $pingreg"
                rrdupdate _ping-registrobr-ipv4.rrd "$pingreg"

                timestamp=$(($timestamp+60))
            done

            # Aumentar a faixa de latencias em 1ms (1000us)
            minbaselat=$(($minbaselat-100)); mintoplat=$(($mintoplat+100))
            avgbaselat=$(($avgbaselat-100)); avgtoplat=$(($avgtoplat+100))
            maxbaselat=$(($maxbaselat-100)); maxtoplat=$(($maxtoplat+100))

        done
    done
)

Eu preenchi com dados aleatórios para fins de teste rápido dos conceitos. Você pode gerar dados reais criando o ping para o registro.br:

# Arquivo /var/www/htdocs/monit-ping-registrobr-ipv4.sh
#!/bin/sh

# Incluir funcoes de ping
 . /var/www/htdocs/_libping.sh

rrdb="/var/www/htdocs/_ping-registrobr-ipv4.rrd"
ip="200.160.2.3"

echo "#> Ping $ip"
pingreg=$(ping_icmp $ip)

echo "#> Reg: $pingreg"
rrdupdate "$rrdb" "$pingreg"

Agora coloque o script /var/www/htdocs/monit-ping-registrobr-ipv4.sh no crontab assim:

crontab -e

*/1   *   *   *   *   /var/www/htdocs/monit-ping-registrobr-ipv4.sh 1> /dev/null

Antes de falarmos de plotagem, há um detalhe curioso a mencionar: nós geramos 3 tipos de RRA (AVERAGE, MIN, MAX), e coletamos 3 valores de latência por entrada (latência mínima, latência média e latência máxima).
Isso disponibilizou uma matriz de dados, a saber:

  • Média da latência mínima (AVERAGE latmin);
  • Média da latência média (AVERAGE latavg);
  • Média da latência máxima (AVERAGE latmax);
  • Mínima da latência mínima (MIN latmin);
  • Mínima da latência média (MIN latavg);
  • Mínima da latência máxima (MIN latmax);
  • Máxima da latência mínima (MAX latmin);
  • Máxima da latência média (MAX latavg);
  • Máxima da latência máxima (MAX latmax);

Sabendo que o jitter é a variação de latência entre os pings, temos:

  • Jitter máximo entre MIN(latmin) e MAX(latmax)
  • Jitter discreto entre AVG(latmin) e AVG(latmax)
  • Jitter mínimo entre MAX(latmin) e MIN(latmax)

Sem mencionar todas as demais variações que podemos obter, incluindo dimensões como a perda de pacotes.

Como são muitas variáveis, vamos nos ater apenas às mais básicas para começar:

  • RRA AVERAGE latmin
  • RRA AVERAGE latavg
  • RRA AVERAGE latmax


Desenhando gráficos

Crie o script abaixe e execute-o:

# Arquivo /var/www/htdocs/graph-ping-registrobr-001.sh
#!/bin/sh

rrdb="/var/www/htdocs/_ping-registrobr-ipv4.rrd"

echo "# $rrdb > Gerando grafico 01:"

rrdtool graph "/var/www/htdocs/image-registrobr-ipv4-001.png" \
      --start 1577836800 --end 1577923140 --step=60  \
      --imgformat PNG                                \
      --vertical-label Latencia                      \
      --title "Latencia IPv4 Registro.br"            \
      --width=800 --height=400                       \
      \
      DEF:dsavglatmin=$rrdb:latmin:AVERAGE           \
      DEF:dsavglatavg=$rrdb:latavg:AVERAGE           \
      DEF:dsavglatmax=$rrdb:latmax:AVERAGE           \
      DEF:dsavgsent=$rrdb:sent:AVERAGE               \
      DEF:dsavgreceived=$rrdb:received:AVERAGE       \
      \
      CDEF:avglatminms=dsavglatmin,1000,/            \
      CDEF:avglatavgms=dsavglatavg,1000,/            \
      CDEF:avglatmaxms=dsavglatmax,1000,/            \
      \
      LINE1:avglatminms#00FF00                       \
      LINE1:avglatavgms#0000FF                       \
      LINE1:avglatmaxms#FF0000

Explicando o comando acima:
rrdtool graph: Aciona a função do rrdtool para geração de imagem. O argumento seguinte deve ser o nome da imagem a ser criada.

Gerar imagem PNG em arquivo:
rrdtool graph /var/www/htdocs/image-registrobr-ipv4-001.png ...

É comum em aplicações web gerar a imagem direto para buffer de saída, nesse caso substitua o nome da imagem por um hífen: -

Gerar imagem PNG na STDOUT (buffer de saida):
rrdtool graph - ...

--start $tstart --end $tend --step $tstep: Os argumentos start e end informam ao rrdtools o período a ser extraido do RRDB nas tabelas RRA para plotagem do gráfico.
Estes argumentos aceitam valores absolutos inteiros em formato TIMESTAMP. Aceitam também formato relativo, veja alguns exemplos:
--start -12h --stop now: TS atual menos 12 horas até o TS atual, o periodo abrente as ultimas 12 horas.
--start -8h --stop now: Últimas 8 horas.
--start -1h --stop now: Última hora.
--start -48h --stop -24h: Volta 48 horas e avança 24 horas a frente.
--start -7day --stop -1h: Últimos 7 dias.
--start -365day --stop -8h: Último ano.

O argumento --step informa o intervalo de tempo em segundos para cada valor armazenado num RRA.
Se você informar um valor de step de 60 e não houver um RRA com essa base de intervalo, um RRA com step maior será selecionado, e assim vai até encontrar algum RRA para fornecer os valores consolidados.

Se você deseja plotar gráficos de um período de 24 horas, deve usar um step entre 60 e 600 segundos. Ao informar um step de 3600 segundos (1 hora) para um gráfico de 24 horas de intervalo você terá um gráfico grosseiro, com apenas 24 colunas representados em degraus largos.

Já para gráficos de intervalos de 1 ano, talvez não seja interessante, nem eficiente, usar step de 60 segundos. O gráfico terá tantas montanhas e vales comprimidos que em muitos casos fica difícil enxergar a média. A medida que você aumenta o step alguns padrões ficam mais claros a longo prazo.

--imgformat PNG: Formato da imagem a ser produzida. O padrão é PNG e pode ser omitido. Os seguintes formatos estão disponíveis: PNG, SVG, EPS, PDF, XML, XMLENUM, JSON, JSONTIME, CSV, TSV, SSV. Sem maiúsculo.

--vertical-label "Latencia": Texto exibido à esquerda do gráfico na vertical.

--title "Latencia IPv4 Registro.br": Título superior central, exebido acima do gráfico.

--width 800 --height 400: Determina a largura e altura, em pixels, da área de plotagem central do gráfico.
É importante saber que não se trata do tamanho da imagem final e sim SOMENTE da área plotagem. A altura final contará com os acréscimos acima do gráfico (--title) e abaixo do gráfico (atributos que veremos mais tarde como legenda e whatermark).

DEF:dsavglatmin=$rrdb:latmin:AVERAGE: Uma DEF é um array de valores extraidos do arquivo RRD baseado em uma ou mais RRA do mesmo tipo (AVERAGE, MIN ou MAX).
Aqui criamos uma DEF de latência mínima (DS latmin) baseada nos RRAs do tipo AVERAGE.
Você precisará informar no DEF o nome da variável a ser criada para coletar esses dados.
Aqui defini como dsavglatmin, você pode usar o nome que desejar.
Você pode coletar datasources-rra de quantas bases de dados desejar, uma para cada variável DEF.

CDEF:avglatminms=dsavglatmin,1000,/: Cria uma variável baseada em expressão RPN, que explicarei mais tarde. Como armazenamos os dados em microsegundos, vamos dividí-los por 1000 para obter o valor em milisegundos. Para rodar uma expressão RPN as variáveis citadas (DEF ou CDCDEFEF) devem ser declaradas em argumentos anteriores.

LINE1:avglatminms#00FF00: Aqui começa a diversão, o argumento LINE realiza o desenho do array de variáveis na área de plotagem.
A palavra LINE deve ser seguida de um número que define a espessura da linha em pixels, LINE1 desenha a linha com 1 pixel, LINE2 desenha a linha com 2 pixels.
O nome da variável deve ser declarada previamente em DEF, CDEF ou VDEF.
Apos o nome da variável você deve especificar a cor da linha em formato RGB (3 valores: red-green-blue) ou RGBA (4 valores: red-green-blue-alpha).


Exemplo alterando alguns argumentos:

# Arquivo /var/www/htdocs/graph-ping-registrobr-002.sh
#!/bin/sh

rrdb="/var/www/htdocs/_ping-registrobr-ipv4.rrd"

echo "# $rrdb > Gerando grafico 02:"

rrdtool graph "/var/www/htdocs/image-registrobr-ipv4-002.png" \
      --start 1577836800 --end 1577923140 --step=600  \
      --imgformat PNG                                \
      --vertical-label Latencia                      \
      --title "Latencia IPv4 Registro.br"            \
      --width=800 --height=400                       \
      \
      DEF:dsavglatmin=$rrdb:latmin:AVERAGE           \
      DEF:dsavglatavg=$rrdb:latavg:AVERAGE           \
      DEF:dsavglatmax=$rrdb:latmax:AVERAGE           \
      DEF:dsavgsent=$rrdb:sent:AVERAGE               \
      DEF:dsavgreceived=$rrdb:received:AVERAGE       \
      \
      CDEF:avglatminms=dsavglatmin,1000,/            \
      CDEF:avglatavgms=dsavglatavg,1000,/            \
      CDEF:avglatmaxms=dsavglatmax,1000,/            \
      \
      LINE2:avglatminms#00FF00                       \
      LINE1:avglatavgms#0000FF                       \
      LINE3:avglatmaxms#FF0000


--step 600: Diminuímos o número de valores coletados ao pegar 1 valor a cada 600 segundos (10 minutos).

LINE2:avglatminms#00FF00: Representar média da latência mínima com linha de 2 pixels de largura na cor verde.
LINE1:avglatavgms#0000FF: Representar média da latência média com linha de 1 pixels de largura na cor azul.
LINE3:avglatmaxms#FF0000: Representar média da latência máxima com linha de 3 pixels de largura na cor vermelha.

Adicionando legenda

# Arquivo /var/www/htdocs/graph-ping-registrobr-003.sh
#!/bin/sh

rrdb="/var/www/htdocs/_ping-registrobr-ipv4.rrd"

echo "# $rrdb > Gerando grafico 03:"

rrdtool graph "/var/www/htdocs/image-registrobr-ipv4-003.png" \
      --start 1577836800 --end 1577923140 --step=600  \
      --imgformat PNG                                \
      --vertical-label Latencia                      \
      --title "Latencia IPv4 Registro.br"            \
      --width=800 --height=400                       \
      \
      DEF:dsavglatmin=$rrdb:latmin:AVERAGE           \
      DEF:dsavglatavg=$rrdb:latavg:AVERAGE           \
      DEF:dsavglatmax=$rrdb:latmax:AVERAGE           \
      DEF:dsavgsent=$rrdb:sent:AVERAGE               \
      DEF:dsavgreceived=$rrdb:received:AVERAGE       \
      \
      CDEF:avglatminms=dsavglatmin,1000,/            \
      CDEF:avglatavgms=dsavglatavg,1000,/            \
      CDEF:avglatmaxms=dsavglatmax,1000,/            \
      \
      LINE1:avglatminms#00FF00:" Lat. min. \n"       \
      LINE1:avglatavgms#0000FF:" Lat. avg. \n"       \
      LINE1:avglatmaxms#FF0000:" Lat. max. \n"


LINE1:avglatminms#00FF00" Lat. min. "
LINE1:avglatavgms#0000FF" Lat. avg. "
LINE1:avglatmaxms#FF0000" Lat. max. "
Informar uma string após a definição de cor cria uma legenda de cor da linha.


Personalizando o gráfico
Novos argumentos
# Arquivo /var/www/htdocs/graph-ping-registrobr-004.sh
#!/bin/sh

rrdb="/var/www/htdocs/_ping-registrobr-ipv4.rrd"

echo "# $rrdb > Gerando grafico 04:"

rrdtool graph "/var/www/htdocs/image-registrobr-ipv4-004.png" \
      --start 1577836800 --end 1577923140 --step=60  \
      --imgformat PNG                                \
      --vertical-label Latencia                      \
      --title "Latencia IPv4 Registro.br"            \
      --width=800 --height=400                       \
      --lower-limit=0                                \
      --upper-limit=100                              \
      --slope-mode                                   \
      --font-render-mode mono                        \
      --watermark "Patrick Brandao"                  \
      \
      DEF:dsavglatmin=$rrdb:latmin:AVERAGE           \
      DEF:dsavglatavg=$rrdb:latavg:AVERAGE           \
      DEF:dsavglatmax=$rrdb:latmax:AVERAGE           \
      DEF:dsavgsent=$rrdb:sent:AVERAGE               \
      DEF:dsavgreceived=$rrdb:received:AVERAGE       \
      \
      CDEF:avglatminms=dsavglatmin,1000,/            \
      CDEF:avglatavgms=dsavglatavg,1000,/            \
      CDEF:avglatmaxms=dsavglatmax,1000,/            \
      \
      LINE1:avglatminms#00FF00:" Lat. min. \n"       \
      LINE1:avglatavgms#0000FF:" Lat. avg. \n"       \
      LINE1:avglatmaxms#FF0000:" Lat. max. \n"


--lower-limit=0 e --upper-limit=100 Observe no gráfico anterior que a escala de valores começam em 10 e não em zero no eixo Y, tal comportamento permite que o gráfico use melhor o espaço para representar os valores entre o mínimo e máximo. Você pode forçar o limite inferior a escala com a opção --lower-limit

Use explicitamente os argumentos --alt-autoscale-min e --alt-autoscale-max se desejar adequar o eixo Y e o espaço de desenho ao conteúdo.
Usando o argumento --alt-autoscale para incluir ambos.

Se os valores a serem desenhados ultrapassam os limites superiores e inferiores, os argumentos --alt-autoscale-min e --alt-autoscale-max não serão obedecidos.
Para forçar obediência informe o argumento --rigid, cortando os valores fora dos limites.

--slope-mode Por padrão o rrdtool desenha a linha dos valores usando ângulos de 90 graus, guiados pelo grid. Ativando slope-mode a transição de valores durante a plotagem é amenizado dando um tom mais elegante aos gráficos.
--font-render-mode mono Define o estilo de renderização e espaçamento das fontes utilizadas nos textos do gráfico. Opções: normal, light e mono.
A opção normal tende a deixar a letra mais forte porém menos nítida.
A opção light deixa as letras mais finas e as vezes menos visíveis.
O opção mono, que iremos utilizar daqui pra frente, tenta deixar uma largura fixa em cada caractere.

--watermark "Patrick Brandao" Cria uma assinatura no limite inferior do gráfico, fora da região de plotagem.

Personalizando GRID

O grid é formado por linhas pontilhadas horizontais e verticais alinhadas com os valores dos eixos Y e X. O argumento --grid-dash ON:OFF informa como os pixels do grid serão desenhados alternando entre número de pixels desenhados e número de pixels não desenhados.

Padrão implicito --grid-dash 1:1

Exemplos:

--grid-dash 1:4 Deixará o grid quase invisível, desenhando 1 pixel on (desenhado) para cada 4 pixels off (não desenhados).
--grid-dash 1:0 Desenha a linha de grid continua.

Testando:

--grid-dash 1:0 --color MGRID#00aa00ff --color GRID#333333ff
# Arquivo /var/www/htdocs/graph-ping-registrobr-005.sh
#!/bin/sh

rrdb="/var/www/htdocs/_ping-registrobr-ipv4.rrd"

echo "# $rrdb > Gerando grafico 05:"

rrdtool graph "/var/www/htdocs/image-registrobr-ipv4-005.png" \
      --start 1577836800 --end 1577923140 --step=60  \
      --imgformat PNG                                \
      --vertical-label Latencia                      \
      --title "Latencia IPv4 Registro.br"            \
      --width=800 --height=400                       \
      --lower-limit=0  --slope-mode                  \
      --font-render-mode mono                        \
      --watermark "Patrick Brandao"                  \
      \
      --grid-dash 1:0                                \
      --color MGRID#00aa00ff --color GRID#333333ff   \
      \
      DEF:dsavglatmin=$rrdb:latmin:AVERAGE           \
      DEF:dsavglatavg=$rrdb:latavg:AVERAGE           \
      DEF:dsavglatmax=$rrdb:latmax:AVERAGE           \
      DEF:dsavgsent=$rrdb:sent:AVERAGE               \
      DEF:dsavgreceived=$rrdb:received:AVERAGE       \
      \
      CDEF:avglatminms=dsavglatmin,1000,/            \
      CDEF:avglatavgms=dsavglatavg,1000,/            \
      CDEF:avglatmaxms=dsavglatmax,1000,/            \
      \
      LINE1:avglatminms#00FF00:" Lat. min. \n"       \
      LINE1:avglatavgms#0000FF:" Lat. avg. \n"       \
      LINE1:avglatmaxms#FF0000:" Lat. max. \n"

Testando:

--grid-dash 1:0 --color MGRID#00aa00ff --color GRID#ffffff00
# Arquivo /var/www/htdocs/graph-ping-registrobr-006.sh
#!/bin/sh

rrdb="/var/www/htdocs/_ping-registrobr-ipv4.rrd"

echo "# $rrdb > Gerando grafico 06:"

rrdtool graph "/var/www/htdocs/image-registrobr-ipv4-006.png" \
      --start 1577836800 --end 1577923140 --step=60  \
      --imgformat PNG                                \
      --vertical-label Latencia                      \
      --title "Latencia IPv4 Registro.br"            \
      --width=800 --height=400                       \
      --lower-limit=0  --slope-mode                  \
      --font-render-mode mono                        \
      --watermark "Patrick Brandao"                  \
      \
      --grid-dash 1:0                                \
      --color MGRID#00aa00ff --color GRID#ffffff00   \
      \
      DEF:dsavglatmin=$rrdb:latmin:AVERAGE           \
      DEF:dsavglatavg=$rrdb:latavg:AVERAGE           \
      DEF:dsavglatmax=$rrdb:latmax:AVERAGE           \
      DEF:dsavgsent=$rrdb:sent:AVERAGE               \
      DEF:dsavgreceived=$rrdb:received:AVERAGE       \
      \
      CDEF:avglatminms=dsavglatmin,1000,/            \
      CDEF:avglatavgms=dsavglatavg,1000,/            \
      CDEF:avglatmaxms=dsavglatmax,1000,/            \
      \
      LINE1:avglatminms#00FF00:" Lat. min. \n"       \
      LINE1:avglatavgms#0000FF:" Lat. avg. \n"       \
      LINE1:avglatmaxms#FF0000:" Lat. max. \n"

Removendo grid:

--grid-dash 0:1
# Arquivo /var/www/htdocs/graph-ping-registrobr-007.sh
#!/bin/sh

rrdb="/var/www/htdocs/_ping-registrobr-ipv4.rrd"

echo "# $rrdb > Gerando grafico 07:"

rrdtool graph "/var/www/htdocs/image-registrobr-ipv4-007.png" \
      --start 1577836800 --end 1577923140 --step=60  \
      --imgformat PNG                                \
      --vertical-label Latencia                      \
      --title "Latencia IPv4 Registro.br"            \
      --width=800 --height=400                       \
      --lower-limit=0  --slope-mode                  \
      --font-render-mode mono                        \
      --watermark "Patrick Brandao"                  \
      \
      --grid-dash 0:1                                \
      \
      DEF:dsavglatmin=$rrdb:latmin:AVERAGE           \
      DEF:dsavglatavg=$rrdb:latavg:AVERAGE           \
      DEF:dsavglatmax=$rrdb:latmax:AVERAGE           \
      DEF:dsavgsent=$rrdb:sent:AVERAGE               \
      DEF:dsavgreceived=$rrdb:received:AVERAGE       \
      \
      CDEF:avglatminms=dsavglatmin,1000,/            \
      CDEF:avglatavgms=dsavglatavg,1000,/            \
      CDEF:avglatmaxms=dsavglatmax,1000,/            \
      \
      LINE1:avglatminms#00FF00:" Lat. min. \n"       \
      LINE1:avglatavgms#0000FF:" Lat. avg. \n"       \
      LINE1:avglatmaxms#FF0000:" Lat. max. \n"

Removendo grid do eixo X: deixa somente as linhas de valores do eixo Y

--x-grid none
# Arquivo /var/www/htdocs/graph-ping-registrobr-008.sh
#!/bin/sh

rrdb="/var/www/htdocs/_ping-registrobr-ipv4.rrd"

echo "# $rrdb > Gerando grafico 08:"

rrdtool graph "/var/www/htdocs/image-registrobr-ipv4-008.png" \
      --start 1577836800 --end 1577923140 --step=60  \
      --imgformat PNG                                \
      --vertical-label Latencia                      \
      --title "Latencia IPv4 Registro.br"            \
      --width=800 --height=400                       \
      --lower-limit=0  --slope-mode                  \
      --font-render-mode mono                        \
      --watermark "Patrick Brandao"                  \
      \
      --x-grid none                                  \
      \
      DEF:dsavglatmin=$rrdb:latmin:AVERAGE           \
      DEF:dsavglatavg=$rrdb:latavg:AVERAGE           \
      DEF:dsavglatmax=$rrdb:latmax:AVERAGE           \
      DEF:dsavgsent=$rrdb:sent:AVERAGE               \
      DEF:dsavgreceived=$rrdb:received:AVERAGE       \
      \
      CDEF:avglatminms=dsavglatmin,1000,/            \
      CDEF:avglatavgms=dsavglatavg,1000,/            \
      CDEF:avglatmaxms=dsavglatmax,1000,/            \
      \
      LINE1:avglatminms#00FF00:" Lat. min. \n"       \
      LINE1:avglatavgms#0000FF:" Lat. avg. \n"       \
      LINE1:avglatmaxms#FF0000:" Lat. max. \n"

Removendo grid do eixo Y: deixa somente as linhas de valores do eixo X

--y-grid none
# Arquivo /var/www/htdocs/graph-ping-registrobr-009.sh
#!/bin/sh

rrdb="/var/www/htdocs/_ping-registrobr-ipv4.rrd"

echo "# $rrdb > Gerando grafico 09:"

rrdtool graph "/var/www/htdocs/image-registrobr-ipv4-009.png" \
      --start 1577836800 --end 1577923140 --step=60  \
      --imgformat PNG                                \
      --vertical-label Latencia                      \
      --title "Latencia IPv4 Registro.br"            \
      --width=800 --height=400                       \
      --lower-limit=0  --slope-mode                  \
      --font-render-mode mono                        \
      --watermark "Patrick Brandao"                  \
      \
      --y-grid none                                  \
      \
      DEF:dsavglatmin=$rrdb:latmin:AVERAGE           \
      DEF:dsavglatavg=$rrdb:latavg:AVERAGE           \
      DEF:dsavglatmax=$rrdb:latmax:AVERAGE           \
      DEF:dsavgsent=$rrdb:sent:AVERAGE               \
      DEF:dsavgreceived=$rrdb:received:AVERAGE       \
      \
      CDEF:avglatminms=dsavglatmin,1000,/            \
      CDEF:avglatavgms=dsavglatavg,1000,/            \
      CDEF:avglatmaxms=dsavglatmax,1000,/            \
      \
      LINE1:avglatminms#00FF00:" Lat. min. \n"       \
      LINE1:avglatavgms#0000FF:" Lat. avg. \n"       \
      LINE1:avglatmaxms#FF0000:" Lat. max. \n"

Removendo grid do eixo Y e Y: remove o grid completamente, mas tambem remove a escala de valores em X e Y

--x-grid none --y-grid none
# Arquivo /var/www/htdocs/graph-ping-registrobr-010.sh
#!/bin/sh

rrdb="/var/www/htdocs/_ping-registrobr-ipv4.rrd"

echo "# $rrdb > Gerando grafico 09:"

rrdtool graph "/var/www/htdocs/image-registrobr-ipv4-010.png" \
      --start 1577836800 --end 1577923140 --step=60  \
      --imgformat PNG                                \
      --vertical-label Latencia                      \
      --title "Latencia IPv4 Registro.br"            \
      --width=800 --height=400                       \
      --lower-limit=0  --slope-mode                  \
      --font-render-mode mono                        \
      --watermark "Patrick Brandao"                  \
      \
      --x-grid none                                  \
      --y-grid none                                  \
      \
      DEF:dsavglatmin=$rrdb:latmin:AVERAGE           \
      DEF:dsavglatavg=$rrdb:latavg:AVERAGE           \
      DEF:dsavglatmax=$rrdb:latmax:AVERAGE           \
      DEF:dsavgsent=$rrdb:sent:AVERAGE               \
      DEF:dsavgreceived=$rrdb:received:AVERAGE       \
      \
      CDEF:avglatminms=dsavglatmin,1000,/            \
      CDEF:avglatavgms=dsavglatavg,1000,/            \
      CDEF:avglatmaxms=dsavglatmax,1000,/            \
      \
      LINE1:avglatminms#00FF00:" Lat. min. \n"       \
      LINE1:avglatavgms#0000FF:" Lat. avg. \n"       \
      LINE1:avglatmaxms#FF0000:" Lat. max. \n"



Personalizando a borda

A borda da imagem final é normalmente cinza claro (top-left) e cinza escuro (bottom-right) com 2 pixels de largura.
A largura em pixels da borda é desenhada do tamanho total para dentro, assim, desenhar uma borda muito larga fará com que haja invasão do espaço interno da imagem.

A espessura da borda é especificada com o argumento --border

As cores das bordas são definidas na opção SHADEA e SHADEB do argumento --color

Testando com --border 10 --border 10 --color SHADEA#00FF00ff --color SHADEB#0000FFff

# Arquivo /var/www/htdocs/graph-ping-registrobr-011.sh
#!/bin/sh

rrdb="/var/www/htdocs/_ping-registrobr-ipv4.rrd"

echo "# $rrdb > Gerando grafico 011:"

rrdtool graph "/var/www/htdocs/image-registrobr-ipv4-011.png" \
      --start 1577836800 --end 1577923140 --step=60  \
      --imgformat PNG                                \
      --vertical-label Latencia                      \
      --title "Latencia IPv4 Registro.br"            \
      --width=800 --height=400                       \
      --lower-limit=0  --slope-mode                  \
      --font-render-mode mono                        \
      --watermark "Patrick Brandao"                  \
      --grid-dash 0:1                                \
      \
      --border 10                                    \
      --color SHADEA#00FF00ff --color SHADEB#0000FFff \
      \
      DEF:dsavglatmin=$rrdb:latmin:AVERAGE           \
      DEF:dsavglatavg=$rrdb:latavg:AVERAGE           \
      DEF:dsavglatmax=$rrdb:latmax:AVERAGE           \
      DEF:dsavgsent=$rrdb:sent:AVERAGE               \
      DEF:dsavgreceived=$rrdb:received:AVERAGE       \
      \
      CDEF:avglatminms=dsavglatmin,1000,/            \
      CDEF:avglatavgms=dsavglatavg,1000,/            \
      CDEF:avglatmaxms=dsavglatmax,1000,/            \
      \
      LINE1:avglatminms#00FF00:" Lat. min. \n"       \
      LINE1:avglatavgms#0000FF:" Lat. avg. \n"       \
      LINE1:avglatmaxms#FF0000:" Lat. max. \n"


Para remover a borda informe o valor zero: --border 0

# Arquivo /var/www/htdocs/graph-ping-registrobr-012.sh
#!/bin/sh

rrdb="/var/www/htdocs/_ping-registrobr-ipv4.rrd"

echo "# $rrdb > Gerando grafico 12:"

rrdtool graph "/var/www/htdocs/image-registrobr-ipv4-012.png" \
      --start 1577836800 --end 1577923140 --step=60  \
      --imgformat PNG                                \
      --vertical-label Latencia                      \
      --title "Latencia IPv4 Registro.br"            \
      --width=800 --height=400                       \
      --lower-limit=0  --slope-mode                  \
      --font-render-mode mono                        \
      --watermark "Patrick Brandao"                  \
      --grid-dash 0:1                                \
      \
      --border 0                                     \
      \
      DEF:dsavglatmin=$rrdb:latmin:AVERAGE           \
      DEF:dsavglatavg=$rrdb:latavg:AVERAGE           \
      DEF:dsavglatmax=$rrdb:latmax:AVERAGE           \
      DEF:dsavgsent=$rrdb:sent:AVERAGE               \
      DEF:dsavgreceived=$rrdb:received:AVERAGE       \
      \
      CDEF:avglatminms=dsavglatmin,1000,/            \
      CDEF:avglatavgms=dsavglatavg,1000,/            \
      CDEF:avglatmaxms=dsavglatmax,1000,/            \
      \
      LINE1:avglatminms#00FF00:" Lat. min. \n"       \
      LINE1:avglatavgms#0000FF:" Lat. avg. \n"       \
      LINE1:avglatmaxms#FF0000:" Lat. max. \n"



Personalizando textos

Cada texto utilizado na imagem pode ser personalizado com os argumentos --font

Fontes: --font TAG:FSIZE:FAMILY

TAG determina o local onde o texto é utilizado.
DEFAULT se aplica a todos os textos.
TITLE se aplica a título.
AXIS se aplica aos valores das escalas.
LEGEND se aplica aos textos de legenda.
UNIT se aplica a legenda vertical esquerda.
WATERMARK se aplica ao texto da marca d'água.

FSIZE determina o tamanho do texto, em pixels.

FAMILY determina o font-family. A fonte que deve estar instalada no sistema (fontconfig)


Cores: --color TAG#RGBA

TAG determina o local onde a cor é empregada.
ARROW ponta da setinha da linha do tempo e da escala.
SHADEA borda do quadro de fora, parte esquerda e superior.
SHADEB borda do quadro de fora, parte direita e inferior.
GRID linha clara do grid no espaço de plotagem.
MGRID linha escura do grid no espaço de plotagem.
FONT cor de todas as fontes (textos) do gráfico.
FRAME cor da borda do quadrado da legenda.
BACK cor de fundo do quadro geral fora da plotagem.
CANVAS cor de fundo do espaço da plotagem.

#RGBA determina a cor no formato RGB ou RGBA.

Exemplo com: --font DEFAULT:0:sans
--font TITLE:7:sans
--font AXIS:7:sans
--font LEGEND:8:sans
--font TITLE:13:sans
--font UNIT:18:sans
--font WATERMARK:8:sans

--color ARROW#00ff00
--color FONT#ff0000
--color FRAME#ffffff
--color BACK#ffff88
--color CANVAS#3399ff

# Arquivo /var/www/htdocs/graph-ping-registrobr-013.sh
#!/bin/sh

rrdb="/var/www/htdocs/_ping-registrobr-ipv4.rrd"

echo "# $rrdb > Gerando grafico 130:"

rrdtool graph "/var/www/htdocs/image-registrobr-ipv4-013.png" \
      --start 1577836800 --end 1577923140 --step=60  \
      --imgformat PNG                                \
      --vertical-label Latencia                      \
      --title "Latencia IPv4 Registro.br"            \
      --width=800 --height=400                       \
      --lower-limit=0  --slope-mode                  \
      --font-render-mode mono                        \
      --watermark "Patrick Brandao"                  \
      --grid-dash 0:1                                \
      --border 0                                     \
      \
      --font DEFAULT:0:sans                          \
      --font TITLE:8:sans                            \
      --font AXIS:10:sans                            \
      --font LEGEND:12:sans                          \
      --font TITLE:14:sans                           \
      --font UNIT:18:sans                            \
      --font WATERMARK:8:sans                        \
      \
      --color ARROW#00ff00                           \
      --color FONT#ff0000                            \
      --color FRAME#ffffff                           \
      --color BACK#ffff88                            \
      --color CANVAS#3399ff                          \
      \
      DEF:dsavglatmin=$rrdb:latmin:AVERAGE           \
      DEF:dsavglatavg=$rrdb:latavg:AVERAGE           \
      DEF:dsavglatmax=$rrdb:latmax:AVERAGE           \
      DEF:dsavgsent=$rrdb:sent:AVERAGE               \
      DEF:dsavgreceived=$rrdb:received:AVERAGE       \
      \
      CDEF:avglatminms=dsavglatmin,1000,/            \
      CDEF:avglatavgms=dsavglatavg,1000,/            \
      CDEF:avglatmaxms=dsavglatmax,1000,/            \
      \
      LINE1:avglatminms#00FF00:" Lat. min. \n"       \
      LINE1:avglatavgms#0000FF:" Lat. avg. \n"       \
      LINE1:avglatmaxms#FF0000:" Lat. max. \n"


Desenhando áreas

Até aqui usamos somente linhas, vamos adicionar as áreas para criar gráficos mais encorpados e com mais cores. O argumento AREA preenche o espaço da base até o valor coletado.

Ao utilizar vários argumentos LINE e AREA com valores que se sobrepõe você pode acabar apagando um elemento plotando outro por cima. Observe:

# Arquivo /var/www/htdocs/graph-ping-registrobr-a01.sh
#!/bin/sh

rrdb="/var/www/htdocs/_ping-registrobr-ipv4.rrd"

echo "# $rrdb > Gerando grafico a01:"

rrdtool graph "/var/www/htdocs/image-registrobr-ipv4-a01.png" \
      --start 1577836800 --end 1577923140 --step=60  \
      --imgformat PNG                                \
      --vertical-label Latencia                      \
      --title "Latencia IPv4 Registro.br"            \
      --width=800 --height=400                       \
      --lower-limit=0                                \
      --slope-mode                                   \
      --font-render-mode mono                        \
      --watermark "Patrick Brandao"                  \
      \
      DEF:dsavglatmin=$rrdb:latmin:AVERAGE           \
      DEF:dsavglatavg=$rrdb:latavg:AVERAGE           \
      DEF:dsavglatmax=$rrdb:latmax:AVERAGE           \
      DEF:dsavgsent=$rrdb:sent:AVERAGE               \
      DEF:dsavgreceived=$rrdb:received:AVERAGE       \
      \
      CDEF:avglatminms=dsavglatmin,1000,/            \
      CDEF:avglatavgms=dsavglatavg,1000,/            \
      CDEF:avglatmaxms=dsavglatmax,1000,/            \
      \
      AREA:avglatminms#00FF00:" Lat. min. \n"       \
      AREA:avglatavgms#0000FF:" Lat. avg. \n"       \
      AREA:avglatmaxms#FF0000:" Lat. max. \n"

Agora invertemos a plotagem, colocando valores maiores primeiros:

# Arquivo /var/www/htdocs/graph-ping-registrobr-a02.sh
#!/bin/sh

rrdb="/var/www/htdocs/_ping-registrobr-ipv4.rrd"

echo "# $rrdb > Gerando grafico a02:"

rrdtool graph "/var/www/htdocs/image-registrobr-ipv4-a02.png" \
      --start 1577836800 --end 1577923140 --step=60  \
      --imgformat PNG                                \
      --vertical-label Latencia                      \
      --title "Latencia IPv4 Registro.br"            \
      --width=800 --height=400                       \
      --lower-limit=0                                \
      --slope-mode                                   \
      --font-render-mode mono                        \
      --watermark "Patrick Brandao"                  \
      \
      DEF:dsavglatmin=$rrdb:latmin:AVERAGE           \
      DEF:dsavglatavg=$rrdb:latavg:AVERAGE           \
      DEF:dsavglatmax=$rrdb:latmax:AVERAGE           \
      DEF:dsavgsent=$rrdb:sent:AVERAGE               \
      DEF:dsavgreceived=$rrdb:received:AVERAGE       \
      \
      CDEF:avglatminms=dsavglatmin,1000,/            \
      CDEF:avglatavgms=dsavglatavg,1000,/            \
      CDEF:avglatmaxms=dsavglatmax,1000,/            \
      \
      AREA:avglatmaxms#FF0000:" Lat. max. \n"       \
      AREA:avglatavgms#0000FF:" Lat. avg. \n"       \
      AREA:avglatminms#00FF00:" Lat. min. \n"

Vamos empregar duas técnicas novas: adicionar transparência de 50% (hexadecimal 0x80) e desenhar uma linha apos plotar a área. Teremos um efeito "borda de área".
Como os desenhos tipo AREA realizados por último tem efeito borracha sobres os anteriores, desenharemos a cor branca no lugar da verde.

# Arquivo /var/www/htdocs/graph-ping-registrobr-a03.sh
#!/bin/sh

rrdb="/var/www/htdocs/_ping-registrobr-ipv4.rrd"

echo "# $rrdb > Gerando grafico a03:"

rrdtool graph "/var/www/htdocs/image-registrobr-ipv4-a03.png" \
      --start 1577836800 --end 1577923140 --step=60  \
      --imgformat PNG                                \
      --vertical-label Latencia                      \
      --title "Latencia IPv4 Registro.br"            \
      --width=800 --height=400                       \
      --lower-limit=0                                \
      --slope-mode                                   \
      --font-render-mode mono                        \
      --watermark "Patrick Brandao"                  \
      \
      DEF:dsavglatmin=$rrdb:latmin:AVERAGE           \
      DEF:dsavglatavg=$rrdb:latavg:AVERAGE           \
      DEF:dsavglatmax=$rrdb:latmax:AVERAGE           \
      DEF:dsavgsent=$rrdb:sent:AVERAGE               \
      DEF:dsavgreceived=$rrdb:received:AVERAGE       \
      \
      CDEF:avglatminms=dsavglatmin,1000,/            \
      CDEF:avglatavgms=dsavglatavg,1000,/            \
      CDEF:avglatmaxms=dsavglatmax,1000,/            \
      \
      AREA:avglatmaxms#FF000080       \
      AREA:avglatavgms#0000FF80       \
      AREA:avglatminms#FFFFFF       \
      \
      LINE3:avglatminms#00FF00:" Lat. min. \n"       \
      LINE1:avglatavgms#0000FF:" Lat. avg. \n"       \
      LINE1:avglatmaxms#FF0000:" Lat. max. \n"



Áreas empilhadas

Não é muito prático ficar apagando parte de uma área anterior para prover um efeito flutuante nos gráficos. Para tal existe o argumento STACK, que representa o valor desejado tomando como base o valor anterior em vez da base zero.

Podemos desenhar uma linha (visíveis ou não) e encima dela empilhar valores relativos.

Tomando como base para o empilhamento o menor valor, desenharemos a diferença entre ele e os demais acima.

# Arquivo /var/www/htdocs/graph-ping-registrobr-a04.sh
#!/bin/sh

rrdb="/var/www/htdocs/_ping-registrobr-ipv4.rrd"

echo "# $rrdb > Gerando grafico a04:"

rrdtool graph "/var/www/htdocs/image-registrobr-ipv4-a04.png" \
      --start 1577836800 --end 1577923140 --step=60  \
      --imgformat PNG                                \
      --vertical-label Latencia                      \
      --title "Latencia IPv4 Registro.br"            \
      --width=800 --height=400                       \
      --lower-limit=0                                \
      --slope-mode                                   \
      --font-render-mode mono                        \
      --watermark "Patrick Brandao"                  \
      \
      DEF:dsavglatmin=$rrdb:latmin:AVERAGE           \
      DEF:dsavglatavg=$rrdb:latavg:AVERAGE           \
      DEF:dsavglatmax=$rrdb:latmax:AVERAGE           \
      DEF:dsavgsent=$rrdb:sent:AVERAGE               \
      DEF:dsavgreceived=$rrdb:received:AVERAGE       \
      \
      CDEF:avglatminms=dsavglatmin,1000,/            \
      CDEF:avglatavgms=dsavglatavg,1000,/            \
      CDEF:avglatmaxms=dsavglatmax,1000,/            \
      \
      CDEF:stkmin2avg=avglatavgms,avglatminms,-      \
      CDEF:stkavg2max=avglatmaxms,avglatavgms,-      \
      \
      LINE1:avglatminms#00FF00       \
      \
      AREA:stkmin2avg#0000FF80::STACK       \
      AREA:stkavg2max#FF000080::STACK



Expressões RPN (Reverse Polish Notation)

Nos exemplos anteriores utilizamos algumas expressões para preencher variáveis CDEF, agora vamos entender a sintaxe e as possibilidades para complementar nossos gráficos.
Observe o argumento abaixo:

"CDEF:zero=latmaxms,0,*" Temos a declaração CDEF criando uma variável chamada zero, em seguida atribuímos (=) a ela a expressão RPN latmaxms,0,*.
Estamos multiplicando (*) o conteúdo da variável latmaxms por 0 (zero).

A sintaxe é: RESULTADO=VALOR1,VALOR2,OPERADOR

Nos operadores você pode: + somar, - subtrair, * múltiplicar, / dividor ou % obter o resto.

Exemplos:
"CDEF:rxbits=rxbytes,8,*" Multiplica o número de rxbytes por 8 para obter o valor em rxbits (nova variável). A variável rxbytes precisa estar definida em argumento anterior.

"CDEF:rxbits90=rxbits,0.9,*" Multiplica a variável rxbits por 0.9 para obter 90% do valor.

"CDEF:rxbitsneg=rxbits,-1,*" Multiplica a variável rxbits por -1 para obter o valor negativo (desenho invertido estilo iceberg).

"CDEF:latminms=dsavglatmin,1000,/" Um dos argumentos que utilizamos no início do artigo, dividimos o valor da latência (dsavglatmin) em microsegundos por 1000 para obter o valor em milisegundos (latminms).


Além de operações matemáticas básicas, temos também funções internas:

MAXIMUM, MINIMUM e AVERAGE
Percorrem todos os valores coletados para a variável do início ao fim do período, em seguida extrai o valor desejado para:
VDEF:toplatmaxms=latmaxms,MAXIMUM Retorna o maior valor da variável latmaxms

VDEF:avglatavgms=latavgms,AVERAGE Soma todos os valores de latavgms e divide pelo número de registros, obtendo uma média geral

VDEF:lowerlatminms=latminms,MINIMUM Retorna o menor valor da variável latminms

PERCENT Calcula a porcentagem de um valor. Exemplo:
VDEF:perc95=mydata,95,PERCENT

LAST e FIRST Retorna o primeiro ou o último número coletado na base de dados. Exemplo:
VDEF:lastlatms=latavgms,LAST VDEF:firstlatms=latavgms,FIRST

FLOOR e CEIL Arredonda para cima ou para baixo para retornar sempre um número inteiro. Exemplo:
VDEF:flatavgms=latavgms,FLOOR VDEF:clatavgms=latavgms,CEIL


# Arquivo /var/www/htdocs/graph-ping-registrobr-b01.sh
#!/bin/sh

rrdb="/var/www/htdocs/_ping-registrobr-ipv4.rrd"

echo "# $rrdb > Gerando grafico a04:"

rrdtool graph "/var/www/htdocs/image-registrobr-ipv4-b01.png" \
      --start 1577836800 --end 1577923140 --step=60  \
      --imgformat PNG                                \
      --vertical-label Latencia                      \
      --title "Latencia IPv4 Registro.br"            \
      --width=800 --height=400                       \
      --lower-limit=0                                \
      --slope-mode                                   \
      --font-render-mode mono                        \
      --watermark "Patrick Brandao"                  \
      \
      DEF:dsavglatmin=$rrdb:latmin:AVERAGE           \
      DEF:dsavglatavg=$rrdb:latavg:AVERAGE           \
      DEF:dsavglatmax=$rrdb:latmax:AVERAGE           \
      DEF:dsavgsent=$rrdb:sent:AVERAGE               \
      DEF:dsavgreceived=$rrdb:received:AVERAGE       \
      \
      CDEF:avglatminms=dsavglatmin,1000,/            \
      CDEF:avglatavgms=dsavglatavg,1000,/            \
      CDEF:avglatmaxms=dsavglatmax,1000,/            \
      \
      VDEF:MINavglatminms=avglatminms,MINIMUM        \
      VDEF:AVGavglatavgms=avglatavgms,AVERAGE        \
      VDEF:TOPavglatmaxms=avglatmaxms,MAXIMUM        \
      \
      CDEF:stkmin2avg=avglatavgms,avglatminms,-      \
      CDEF:stkavg2max=avglatmaxms,avglatavgms,-      \
      \
      LINE1:avglatminms#00FF00                       \
      \
      AREA:stkmin2avg#0000FF80::STACK                \
      AREA:stkavg2max#FF000080::STACK                \
      \
      LINE5:MINavglatminms#00AA0080                 \
      LINE5:AVGavglatavgms#0000AA80                 \
      LINE5:TOPavglatmaxms#AA000080



Produzindo legendas
# Arquivo /var/www/htdocs/graph-ping-registrobr-leg01.sh
#!/bin/sh

rrdb="/var/www/htdocs/_ping-registrobr-ipv4.rrd"

echo "# $rrdb > Gerando grafico a04:"

rrdtool graph "/var/www/htdocs/image-registrobr-ipv4-leg01.png" \
      --start 1577836800 --end 1577923140 --step=60  \
      --imgformat PNG                                \
      --width=800 --height=400                       \
      --lower-limit=0                                \
      --slope-mode                                   \
      --font-render-mode mono                        \
      \
      -Y \
      -E \
      -R normal \
      --alt-autoscale --rigid \
      \
      --font LEGEND:8:'DroidSansMono,DejaVuSansMono' \
      --font AXIS:7:'DroidSansMono,DejaVuSansMono' \
      --font-render-mode normal \
      \
      -c BACK#EEEEEE00 \
      -c SHADEA#EEEEEE00 \
      -c SHADEB#EEEEEE00 \
      -c FONT#000000 \
      -c CANVAS#FFFFFF00 \
      -c GRID#a5a5a5 \
      -c MGRID#FF9999 \
      -c FRAME#5e5e5e \
      -c ARROW#5e5e5e \
      \
      --vertical-label Latencia                      \
      --title "Latencia IPv4 Registro.br"            \
      --watermark "Patrick Brandao"                  \
      \
      DEF:dsavglatmin=$rrdb:latmin:AVERAGE           \
      DEF:dsavglatavg=$rrdb:latavg:AVERAGE           \
      DEF:dsavglatmax=$rrdb:latmax:AVERAGE           \
      DEF:dsavgsent=$rrdb:sent:AVERAGE               \
      DEF:dsavgreceived=$rrdb:received:AVERAGE       \
      \
      CDEF:avglatminms=dsavglatmin,1000,/            \
      CDEF:avglatavgms=dsavglatavg,1000,/            \
      CDEF:avglatmaxms=dsavglatmax,1000,/            \
      \
      CDEF:stkmin2avg=avglatavgms,avglatminms,-      \
      CDEF:stkavg2max=avglatmaxms,avglatavgms,-      \
      \
      \
      COMMENT:'Milliseconds ' \
      COMMENT:' Now' COMMENT:' Avg' \
      COMMENT:' Min' COMMENT:' Max' \
      COMMENT:'\l' \
      \
      AREA:avglatavgms#FF7373:"Ping " \
      GPRINT:avglatavgms:LAST:%6.1lf%S \
      GPRINT:avglatavgms:AVERAGE:%6.1lf%S \
      GPRINT:avglatavgms:MIN:%6.1lf%S \
      GPRINT:avglatavgms:MAX:%6.1lf%S \
      COMMENT:'\l' \
      LINE1:avglatavgms#7F3939













CONTINUA... (ainda estou escrevendo!)
CONTATO