ClickHouse Database for Humans, or Alien Technologies

Aleksey Lizunov, Chefe do Centro de Competência para Canais de Atendimento Remoto da Diretoria de Tecnologias da Informação do MKB

ClickHouse Database for Humans, or Alien Technologies

Como alternativa à pilha ELK (ElasticSearch, Logstash, Kibana), estamos pesquisando sobre o uso do banco de dados ClickHouse como um armazenamento de dados para logs.

Neste artigo, gostaríamos de falar sobre nossa experiência de uso do banco de dados ClickHouse e os resultados preliminares da operação piloto. Deve-se notar desde já que os resultados foram impressionantes.


ClickHouse Database for Humans, or Alien Technologies

A seguir, descreveremos com mais detalhes como nosso sistema está configurado e em quais componentes ele consiste. Mas agora gostaria de falar um pouco sobre esse banco de dados como um todo e porque vale a pena ficar atento. O banco de dados ClickHouse é um banco de dados colunar analítico de alto desempenho da Yandex. É usado nos serviços Yandex, inicialmente é o principal armazenamento de dados do Yandex.Metrica. Sistema de código aberto, gratuito. Do ponto de vista de um desenvolvedor, sempre me perguntei como eles o implementaram, porque há dados fantasticamente grandes. E a própria interface de usuário do Metrica é muito flexível e rápida. Ao primeiro contato com esse banco de dados, a impressão é: “Bem, finalmente! Feito para o povo! Começando com o processo de instalação e terminando com o envio de solicitações.

Este banco de dados tem um limite de entrada muito baixo. Mesmo um desenvolvedor mediano pode instalar esse banco de dados em poucos minutos e começar a usá-lo. Tudo funciona claramente. Mesmo as pessoas que são novas no Linux podem lidar rapidamente com a instalação e fazer as operações mais simples. Se antes, com as palavras Big Data, Hadoop, Google BigTable, HDFS, um desenvolvedor comum tinha ideias de que se tratava de alguns terabytes, petabytes, que alguns super-humanos estão envolvidos em configurações e desenvolvimento para esses sistemas, então com o advento do ClickHouse banco de dados, temos uma ferramenta simples e compreensível com a qual você pode resolver uma gama de tarefas anteriormente inatingíveis. Leva apenas uma máquina razoavelmente média e cinco minutos para instalar. Ou seja, obtivemos um banco de dados como, por exemplo, MySql, mas apenas para armazenar bilhões de registros! Um certo super-arquivador com a linguagem SQL. É como se as pessoas tivessem recebido armas de alienígenas.

Sobre nosso sistema de registro

Para coletar informações, são usados ​​arquivos de log do IIS de aplicativos da Web de formato padrão (no momento, também estamos analisando logs de aplicativos, mas o objetivo principal no estágio piloto é coletar logs do IIS).

Por vários motivos, não poderíamos abandonar completamente a pilha ELK e continuamos a usar os componentes LogStash e Filebeat, que se mostraram bons e funcionam de maneira confiável e previsível.

O esquema geral de registro é mostrado na figura abaixo:

ClickHouse Database for Humans, or Alien Technologies

Um recurso de gravação de dados no banco de dados ClickHouse é a inserção pouco frequente (uma vez por segundo) de registros em grandes lotes. Esta, aparentemente, é a parte mais “problemática” que você encontra quando trabalha pela primeira vez com o banco de dados ClickHouse: o esquema torna-se um pouco mais complicado.
O plugin para LogStash, que insere dados diretamente no ClickHouse, ajudou muito aqui. Esse componente é implantado no mesmo servidor que o próprio banco de dados. Então, de um modo geral, não é recomendável fazê-lo, mas do ponto de vista prático, para não produzir servidores separados enquanto estiver implantado no mesmo servidor. Não observamos nenhuma falha ou conflito de recursos com o banco de dados. Além disso, deve-se notar que o plug-in possui um mecanismo de repetição em caso de erros. E em caso de erros, o plug-in grava no disco um lote de dados que não pôde ser inserido (o formato do arquivo é conveniente: após a edição, você pode inserir facilmente o lote corrigido usando o clickhouse-client).

Uma lista completa de softwares usados ​​no esquema é apresentada na tabela:

Lista de softwares usados

Nome

descrição

link de distribuição

NGINX

Proxy reverso para restringir o acesso por portas e organizar a autorização

Atualmente não usado no esquema

https://nginx.org/ru/download.html

https://nginx.org/download/nginx-1.16.0.tar.gz

ArquivoBeat

Transferência de logs de arquivo.

https://www.elastic.co/downloads/beats/filebeat (kit de distribuição para Windows 64 bits).

https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.3.0-windows-x86_64.zip

logstash

Coletor de log.

Usado para coletar logs do FileBeat, bem como para coletar logs da fila do RabbitMQ (para servidores que estão na DMZ).

https://www.elastic.co/products/logstash

https://artifacts.elastic.co/downloads/logstash/logstash-7.0.1.rpm

Logstash-output-clickhouse

Plug-in Loagstash para transferir logs para o banco de dados ClickHouse em lotes

https://github.com/mikechris/logstash-output-clickhouse

/usr/share/logstash/bin/logstash-plugin instalar logstash-output-clickhouse

/usr/share/logstash/bin/logstash-plugin instalar logstash-filter-prune

/usr/share/logstash/bin/logstash-plugin instalar logstash-filter-multiline

clickhouse

Armazenamento de registros https://clickhouse.yandex/docs/ru/

https://packagecloud.io/Altinity/clickhouse/packages/el/7/clickhouse-server-19.5.3.8-1.el7.x86_64.rpm

https://packagecloud.io/Altinity/clickhouse/packages/el/7/clickhouse-client-19.5.3.8-1.el7.x86_64.rpm

Observação. A partir de agosto de 2018, as compilações rpm “normais” para RHEL apareceram no repositório Yandex, para que você possa tentar usá-las. No momento da instalação, estávamos usando pacotes criados pelo Altinity.

grafana

Visualização de log. Configurando painéis

https://grafana.com/

https://grafana.com/grafana/download

Redhat & Centos(64 bits) - versão mais recente

Fonte de dados ClickHouse para Grafana 4.6+

Plugin para Grafana com fonte de dados ClickHouse

https://grafana.com/plugins/vertamedia-clickhouse-datasource

https://grafana.com/api/plugins/vertamedia-clickhouse-datasource/versions/1.8.1/download

logstash

Roteador de log do FileBeat para a fila do RabbitMQ.

Observação. Infelizmente, o FileBeat não envia diretamente para o RabbitMQ, portanto, é necessário um link intermediário na forma de Logstash

https://www.elastic.co/products/logstash

https://artifacts.elastic.co/downloads/logstash/logstash-7.0.1.rpm

RabbitMQ

fila de mensagens. Este é o buffer de log na DMZ

https://www.rabbitmq.com/download.html

https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.7.14/rabbitmq-server-3.7.14-1.el7.noarch.rpm

Erlang Runtime (Necessário para RabbitMQ)

Tempo de execução do Erlang. Necessário para RabbitMQ funcionar

http://www.erlang.org/download.html

https://www.rabbitmq.com/install-rpm.html#install-erlang http://www.erlang.org/downloads/21.3

A configuração do servidor com o banco de dados ClickHouse é apresentada na tabela a seguir:

Nome

Valor

Nota

Configuração

HDD: 40GB
RAM: 8GB
Processador: Core 2 2Ghz

É preciso ficar atento às dicas de operação do banco de dados ClickHouse (https://clickhouse.yandex/docs/ru/operations/tips/)

Software geral do sistema

SO: Red Hat Enterprise Linux Server (Maipo)

JRE (Java8)

 

Como você pode ver, esta é uma estação de trabalho comum.

A estrutura da tabela para armazenamento de logs é a seguinte:

log_web.sql

CREATE TABLE log_web (
  logdate Date,
  logdatetime DateTime CODEC(Delta, LZ4HC),
   
  fld_log_file_name LowCardinality( String ),
  fld_server_name LowCardinality( String ),
  fld_app_name LowCardinality( String ),
  fld_app_module LowCardinality( String ),
  fld_website_name LowCardinality( String ),
 
  serverIP LowCardinality( String ),
  method LowCardinality( String ),
  uriStem String,
  uriQuery String,
  port UInt32,
  username LowCardinality( String ),
  clientIP String,
  clientRealIP String,
  userAgent String,
  referer String,
  response String,
  subresponse String,
  win32response String,
  timetaken UInt64
   
  , uriQuery__utm_medium String
  , uriQuery__utm_source String
  , uriQuery__utm_campaign String
  , uriQuery__utm_term String
  , uriQuery__utm_content String
  , uriQuery__yclid String
  , uriQuery__region String
 
) Engine = MergeTree()
PARTITION BY toYYYYMM(logdate)
ORDER BY (fld_app_name, fld_app_module, logdatetime)
SETTINGS index_granularity = 8192;

Usamos particionamento padrão (por mês) e granularidade de índice. Todos os campos correspondem praticamente às entradas de log do IIS para registrar solicitações http. Separadamente, notamos que existem campos separados para armazenar tags utm (eles são analisados ​​​​no estágio de inserção na tabela a partir do campo de string de consulta).

Além disso, vários campos do sistema foram adicionados à tabela para armazenar informações sobre sistemas, componentes e servidores. Consulte a tabela abaixo para obter uma descrição desses campos. Em uma tabela, armazenamos logs para vários sistemas.

Nome

descrição

Exemplo

fld_app_name

Nome do aplicativo/sistema
Valores válidos:

  • site1.domain.com Site externo 1
  • site2.domain.com Site externo 2
  • internal-site1.domain.local Site interno 1

site1.dominio.com

fld_app_module

módulo do sistema
Valores válidos:

  • web - site
  • svc - serviço de site
  • intgr - Serviço Web de Integração
  • bo - Admin (BackOffice)

web

fld_website_name

Nome do site no IIS

Vários sistemas podem ser implantados em um servidor ou até mesmo várias instâncias de um módulo de sistema

web principal

nome_do_servidor_fld

Nome do servidor

web1.dominio.com

fld_log_file_name

Caminho para o arquivo de log no servidor

C:inetpublogsLogFiles
W3SVC1u_ex190711.log

Isso permite que você crie gráficos com eficiência no Grafana. Por exemplo, visualize as solicitações do front-end de um sistema específico. Isso é semelhante ao contador de sites no Yandex.Metrica.

Aqui estão algumas estatísticas sobre o uso do banco de dados por dois meses.

Número de registros divididos por sistemas e seus componentes

SELECT
    fld_app_name,
    fld_app_module,
    count(fld_app_name) AS rows_count
FROM log_web
GROUP BY
    fld_app_name,
    fld_app_module
    WITH TOTALS
ORDER BY
    fld_app_name ASC,
    rows_count DESC
 
┌─fld_app_name─────┬─fld_app_module─┬─rows_count─┐
│ site1.domain.ru  │ web            │     131441 │
│ site2.domain.ru  │ web            │    1751081 │
│ site3.domain.ru  │ web            │  106887543 │
│ site3.domain.ru  │ svc            │   44908603 │
│ site3.domain.ru  │ intgr          │    9813911 │
│ site4.domain.ru  │ web            │     772095 │
│ site5.domain.ru  │ web            │   17037221 │
│ site5.domain.ru  │ intgr          │     838559 │
│ site5.domain.ru  │ bo             │       7404 │
│ site6.domain.ru  │ web            │     595877 │
│ site7.domain.ru  │ web            │   27778858 │
└──────────────────┴────────────────┴────────────┘
 
Totals:
┌─fld_app_name─┬─fld_app_module─┬─rows_count─┐
│              │                │  210522593 │
└──────────────┴────────────────┴────────────┘
 
11 rows in set. Elapsed: 4.874 sec. Processed 210.52 million rows, 421.67 MB (43.19 million rows/s., 86.51 MB/s.)

A quantidade de dados no disco

SELECT
    formatReadableSize(sum(data_uncompressed_bytes)) AS uncompressed,
    formatReadableSize(sum(data_compressed_bytes)) AS compressed,
    sum(rows) AS total_rows
FROM system.parts
WHERE table = 'log_web'
 
┌─uncompressed─┬─compressed─┬─total_rows─┐
│ 54.50 GiB    │ 4.86 GiB   │  211427094 │
└──────────────┴────────────┴────────────┘
 
1 rows in set. Elapsed: 0.035 sec.

Grau de compactação de dados em colunas

SELECT
    name,
    formatReadableSize(data_uncompressed_bytes) AS uncompressed,
    formatReadableSize(data_compressed_bytes) AS compressed,
    data_uncompressed_bytes / data_compressed_bytes AS compress_ratio
FROM system.columns
WHERE table = 'log_web'
 
┌─name───────────────────┬─uncompressed─┬─compressed─┬─────compress_ratio─┐
│ logdate                │ 401.53 MiB   │ 1.80 MiB   │ 223.16665968777315 │
│ logdatetime            │ 803.06 MiB   │ 35.91 MiB  │ 22.363966401202305 │
│ fld_log_file_name      │ 220.66 MiB   │ 2.60 MiB   │  84.99905736932571 │
│ fld_server_name        │ 201.54 MiB   │ 50.63 MiB  │  3.980924816977078 │
│ fld_app_name           │ 201.17 MiB   │ 969.17 KiB │ 212.55518183686877 │
│ fld_app_module         │ 201.17 MiB   │ 968.60 KiB │ 212.67805817411906 │
│ fld_website_name       │ 201.54 MiB   │ 1.24 MiB   │  162.7204926761546 │
│ serverIP               │ 201.54 MiB   │ 50.25 MiB  │  4.010824061219731 │
│ method                 │ 201.53 MiB   │ 43.64 MiB  │  4.617721053304486 │
│ uriStem                │ 5.13 GiB     │ 832.51 MiB │  6.311522291936919 │
│ uriQuery               │ 2.58 GiB     │ 501.06 MiB │  5.269731450124478 │
│ port                   │ 803.06 MiB   │ 3.98 MiB   │ 201.91673864241824 │
│ username               │ 318.08 MiB   │ 26.93 MiB  │ 11.812513794583598 │
│ clientIP               │ 2.35 GiB     │ 82.59 MiB  │ 29.132328640073343 │
│ clientRealIP           │ 2.49 GiB     │ 465.05 MiB │  5.478382297052563 │
│ userAgent              │ 18.34 GiB    │ 764.08 MiB │  24.57905114484208 │
│ referer                │ 14.71 GiB    │ 1.37 GiB   │ 10.736792723669906 │
│ response               │ 803.06 MiB   │ 83.81 MiB  │  9.582334090987247 │
│ subresponse            │ 399.87 MiB   │ 1.83 MiB   │  218.4831068635027 │
│ win32response          │ 407.86 MiB   │ 7.41 MiB   │ 55.050315514606815 │
│ timetaken              │ 1.57 GiB     │ 402.06 MiB │ 3.9947395692010637 │
│ uriQuery__utm_medium   │ 208.17 MiB   │ 12.29 MiB  │ 16.936148912472955 │
│ uriQuery__utm_source   │ 215.18 MiB   │ 13.00 MiB  │ 16.548367623199912 │
│ uriQuery__utm_campaign │ 381.46 MiB   │ 37.94 MiB  │ 10.055156353418509 │
│ uriQuery__utm_term     │ 231.82 MiB   │ 10.78 MiB  │ 21.502540454070672 │
│ uriQuery__utm_content  │ 441.34 MiB   │ 87.60 MiB  │  5.038260760449327 │
│ uriQuery__yclid        │ 216.88 MiB   │ 16.58 MiB  │  13.07721335008116 │
│ uriQuery__region       │ 204.35 MiB   │ 9.49 MiB   │  21.52661903446796 │
└────────────────────────┴──────────────┴────────────┴────────────────────┘
 
28 rows in set. Elapsed: 0.005 sec.

Descrição dos componentes usados

FileBeat. Transferindo logs de arquivo

Esse componente rastreia as alterações nos arquivos de log no disco e passa as informações para o LogStash. Instalado em todos os servidores onde os arquivos de log são gravados (geralmente IIS). Funciona em modo tail (ou seja, transfere apenas os registros adicionados ao arquivo). Mas separadamente pode ser configurado para transferir arquivos inteiros. Isso é útil quando você precisa baixar dados de meses anteriores. Basta colocar o arquivo de log em uma pasta e ele o lerá na íntegra.

Quando o serviço é interrompido, os dados não são mais transferidos para o armazenamento.

Um exemplo de configuração se parece com isto:

arquivobeat.yml

filebeat.inputs:
- type: log
  enabled: true
  paths:
    - C:/inetpub/logs/LogFiles/W3SVC1/*.log
  exclude_files: ['.gz$','.zip$']
  tail_files: true
  ignore_older: 24h
  fields:
    fld_server_name: "site1.domain.ru"
    fld_app_name: "site1.domain.ru"
    fld_app_module: "web"
    fld_website_name: "web-main"
 
- type: log
  enabled: true
  paths:
    - C:/inetpub/logs/LogFiles/__Import/access_log-*
  exclude_files: ['.gz$','.zip$']
  tail_files: false
  fields:
    fld_server_name: "site2.domain.ru"
    fld_app_name: "site2.domain.ru"
    fld_app_module: "web"
    fld_website_name: "web-main"
    fld_logformat: "logformat__apache"
 
 
filebeat.config.modules:
  path: ${path.config}/modules.d/*.yml
  reload.enabled: false
  reload.period: 2s
 
output.logstash:
  hosts: ["log.domain.com:5044"]
 
  ssl.enabled: true
  ssl.certificate_authorities: ["C:/filebeat/certs/ca.pem", "C:/filebeat/certs/ca-issuing.pem"]
  ssl.certificate: "C:/filebeat/certs/site1.domain.ru.cer"
  ssl.key: "C:/filebeat/certs/site1.domain.ru.key"
 
#================================ Processors =====================================
 
processors:
  - add_host_metadata: ~
  - add_cloud_metadata: ~

logstash. Coletor de registros

Este componente é projetado para receber entradas de log do FileBeat (ou através da fila RabbitMQ), analisando e inserindo lotes no banco de dados ClickHouse.

Para inserção no ClickHouse, o plug-in Logstash-output-clickhouse é usado. O plug-in Logstash possui um mecanismo de repetição de consulta, mas com um desligamento regular, é melhor interromper o próprio serviço. Quando parado, as mensagens serão acumuladas na fila do RabbitMQ, portanto, se a parada for por muito tempo, é melhor parar o Filebeats nos servidores. Em um esquema em que o RabbitMQ não é usado (na rede local, o Filebeat envia logs diretamente para o Logstash), o Filebeats funciona de maneira bastante aceitável e segura, portanto, para eles, a indisponibilidade da saída passa sem consequências.

Um exemplo de configuração se parece com isto:

log_web__filebeat_clickhouse.conf

input {
 
    beats {
        port => 5044
        type => 'iis'
        ssl => true
        ssl_certificate_authorities => ["/etc/logstash/certs/ca.cer", "/etc/logstash/certs/ca-issuing.cer"]
        ssl_certificate => "/etc/logstash/certs/server.cer"
        ssl_key => "/etc/logstash/certs/server-pkcs8.key"
        ssl_verify_mode => "peer"
 
            add_field => {
                "fld_server_name" => "%{[fields][fld_server_name]}"
                "fld_app_name" => "%{[fields][fld_app_name]}"
                "fld_app_module" => "%{[fields][fld_app_module]}"
                "fld_website_name" => "%{[fields][fld_website_name]}"
                "fld_log_file_name" => "%{source}"
                "fld_logformat" => "%{[fields][fld_logformat]}"
            }
    }
 
    rabbitmq {
        host => "queue.domain.com"
        port => 5671
        user => "q-reader"
        password => "password"
        queue => "web_log"
        heartbeat => 30
        durable => true
        ssl => true
        #ssl_certificate_path => "/etc/logstash/certs/server.p12"
        #ssl_certificate_password => "password"
 
        add_field => {
            "fld_server_name" => "%{[fields][fld_server_name]}"
            "fld_app_name" => "%{[fields][fld_app_name]}"
            "fld_app_module" => "%{[fields][fld_app_module]}"
            "fld_website_name" => "%{[fields][fld_website_name]}"
            "fld_log_file_name" => "%{source}"
            "fld_logformat" => "%{[fields][fld_logformat]}"
        }
    }
 
}
 
filter { 
 
      if [message] =~ "^#" {
        drop {}
      }
 
      if [fld_logformat] == "logformat__iis_with_xrealip" {
     
          grok {
            match => ["message", "%{TIMESTAMP_ISO8601:log_timestamp} %{IP:serverIP} %{WORD:method} %{NOTSPACE:uriStem} %{NOTSPACE:uriQuery} %{NUMBER:port} %{NOTSPACE:username} %{IPORHOST:clientIP} %{NOTSPACE:userAgent} %{NOTSPACE:referer} %{NUMBER:response} %{NUMBER:subresponse} %{NUMBER:win32response} %{NUMBER:timetaken} %{NOTSPACE:xrealIP} %{NOTSPACE:xforwarderfor}"]
          }
      } else {
   
          grok {
             match => ["message", "%{TIMESTAMP_ISO8601:log_timestamp} %{IP:serverIP} %{WORD:method} %{NOTSPACE:uriStem} %{NOTSPACE:uriQuery} %{NUMBER:port} %{NOTSPACE:username} %{IPORHOST:clientIP} %{NOTSPACE:userAgent} %{NOTSPACE:referer} %{NUMBER:response} %{NUMBER:subresponse} %{NUMBER:win32response} %{NUMBER:timetaken}"]
          }
 
      }
 
      date {
        match => [ "log_timestamp", "YYYY-MM-dd HH:mm:ss" ]
          timezone => "Etc/UTC"
        remove_field => [ "log_timestamp", "@timestamp" ]
        target => [ "log_timestamp2" ]
      }
 
        ruby {
            code => "tstamp = event.get('log_timestamp2').to_i
                        event.set('logdatetime', Time.at(tstamp).strftime('%Y-%m-%d %H:%M:%S'))
                        event.set('logdate', Time.at(tstamp).strftime('%Y-%m-%d'))"
        }
 
      if [bytesSent] {
        ruby {
          code => "event['kilobytesSent'] = event['bytesSent'].to_i / 1024.0"
        }
      }
 
 
      if [bytesReceived] {
        ruby {
          code => "event['kilobytesReceived'] = event['bytesReceived'].to_i / 1024.0"
        }
      }
 
   
        ruby {
            code => "event.set('clientRealIP', event.get('clientIP'))"
        }
        if [xrealIP] {
            ruby {
                code => "event.set('clientRealIP', event.get('xrealIP'))"
            }
        }
        if [xforwarderfor] {
            ruby {
                code => "event.set('clientRealIP', event.get('xforwarderfor'))"
            }
        }
 
      mutate {
        convert => ["bytesSent", "integer"]
        convert => ["bytesReceived", "integer"]
        convert => ["timetaken", "integer"] 
        convert => ["port", "integer"]
 
        add_field => {
            "clientHostname" => "%{clientIP}"
        }
      }
 
        useragent {
            source=> "useragent"
            prefix=> "browser"
        }
 
        kv {
            source => "uriQuery"
            prefix => "uriQuery__"
            allow_duplicate_values => false
            field_split => "&"
            include_keys => [ "utm_medium", "utm_source", "utm_campaign", "utm_term", "utm_content", "yclid", "region" ]
        }
 
        mutate {
            join => { "uriQuery__utm_source" => "," }
            join => { "uriQuery__utm_medium" => "," }
            join => { "uriQuery__utm_campaign" => "," }
            join => { "uriQuery__utm_term" => "," }
            join => { "uriQuery__utm_content" => "," }
            join => { "uriQuery__yclid" => "," }
            join => { "uriQuery__region" => "," }
        }
 
}
 
output { 
  #stdout {codec => rubydebug}
    clickhouse {
      headers => ["Authorization", "Basic abcdsfks..."]
      http_hosts => ["http://127.0.0.1:8123"]
      save_dir => "/etc/logstash/tmp"
      table => "log_web"
      request_tolerance => 1
      flush_size => 10000
      idle_flush_time => 1
        mutations => {
            "fld_log_file_name" => "fld_log_file_name"
            "fld_server_name" => "fld_server_name"
            "fld_app_name" => "fld_app_name"
            "fld_app_module" => "fld_app_module"
            "fld_website_name" => "fld_website_name"
 
            "logdatetime" => "logdatetime"
            "logdate" => "logdate"
            "serverIP" => "serverIP"
            "method" => "method"
            "uriStem" => "uriStem"
            "uriQuery" => "uriQuery"
            "port" => "port"
            "username" => "username"
            "clientIP" => "clientIP"
            "clientRealIP" => "clientRealIP"
            "userAgent" => "userAgent"
            "referer" => "referer"
            "response" => "response"
            "subresponse" => "subresponse"
            "win32response" => "win32response"
            "timetaken" => "timetaken"
             
            "uriQuery__utm_medium" => "uriQuery__utm_medium"
            "uriQuery__utm_source" => "uriQuery__utm_source"
            "uriQuery__utm_campaign" => "uriQuery__utm_campaign"
            "uriQuery__utm_term" => "uriQuery__utm_term"
            "uriQuery__utm_content" => "uriQuery__utm_content"
            "uriQuery__yclid" => "uriQuery__yclid"
            "uriQuery__region" => "uriQuery__region"
        }
    }
 
}

pipelines.yml

# This file is where you define your pipelines. You can define multiple.
# For more information on multiple pipelines, see the documentation:
#   https://www.elastic.co/guide/en/logstash/current/multiple-pipelines.html
 
- pipeline.id: log_web__filebeat_clickhouse
  path.config: "/etc/logstash/log_web__filebeat_clickhouse.conf"

clickhouse. Armazenamento de registros

Os logs de todos os sistemas são armazenados em uma tabela (veja no início do artigo). Destina-se a armazenar informações sobre as solicitações: todos os parâmetros são semelhantes para diferentes formatos, como logs do IIS, apache e logs do nginx. Para logs de aplicativos, nos quais, por exemplo, erros, mensagens informativas, avisos são registrados, uma tabela separada será fornecida com a estrutura apropriada (atualmente em estágio de design).

Ao projetar uma tabela, é muito importante decidir sobre a chave primária (pela qual os dados serão classificados durante o armazenamento). O grau de compactação de dados e a velocidade da consulta dependem disso. No nosso exemplo, a chave é
ORDER BY (fld_app_name, fld_app_module, logdatetime)
Ou seja, pelo nome do sistema, o nome do componente do sistema e a data do evento. Inicialmente, a data do evento veio primeiro. Depois de movê-lo para o último lugar, as consultas começaram a funcionar duas vezes mais rápido. A alteração da chave primária exigirá a recriação da tabela e o recarregamento dos dados para que o ClickHouse reordene os dados no disco. Esta é uma operação pesada, então é uma boa ideia pensar muito sobre o que deve ser incluído na chave de classificação.

Também deve ser observado que o tipo de dados LowCardinality apareceu em versões relativamente recentes. Ao utilizá-lo, o tamanho dos dados compactados é drasticamente reduzido para aqueles campos que possuem baixa cardinalidade (poucas opções).

A versão 19.6 está atualmente em uso e planejamos tentar atualizar para a versão mais recente. Eles têm recursos maravilhosos como Adaptive Granularity, Skipping indexs e o codec DoubleDelta, por exemplo.

Por padrão, durante a instalação, o nível de criação de log é definido como rastreamento. Os logs são girados e arquivados, mas ao mesmo tempo se expandem para um gigabyte. Se não houver necessidade, você pode definir o nível de aviso e o tamanho do log será drasticamente reduzido. A configuração de registro é definida no arquivo config.xml:

<!-- Possible levels: https://github.com/pocoproject/poco/blob/develop/Foundation/include/Poco/Logger. h#L105 -->
<level>warning</level>

Alguns comandos úteis

Поскольку оригинальные пакеты установки собираются по Debian, то для других версий Linux необходимо использовать пакеты собранные компанией Altinity.
 
Вот по этой ссылке есть инструкции с ссылками на их репозиторий: https://www.altinity.com/blog/2017/12/18/logstash-with-clickhouse
sudo yum search clickhouse-server
sudo yum install clickhouse-server.noarch
  
1. проверка статуса
sudo systemctl status clickhouse-server
 
2. остановка сервера
sudo systemctl stop clickhouse-server
 
3. запуск сервера
sudo systemctl start clickhouse-server
 
Запуск для выполнения запросов в многострочном режиме (выполнение после знака ";")
clickhouse-client --multiline
clickhouse-client --multiline --host 127.0.0.1 --password pa55w0rd
clickhouse-client --multiline --host 127.0.0.1 --port 9440 --secure --user default --password pa55w0rd
 
Плагин кликлауза для логстеш в случае ошибки в одной строке сохраняет всю пачку в файл /tmp/log_web_failed.json
Можно вручную исправить этот файл и попробовать залить его в БД вручную:
clickhouse-client --host 127.0.0.1 --password password --query="INSERT INTO log_web FORMAT JSONEachRow" < /tmp/log_web_failed__fixed.json
 
sudo mv /etc/logstash/tmp/log_web_failed.json /etc/logstash/tmp/log_web_failed__fixed.json
sudo chown user_dev /etc/logstash/tmp/log_web_failed__fixed.json
sudo clickhouse-client --host 127.0.0.1 --password password --query="INSERT INTO log_web FORMAT JSONEachRow" < /etc/logstash/tmp/log_web_failed__fixed.json
sudo mv /etc/logstash/tmp/log_web_failed__fixed.json /etc/logstash/tmp/log_web_failed__fixed_.json
 
выход из командной строки
quit;
## Настройка TLS
https://www.altinity.com/blog/2019/3/5/clickhouse-networking-part-2
 
openssl s_client -connect log.domain.com:9440 < /dev/null

logstash. Roteador de log do FileBeat para a fila do RabbitMQ

Este componente é usado para rotear logs vindos do FileBeat para a fila do RabbitMQ. Há dois pontos aqui:

  1. Infelizmente, o FileBeat não possui um plug-in de saída para gravar diretamente no RabbitMQ. E essa funcionalidade, a julgar pelo problema em seu github, não está planejada para implementação. Existe um plugin para o Kafka, mas por algum motivo não podemos usá-lo em casa.
  2. Existem requisitos para coletar logs na DMZ. Com base neles, os logs devem primeiro ser adicionados à fila e, em seguida, o LogStash lê as entradas da fila de fora.

Portanto, é para o caso em que os servidores estão localizados na DMZ que é necessário usar um esquema um pouco complicado. Um exemplo de configuração se parece com isto:

iis_w3c_logs__filebeat_rabbitmq.conf

input {
 
    beats {
        port => 5044
        type => 'iis'
        ssl => true
        ssl_certificate_authorities => ["/etc/pki/tls/certs/app/ca.pem", "/etc/pki/tls/certs/app/ca-issuing.pem"]
        ssl_certificate => "/etc/pki/tls/certs/app/queue.domain.com.cer"
        ssl_key => "/etc/pki/tls/certs/app/queue.domain.com-pkcs8.key"
        ssl_verify_mode => "peer"
    }
 
}
 
output { 
  #stdout {codec => rubydebug}
 
    rabbitmq {
        host => "127.0.0.1"
        port => 5672
        exchange => "monitor.direct"
        exchange_type => "direct"
        key => "%{[fields][fld_app_name]}"
        user => "q-writer"
        password => "password"
        ssl => false
    }
}

RabbitMQ. fila de mensagens

Este componente é usado para armazenar em buffer as entradas de log na DMZ. A gravação é feita através de um monte de Filebeat → LogStash. A leitura é feita de fora da DMZ via LogStash. Ao operar através do RabboitMQ, são processadas cerca de 4 mil mensagens por segundo.

O roteamento de mensagens é configurado pelo nome do sistema, ou seja, com base nos dados de configuração do FileBeat. Todas as mensagens vão para uma fila. Se, por algum motivo, o serviço de enfileiramento for interrompido, isso não levará à perda de mensagens: o FileBeats receberá erros de conexão e suspenderá temporariamente o envio. E o LogStash que lê da fila também receberá erros de rede e aguardará a restauração da conexão. Nesse caso, os dados, é claro, não serão mais gravados no banco de dados.

As seguintes instruções são usadas para criar e configurar filas:

sudo /usr/local/bin/rabbitmqadmin/rabbitmqadmin declare exchange --vhost=/ name=monitor.direct type=direct sudo /usr/local/bin/rabbitmqadmin/rabbitmqadmin declare queue --vhost=/ name=web_log durable=true
sudo /usr/local/bin/rabbitmqadmin/rabbitmqadmin --vhost="/" declare binding source="monitor.direct" destination_type="queue" destination="web_log" routing_key="site1.domain.ru"
sudo /usr/local/bin/rabbitmqadmin/rabbitmqadmin --vhost="/" declare binding source="monitor.direct" destination_type="queue" destination="web_log" routing_key="site2.domain.ru"

Grafana. Painéis

Este componente é usado para visualizar os dados de monitoramento. Nesse caso, você precisa instalar a fonte de dados ClickHouse para o plug-in Grafana 4.6+. Tivemos que ajustá-lo um pouco para melhorar a eficiência do processamento de filtros SQL no painel.

Por exemplo, usamos variáveis, e se elas não estiverem definidas no campo filtro, então gostaríamos que não gerasse uma condição no WHERE do formulário ( uriStem = » AND uriStem != » ). Neste caso, o ClickHouse irá ler a coluna uriStem. Em geral, tentamos diferentes opções e eventualmente corrigimos o plug-in (a macro $valueIfEmpty) para que, no caso de um valor vazio, ele retorne 1, sem mencionar a coluna em si.

E agora você pode usar esta consulta para o gráfico

$columns(response, count(*) c) from $table where $adhoc
and $valueIfEmpty($fld_app_name, 1, fld_app_name = '$fld_app_name')
and $valueIfEmpty($fld_app_module, 1, fld_app_module = '$fld_app_module') and $valueIfEmpty($fld_server_name, 1, fld_server_name = '$fld_server_name') and $valueIfEmpty($uriStem, 1, uriStem like '%$uriStem%')
and $valueIfEmpty($clientRealIP, 1, clientRealIP = '$clientRealIP')

que traduz para este SQL (observe que os campos uriStem vazios foram convertidos para apenas 1)

SELECT
t,
groupArray((response, c)) AS groupArr
FROM (
SELECT
(intDiv(toUInt32(logdatetime), 60) * 60) * 1000 AS t, response,
count(*) AS c FROM default.log_web
WHERE (logdate >= toDate(1565061982)) AND (logdatetime >= toDateTime(1565061982)) AND 1 AND (fld_app_name = 'site1.domain.ru') AND (fld_app_module = 'web') AND 1 AND 1 AND 1
GROUP BY
t, response
ORDER BY
t ASC,
response ASC
)
GROUP BY t ORDER BY t ASC

Conclusão

O surgimento do banco de dados ClickHouse tornou-se um marco no mercado. Era difícil imaginar que, de forma totalmente gratuita, em um instante estivéssemos munidos de uma ferramenta poderosa e prática para trabalhar com big data. Obviamente, com necessidades crescentes (por exemplo, sharding e replicação para vários servidores), o esquema se tornará mais complicado. Mas nas primeiras impressões, trabalhar com esse banco de dados é muito agradável. Percebe-se que o produto é feito "para as pessoas".

Comparado ao ElasticSearch, estima-se que o custo de armazenamento e processamento de logs seja reduzido de cinco a dez vezes. Em outras palavras, se para a quantidade atual de dados teríamos que configurar um cluster de várias máquinas, ao usar o ClickHouse, uma máquina de baixo consumo é suficiente para nós. Sim, claro, o ElasticSearch também possui mecanismos de compactação de dados em disco e outros recursos que podem reduzir significativamente o consumo de recursos, mas em comparação com o ClickHouse, isso será mais caro.

Sem nenhuma otimização especial de nossa parte, nas configurações padrão, o carregamento de dados e a seleção do banco de dados funcionam em uma velocidade incrível. Ainda não temos muitos dados (cerca de 200 milhões de registros), mas o servidor em si é fraco. Podemos usar essa ferramenta no futuro para outras finalidades não relacionadas ao armazenamento de logs. Por exemplo, para análise de ponta a ponta, no campo da segurança, aprendizado de máquina.

No final, um pouco sobre os prós e contras.

Contras

  1. Carregando registros em grandes lotes. Por um lado, esse é um recurso, mas você ainda precisa usar componentes adicionais para armazenar registros em buffer. Esta tarefa nem sempre é fácil, mas ainda solucionável. E eu gostaria de simplificar o esquema.
  2. Algumas funcionalidades exóticas ou novos recursos geralmente são interrompidos em novas versões. Isso causa preocupação, diminuindo o desejo de atualizar para uma nova versão. Por exemplo, o mecanismo de tabela Kafka é um recurso muito útil que permite ler eventos diretamente do Kafka, sem implementar consumidores. Mas, a julgar pelo número de problemas no github, ainda tomamos cuidado para não usar esse mecanismo em produção. No entanto, se você não fizer gestos bruscos para o lado e usar a funcionalidade principal, ele funcionará de forma estável.

Prós

  1. Não desacelera.
  2. Limite de entrada baixo.
  3. Código aberto.
  4. Livre.
  5. Escala bem (sharding/replicação pronta para uso)
  6. Incluído no registro de software russo recomendado pelo Ministério das Comunicações.
  7. A presença de suporte oficial do Yandex.

Fonte: habr.com

Adicionar um comentário