Análise TSDB no Prometheus 2

Análise TSDB no Prometheus 2

O banco de dados de série temporal (TSDB) no Prometheus 2 é um excelente exemplo de solução de engenharia que oferece grandes melhorias em relação ao armazenamento v2 no Prometheus 1 em termos de velocidade de acumulação de dados, execução de consultas e eficiência de recursos. Estávamos implementando o Prometheus 2 no Percona Monitoring and Management (PMM) e tive a oportunidade de entender o desempenho do Prometheus 2 TSDB. Neste artigo falarei sobre os resultados dessas observações.

Carga de trabalho média do Prometheus

Para aqueles acostumados a lidar com bancos de dados de uso geral, a carga de trabalho típica do Prometheus é bastante interessante. A taxa de acumulação de dados tende a ser estável: normalmente os serviços que você monitora enviam aproximadamente o mesmo número de métricas e a infraestrutura muda de forma relativamente lenta.
Os pedidos de informação podem provir de diversas fontes. Alguns deles, como os alertas, também buscam um valor estável e previsível. Outros, como solicitações de usuários, podem causar intermitências, embora esse não seja o caso da maioria das cargas de trabalho.

Teste de carga

Durante os testes, concentrei-me na capacidade de acumular dados. Implantei o Prometheus 2.3.2 compilado com Go 1.10.1 (como parte do PMM 1.14) no serviço Linode usando este script: StackScript. Para a geração de carga mais realista, usando este StackScript Lancei vários nós MySQL com carga real (teste Sysbench TPC-C), cada um dos quais emulou 10 nós Linux/MySQL.
Todos os testes a seguir foram realizados em um servidor Linode com oito núcleos virtuais e 32 GB de memória, executando 20 simulações de carga monitorando duzentas instâncias MySQL. Ou, nos termos do Prometheus, 800 alvos, 440 arranhões por segundo, 380 mil registros por segundo e 1,7 milhão de séries temporais ativas.

Projeto

A abordagem usual dos bancos de dados tradicionais, incluindo aquele usado pelo Prometheus 1.x, é limite de memória. Se não for suficiente para lidar com a carga, você experimentará altas latências e algumas solicitações falharão. O uso de memória no Prometheus 2 é configurável via chave storage.tsdb.min-block-duration, que determina por quanto tempo as gravações serão mantidas na memória antes de serem descarregadas no disco (o padrão é 2 horas). A quantidade de memória necessária dependerá do número de séries temporais, rótulos e raspagens adicionadas ao fluxo de entrada líquido. Em termos de espaço em disco, o Prometheus pretende utilizar 3 bytes por registro (amostra). Por outro lado, os requisitos de memória são muito maiores.

Embora seja possível configurar o tamanho do bloco, não é recomendado configurá-lo manualmente, portanto você é forçado a fornecer ao Prometheus a quantidade de memória necessária para sua carga de trabalho.
Se não houver memória suficiente para suportar o fluxo de entrada de métricas, o Prometheus ficará sem memória ou o assassino OOM chegará até ele.
Adicionar swap para atrasar a falha quando o Prometheus fica sem memória realmente não ajuda, porque usar esta função causa um consumo explosivo de memória. Acho que tem algo a ver com Go, seu coletor de lixo e a maneira como ele lida com a troca.
Outra abordagem interessante é configurar o bloco principal para ser descarregado no disco em um determinado momento, em vez de contá-lo desde o início do processo.

Análise TSDB no Prometheus 2

Como você pode ver no gráfico, as liberações no disco ocorrem a cada duas horas. Se você alterar o parâmetro min-block-duration para uma hora, essas redefinições ocorrerão a cada hora, começando após meia hora.
Se quiser usar este e outros gráficos em sua instalação do Prometheus, você pode usar este painel. Ele foi projetado para PMM, mas, com pequenas modificações, cabe em qualquer instalação do Prometheus.
Temos um bloco ativo chamado bloco principal que está armazenado na memória; blocos com dados mais antigos estão disponíveis via mmap(). Isso elimina a necessidade de configurar o cache separadamente, mas também significa que você precisa deixar espaço suficiente para o cache do sistema operacional se quiser consultar dados mais antigos do que o bloco principal pode acomodar.
Isso também significa que o consumo de memória virtual do Prometheus parecerá bastante alto, o que não é motivo de preocupação.

Análise TSDB no Prometheus 2

Outro ponto interessante de design é o uso do WAL (write ahead log). Como você pode ver na documentação de armazenamento, o Prometheus usa WAL para evitar travamentos. Os mecanismos específicos para garantir a sobrevivência dos dados não estão, infelizmente, bem documentados. O Prometheus versão 2.3.2 libera o WAL para o disco a cada 10 segundos e esta opção não pode ser configurada pelo usuário.

Compactações

O Prometheus TSDB foi projetado como um armazenamento LSM (Log Structured Merge): o bloco principal é descarregado periodicamente no disco, enquanto um mecanismo de compactação combina vários blocos para evitar a varredura de muitos blocos durante as consultas. Aqui você pode ver a quantidade de blocos que observei no sistema de teste após um dia de carga.

Análise TSDB no Prometheus 2

Se quiser saber mais sobre a loja, você pode examinar o arquivo meta.json, que contém informações sobre os blocos disponíveis e como eles surgiram.

{
       "ulid": "01CPZDPD1D9R019JS87TPV5MPE",
       "minTime": 1536472800000,
       "maxTime": 1536494400000,
       "stats": {
               "numSamples": 8292128378,
               "numSeries": 1673622,
               "numChunks": 69528220
       },
       "compaction": {
               "level": 2,
               "sources": [
                       "01CPYRY9MS465Y5ETM3SXFBV7X",
                       "01CPYZT0WRJ1JB1P0DP80VY5KJ",
                       "01CPZ6NR4Q3PDP3E57HEH760XS"
               ],
               "parents": [
                       {
                               "ulid": "01CPYRY9MS465Y5ETM3SXFBV7X",
                               "minTime": 1536472800000,
                               "maxTime": 1536480000000
                       },
                       {
                               "ulid": "01CPYZT0WRJ1JB1P0DP80VY5KJ",
                               "minTime": 1536480000000,
                               "maxTime": 1536487200000
                       },
                       {
                               "ulid": "01CPZ6NR4Q3PDP3E57HEH760XS",
                               "minTime": 1536487200000,
                               "maxTime": 1536494400000
                       }
               ]
       },
       "version": 1
}

As compactações no Prometheus estão vinculadas ao momento em que o bloco principal é descarregado no disco. Neste ponto, várias dessas operações podem ser realizadas.

Análise TSDB no Prometheus 2

Parece que as compactações não são limitadas de forma alguma e podem causar grandes picos de E/S no disco durante a execução.

Análise TSDB no Prometheus 2

Picos de carga da CPU

Análise TSDB no Prometheus 2

É claro que isso tem um impacto bastante negativo na velocidade do sistema e também representa um sério desafio para o armazenamento LSM: como fazer a compactação para suportar altas taxas de solicitação sem causar muita sobrecarga?
O uso da memória no processo de compactação também parece bastante interessante.

Análise TSDB no Prometheus 2

Podemos ver como, após a compactação, a maior parte da memória muda de estado de Cache para Livre: isso significa que informações potencialmente valiosas foram removidas de lá. Curioso se é usado aqui fadvice() ou alguma outra técnica de minimização, ou é porque o cache foi liberado de blocos destruídos durante a compactação?

Recuperação após uma falha

A recuperação de falhas leva tempo e por boas razões. Para um fluxo de entrada de um milhão de registros por segundo, tive que esperar cerca de 25 minutos enquanto a recuperação era realizada levando em consideração a unidade SSD.

level=info ts=2018-09-13T13:38:14.09650965Z caller=main.go:222 msg="Starting Prometheus" version="(version=2.3.2, branch=v2.3.2, revision=71af5e29e815795e9dd14742ee7725682fa14b7b)"
level=info ts=2018-09-13T13:38:14.096599879Z caller=main.go:223 build_context="(go=go1.10.1, user=Jenkins, date=20180725-08:58:13OURCE)"
level=info ts=2018-09-13T13:38:14.096624109Z caller=main.go:224 host_details="(Linux 4.15.0-32-generic #35-Ubuntu SMP Fri Aug 10 17:58:07 UTC 2018 x86_64 1bee9e9b78cf (none))"
level=info ts=2018-09-13T13:38:14.096641396Z caller=main.go:225 fd_limits="(soft=1048576, hard=1048576)"
level=info ts=2018-09-13T13:38:14.097715256Z caller=web.go:415 component=web msg="Start listening for connections" address=:9090
level=info ts=2018-09-13T13:38:14.097400393Z caller=main.go:533 msg="Starting TSDB ..."
level=info ts=2018-09-13T13:38:14.098718401Z caller=repair.go:39 component=tsdb msg="found healthy block" mint=1536530400000 maxt=1536537600000 ulid=01CQ0FW3ME8Q5W2AN5F9CB7R0R
level=info ts=2018-09-13T13:38:14.100315658Z caller=web.go:467 component=web msg="router prefix" prefix=/prometheus
level=info ts=2018-09-13T13:38:14.101793727Z caller=repair.go:39 component=tsdb msg="found healthy block" mint=1536732000000 maxt=1536753600000 ulid=01CQ78486TNX5QZTBF049PQHSM
level=info ts=2018-09-13T13:38:14.102267346Z caller=repair.go:39 component=tsdb msg="found healthy block" mint=1536537600000 maxt=1536732000000 ulid=01CQ78DE7HSQK0C0F5AZ46YGF0
level=info ts=2018-09-13T13:38:14.102660295Z caller=repair.go:39 component=tsdb msg="found healthy block" mint=1536775200000 maxt=1536782400000 ulid=01CQ7SAT4RM21Y0PT5GNSS146Q
level=info ts=2018-09-13T13:38:14.103075885Z caller=repair.go:39 component=tsdb msg="found healthy block" mint=1536753600000 maxt=1536775200000 ulid=01CQ7SV8WJ3C2W5S3RTAHC2GHB
level=error ts=2018-09-13T14:05:18.208469169Z caller=wal.go:275 component=tsdb msg="WAL corruption detected; truncating" err="unexpected CRC32 checksum d0465484, want 0" file=/opt/prometheus/data/.prom2-data/wal/007357 pos=15504363
level=info ts=2018-09-13T14:05:19.471459777Z caller=main.go:543 msg="TSDB started"
level=info ts=2018-09-13T14:05:19.471604598Z caller=main.go:603 msg="Loading configuration file" filename=/etc/prometheus.yml
level=info ts=2018-09-13T14:05:19.499156711Z caller=main.go:629 msg="Completed loading of configuration file" filename=/etc/prometheus.yml
level=info ts=2018-09-13T14:05:19.499228186Z caller=main.go:502 msg="Server is ready to receive web requests."

O principal problema do processo de recuperação é o alto consumo de memória. Apesar de em uma situação normal o servidor poder funcionar de forma estável com a mesma quantidade de memória, se travar poderá não se recuperar devido ao OOM. A única solução que encontrei foi desabilitar a coleta de dados, abrir o servidor, deixá-lo recuperar e reiniciar com a coleta habilitada.

Aquecendo

Outro comportamento a ter em conta durante o aquecimento é a relação entre baixo desempenho e alto consumo de recursos logo após o arranque. Durante algumas inicializações, mas não todas, observei uma grande carga na CPU e na memória.

Análise TSDB no Prometheus 2

Análise TSDB no Prometheus 2

Lacunas no uso de memória indicam que o Prometheus não pode configurar todas as coleções desde o início e algumas informações são perdidas.
Não descobri os motivos exatos da alta carga de CPU e memória. Suspeito que isso se deva à criação de novas séries temporais no bloco principal com alta frequência.

Picos de carga da CPU

Além das compactações, que criam uma carga de E/S bastante alta, notei sérios picos na carga da CPU a cada dois minutos. As rajadas são mais longas quando o fluxo de entrada é alto e parecem ser causadas pelo coletor de lixo do Go, pelo menos alguns núcleos estão totalmente carregados.

Análise TSDB no Prometheus 2

Análise TSDB no Prometheus 2

Esses saltos não são tão insignificantes. Parece que quando isso ocorre, o ponto de entrada interno e as métricas do Prometheus ficam indisponíveis, causando lacunas de dados durante esses mesmos períodos de tempo.

Análise TSDB no Prometheus 2

Você também pode notar que o exportador do Prometheus é desligado por um segundo.

Análise TSDB no Prometheus 2

Podemos notar correlações com a coleta de lixo (GC).

Análise TSDB no Prometheus 2

Conclusão

O TSDB no Prometheus 2 é rápido, capaz de lidar com milhões de séries temporais e ao mesmo tempo milhares de registros por segundo usando hardware bastante modesto. A utilização de CPU e E/S de disco também é impressionante. Meu exemplo mostrou até 200 métricas por segundo por núcleo usado.

Para planejar a expansão, você precisa se lembrar de quantidades suficientes de memória, e esta deve ser memória real. A quantidade de memória usada que observei foi de cerca de 5 GB por 100 registros por segundo do fluxo de entrada, o que junto com o cache do sistema operacional gerou cerca de 000 GB de memória ocupada.

Claro, ainda há muito trabalho a ser feito para controlar os picos de CPU e E/S de disco, e isso não é surpreendente considerando o quão jovem o TSDB Prometheus 2 é comparado ao InnoDB, TokuDB, RocksDB, WiredTiger, mas todos eles tinham desempenho semelhante. problemas no início do seu ciclo de vida.

Fonte: habr.com

Adicionar um comentário