Banco de Dados ClickHouse para Pessoas, ou Tecnologias de Alienígenas

Alexey Lizunov, gerente da área do centro de competências de canais de atendimento remoto da diretoria de tecnologias da informação da MKB

Banco de Dados ClickHouse para Pessoas, ou Tecnologias de Alienígenas

Como alternativa ao stack ELK (ElasticSearch, Logstash, Kibana), estamos realizando pesquisas sobre o uso do banco de dados ClickHouse como armazenamento de dados para logs.

Neste artigo, gostaríamos de compartilhar nossa experiência com o uso do banco de dados ClickHouse e os resultados preliminares após a implementação piloto. É importante notar desde o início que os resultados foram impressionantes.


Banco de Dados ClickHouse para Pessoas, ou Tecnologias de Alienígenas

A seguir, descreveremos com mais detalhes como nosso sistema está configurado e de quais componentes ele é composto. Mas agora gostaríamos de falar um pouco sobre este banco de dados em geral e por que ele merece sua atenção. O banco de dados ClickHouse é um banco de dados analítico em coluna de alta performance desenvolvido pela Yandex. É utilizado nos serviços da Yandex, sendo originalmente o armazenamento principal de dados para o Yandex.Metrica. O sistema é open-source e gratuito. Do ponto de vista do desenvolvedor, sempre me interessei por como isso é implementado, pois lidam com volumes de dados fantastamente grandes. E a interface do usuário do Metrica é muito flexível e rápida. Na primeira vez que conheci este banco de dados, a impressão foi: “Finalmente! Feito ‘para as pessoas’! Desde o processo de instalação até o envio de consultas.”

Este banco de dados tem um limiar de entrada muito baixo. Mesmo um desenvolvedor de média habilidade pode instalar este banco de dados em alguns minutos e começar a usá-lo. Tudo funciona perfeitamente. Mesmo pessoas que não estão familiarizadas com Linux podem gerenciar a instalação e realizar operações simples de forma rápida. Se antes, quando se falava em Big Data, Hadoop, Google BigTable, HDFS, a imagem que um desenvolvedor comum tinha era de que se tratava de terabytes e petabytes, e que as configurações e desenvolvimento dessas sistemas eram feitas por super-humanos, agora com o surgimento do banco de dados ClickHouse temos uma ferramenta simples e compreensível que nos permite resolver um conjunto de tarefas que antes era inacessível. Basta uma máquina bastante comum e cinco minutos para a instalação. Ou seja, temos um banco de dados como o MySql, mas apenas para armazenar bilhões de registros! Uma espécie de super-arquivador com linguagem SQL. É como se as pessoas recebessem armas de alienígenas.

Sobre nosso sistema de coleta de logs

Para coletar informações, utilizam-se arquivos de log do IIS em formato padrão (atualmente também estamos fazendo a análise dos logs de aplicativos, mas o objetivo principal nesta fase de teste é a coleta dos logs do IIS).

Não conseguimos abandonar completamente a pilha ELK devido a várias razões, e continuamos utilizando os componentes LogStash e Filebeat, que se mostraram eficazes e funcionam de maneira bastante confiável e previsível.

O esquema geral de logging é apresentado na figura abaixo:

Banco de Dados ClickHouse para Pessoas, ou Tecnologias de Alienígenas

Uma característica da gravação de dados no banco de dados ClickHouse é a inserção rara (uma vez por segundo) de registros em grandes lotes. Essa, aparentemente, é a parte mais "problemática" que se enfrenta na primeira experiência com o banco de dados ClickHouse: o esquema é um pouco mais complexo.
Aqui, o plugin para LogStash que insere dados diretamente no ClickHouse ajudou bastante. Este componente é implantado no mesmo servidor que o banco de dados. Na verdade, isso não é recomendado, mas, do ponto de vista prático, para não criar servidores separados, enquanto está implantado no mesmo servidor. Não observamos falhas nem conflitos de recursos com o banco de dados. Além disso, é necessário notar que o plugin possui um mecanismo de repetição em caso de erros. Em caso de falhas, o plugin grava no disco um lote de dados que não pôde ser inserido (o formato do arquivo é conveniente: após a correção, é fácil inserir o lote corrigido com a ajuda do clickhouse-client).

A lista completa de softwares utilizados no esquema é apresentada na tabela:

Lista de softwares utilizados

Nome

Descrição

Link para o distribuidor

NGINX

Reverse-proxy para limitar o acesso por portas e organizar a autenticação

Atualmente não é utilizado no esquema

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

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

FileBeat

Transmissão de logs de arquivos.

https://www.elastic.co/downloads/beats/filebeat (distribuidor para Windows 64bit).

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

LogStash

Coletor de logs.

Utilizado para coletar logs do FileBeat e também para coletar logs da fila RabbitMQ (para os 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

Plugin Logstash para transmissão de logs para o banco de dados ClickHouse em lotes

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

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

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

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

ClickHouse

Armazenamento de logs 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

Nota. Desde agosto de 2018, apareceram no repositório do Yandex assemblagens "normais" .rpm para RHEL, então pode-se tentar usá-las. No momento da instalação, utilizamos pacotes construídos pela Altinity.

Grafana

Visualização de logs. Configuração de dashboards

https://grafana.com/

https://grafana.com/grafana/download

Redhat & CentOS (64 Bit) – versão mais recente

Fonte de dados ClickHouse para Grafana 4.6+

Plugin para Grafana com a 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 logs do FileBeat para a fila RabbitMQ.

Nota. Infelizmente, o FileBeat não possui saída direta para o RabbitMQ, portanto, é necessário um elo intermediário na forma do Logstash.

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

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

RabbitMQ

Fila de mensagens. Este é um buffer de entradas de logs 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)

Ambiente de execução Erlang. Requerido para operação do RabbitMQ.

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

O valor

Nota

Configuração

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

É necessário observar as dicas para operação do banco de dados ClickHouse (https://clickhouse.yandex/docs/ru/operations/tips/)

Software de sistema geral

SO: Red Hat Enterprise Linux Server (Maipo)

JRE (Java 8)

 

Como pode ser visto, é uma estação de trabalho comum.

A estrutura da tabela para armazenar 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 os valores padrão para particionamento (por meses) e granularidade do índice. Todos os campos correspondem praticamente às entradas do log do IIS para o registro de solicitações HTTP. Ressaltamos campos separados para armazenamento de rótulos UTM (eles são analisados na fase de inserção na tabela a partir do campo da string de consulta).

Além disso, a tabela contém vários campos de sistema para armazenar informações sobre sistemas, componentes e servidores. A descrição desses campos pode ser encontrada na tabela abaixo. Em uma única tabela, armazenamos logs de vários sistemas.

Nome

Descrição

Exemplo

fld_app_name

Nome do aplicativo/sistema
Valores permitidos:

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

site1.domain.com

fld_app_module

Módulo do sistema
Valores permitidos:

  • web — Site
  • svc — Serviço web do site
  • intgr — Serviço web de integração
  • bo — Admin (BackOffice)

web

fld_website_name

Nome do site no IIS

Em um único servidor, podem ser implementados vários sistemas, ou mesmo várias instâncias de um único módulo do sistema.

web-main

fld_server_name

Nome do servidor

web1.domain.com

fld_log_file_name

Caminho para o arquivo de log no servidor

C:inetpublogsLogFiles
W3SVC1u_ex190711.log

Isso permite criar gráficos de forma eficaz no Grafana. Por exemplo, visualizar as consultas do frontend de um sistema específico. Isso é semelhante a um contador de site no Yandex.Metrica.

Aqui estão algumas estatísticas sobre o uso do banco de dados nos últimos 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.)

Tamanho dos dados em 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 compressão dos dados nas colunas

SELECIONE
    nome,
    formatReadableSize(data_uncompressed_bytes) AS não_comprimido,
    formatReadableSize(data_compressed_bytes) AS comprimido,
    data_uncompressed_bytes / data_compressed_bytes AS taxa_de_compressão
FROM system.columns
WHERE table = 'log_web'
 
┌─nome────────────────────┬─não_comprimido─┬─comprimido─┬─────taxa_de_compressão────┐
│ 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 linhas no conjunto. Tempo decorrido: 0.005 seg.

Descrição dos componentes utilizados

FileBeat. Transmissão de logs de arquivos

Este componente monitora alterações em arquivos de log no disco e transmite as informações para o LogStash. É instalado em todos os servidores onde os arquivos de log são gerados (geralmente, IIS). Funciona no modo tail (ou seja, transmite apenas os registros adicionados ao arquivo). Mas também pode ser configurado para transmitir arquivos inteiros. Isso é conveniente quando é necessário carregar dados dos meses anteriores. Basta colocar o arquivo de log na pasta e ele será lido completamente.

Quando o serviço for interrompido, os dados param de ser enviados para o armazenamento.

Um exemplo de configuração é o seguinte:

filebeat.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 logs

Este componente é projetado para receber registros de log do FileBeat (ou através da fila RabbitMQ), fazer a análise e inserir em lote no banco de dados ClickHouse.

Para inserção no ClickHouse, é utilizado o plugin Logstash-output-clickhouse. O Logstash possui um mecanismo de retry para requisições, mas durante uma parada programada, é melhor interromper o próprio serviço. Durante a parada, as mensagens se acumularão na fila RabbitMQ, portanto, se a interrupção for por um longo período, é melhor parar os Filebeats nos servidores. Na configuração onde o RabbitMQ não é utilizado (na rede local, o Filebeat envia logs diretamente para o Logstash), os Filebeats funcionam de forma aceitável e segura, portanto, para eles, a indisponibilidade da saída não resulta em consequências.

Um exemplo de configuração é o seguinte:

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 logs

Os logs de todos os sistemas são armazenados em uma única tabela (veja no início do artigo). Ela se destina a armazenar informações sobre as solicitações: todos os parâmetros são semelhantes para diferentes formatos, como logs do IIS, logs do Apache e Nginx. Para logs de aplicações, onde são registrados, por exemplo, erros, mensagens informativas e avisos, haverá uma tabela separada, com a estrutura adequada (atualmente em fase de projeto).

Ao projetar a tabela, é muito importante definir a chave primária (com a qual os dados serão ordenados ao serem armazenados). Isso afeta o grau de compressão dos dados e a velocidade das consultas. No nosso exemplo, a chave é
ORDER BY (fld_app_name, fld_app_module, logdatetime)
Ou seja, pelo nome do sistema, nome do componente do sistema e data do evento. Inicialmente, a data do evento estava em primeiro lugar. Após movê-la para o último lugar, as consultas passaram a funcionar aproximadamente duas vezes mais rápido. Alterar a chave primária exigirá a recriação da tabela e o reenviado dos dados para que o ClickHouse reorganize os dados no disco. Esta é uma operação intensa, portanto, é desejável planejar cuidadosamente o que deve estar incluído na chave de ordenação.

Também é necessário observar que, nas versões mais recentes, apareceu o tipo de dado LowCardinality. Ao usá-lo, há uma redução acentuada no tamanho dos dados comprimidos para aqueles campos que possuem baixa cardinalidade (poucas opções).

Atualmente, está sendo utilizada a versão 19.6, e planejamos tentar atualizar para a última versão. Nelas surgiram funcionalidades incríveis, como Granularidade Adaptativa, índices de Omissão e o codec DoubleDelta, por exemplo.

Por padrão, ao instalar, o nível de registro na configuração está definido como trace. Os logs são rotacionados e arquivados, mas se expandem até um gigabyte. Se não houver necessidade, pode-se definir o nível como warning, reduzindo consideravelmente o tamanho do log. A configuração do logging é definida no arquivo config.xml:

<!-- Níveis possíveis: https://github.com/pocoproject/poco/blob/develop/Foundation/include/Poco/Logger.h#L105 -->
<level>warning</level>

Alguns comandos úteis

Como os pacotes de instalação originais são construídos com base no Debian, para outras versões do Linux, é necessário usar pacotes criados pela Altinity.

Aqui está o link com instruções e links para o repositório deles: https://www.altinity.com/blog/2017/12/18/logstash-with-clickhouse
sudo yum search clickhouse-server
sudo yum install clickhouse-server.noarch

1. verificar o status
sudo systemctl status clickhouse-server

2. parar o servidor
sudo systemctl stop clickhouse-server

3. iniciar o servidor
sudo systemctl start clickhouse-server

Iniciar para executar consultas em modo multilinha (executar após o sinal ";")
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

O plugin ClickHouse para Logstash, em caso de erro em uma linha, salva todo o lote no arquivo /tmp/log_web_failed.json
Você pode corrigir manualmente este arquivo e tentar carregá-lo no banco de dados manualmente:
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

sair do terminal
quit;
## Configurando o TLS
https://www.altinity.com/blog/2019/3/5/clickhouse-networking-part-2

openssl s_client -connect log.domain.com:9440 < /dev/null

LogStash. O roteador de logs do FileBeat para a fila RabbitMQ

Este componente é usado para roteamento de logs que chegam do FileBeat para a fila RabbitMQ. Aqui estão dois pontos:

  1. Infelizmente, o FileBeat não possui um plugin de saída para gravação diretamente no RabbitMQ. E tal funcionalidade, segundo as questões no GitHub deles, não está planejada. Há um plugin para Kafka, mas por razões específicas não podemos usá-lo.
  2. Há requisitos para coleta de logs na DMZ. De acordo com esses requisitos, os logs devem primeiro ser armazenados em uma fila e, em seguida, o LogStash lê registros da fila externamente.

Portanto, para o caso de servidores localizados na DMZ, é necessário utilizar um esquema um pouco mais complexo. Um exemplo de configuração é o seguinte:

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 é utilizado para bufferizar registros de logs na DMZ. A gravação é realizada através da combinação Filebeat → LogStash. A leitura é feita externamente à DMZ via LogStash. Ao operar através do RabbitMQ, cerca de 4 mil mensagens são processadas por segundo.

O roteamento das mensagens é configurado pelo nome do sistema, ou seja, com base nas configurações do FileBeat. Todas as mensagens vão para uma única fila. Se, por algum motivo, o serviço de filas for interrompido, isso não resultará na perda de mensagens: os FileBeats receberão erros de conexão e suspenderão temporariamente o envio. O LogStash, que lê da fila, também receberá erros de rede e aguardará a restauração da conexão. Os dados, claro, pararão de ser gravados no banco de dados.

As seguintes instruções são usadas para criar e configurar as 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 é utilizado para visualizar dados de monitoramento. Para isso, é necessário instalar o plugin ClickHouse datasource for Grafana 4.6+. Tivemos que ajustá-lo um pouco para aumentar a eficiência do processamento de filtros SQL no painel.

Por exemplo, utilizamos variáveis e, se não estiverem definidas no campo do filtro, gostaríamos que ele não gerasse uma condição no WHERE do tipo ( uriStem = » AND uriStem != » ). Nesse caso, o ClickHouse lerá a coluna uriStem. No geral, experimentamos várias opções e, no final, ajustamos o plugin (macro $valueIfEmpty) para que, em caso de valor vazio, retornasse 1, sem mencionar a própria coluna.

E agora é possível usar uma consulta como esta 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 é convertido para este SQL (note que os campos vazios uriStem foram transformados em 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 foi um evento marcante no mercado. Era difícil imaginar que, de forma absolutamente gratuita, nós teríamos em mãos uma poderosa e prática ferramenta para trabalhar com grandes volumes de dados. Sem dúvida, à medida que as necessidades aumentam (por exemplo, sharding e replicação em vários servidores), o esquema se tornará mais complexo. Mas pelas primeiras impressões, trabalhar com este banco de dados é muito agradável. É evidente que o produto foi feito "para as pessoas".

Comparado ao ElasticSearch, os custos de armazenamento e processamento de logs, segundo estimativas preliminares, são reduzidos de cinco a dez vezes. Em outras palavras, se para o atual volume de dados precisaríamos configurar um cluster de várias máquinas, ao usar o ClickHouse, um único servidor de baixo desempenho é suficiente. Sim, claro, o ElasticSearch também possui mecanismos de compressão de dados em disco e outras funcionalidades que permitem reduzir significativamente o consumo de recursos, mas em comparação com o ClickHouse, isso exigirá maiores gastos.

Sem quaisquer otimizações especiais da nossa parte, com as configurações padrão, o carregamento de dados e as consultas ao banco de dados funcionam com uma velocidade impressionante. Por enquanto, temos um volume moderado de dados (cerca de 200 milhões de registros), mas o servidor é fraco. Este ferramenta podemos usar futuramente para outros objetivos, não apenas para armazenamento de logs. Por exemplo, para análise holística, na área de segurança e aprendizado de máquina.

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

Desvantagens

  1. Carregamento de registros em grandes lotes. Isso, por um lado, é um recurso, mas é necessário utilizar componentes adicionais para bufferizar os registros. Essa tarefa nem sempre é simples, mas é solucionável. E gostaríamos de simplificar o esquema.
  2. Algumas funcionalidades exóticas ou novos recursos frequentemente falham em novas versões. Isso gera preocupações, diminuindo o desejo de atualizar para a nova versão. Por exemplo, o mecanismo de tabelas Kafka – um recurso muito útil que permite ler eventos diretamente do Kafka, sem a necessidade de implementar consumidores. Mas, com base na quantidade de problemas no GitHub, ainda estamos cautelosos em usar esse mecanismo em produção. No entanto, se não fizermos mudanças bruscas e utilizarmos a funcionalidade principal, ele opera de forma estável.

Prós

  1. Não trava.
  2. Baixa barreira de entrada.
  3. Código aberto.
  4. Gratuito.
  5. Escalabilidade adequada (sharding/reduplicação "fora da caixa").
  6. Está listado no registro de software russo recomendado pelo Ministério das Comunicações.
  7. A presença de suporte oficial do Yandex.

Fonte: habr.com

Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS 🔥 Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS | ProHoster