Database ClickHouse per le persone, o Tecnologie degli alieni

Aleksej Lizunov, responsabile del centro competenze per i canali di servizio a distanza della direzione tecnologie informative MKB

Database ClickHouse per le persone, o Tecnologie degli alieni

Come alternativa allo stack ELK (ElasticSearch, Logstash, Kibana), stiamo conducendo studi sull'uso del database ClickHouse come archivio dati per i log.

In questo articolo vorremmo condividere la nostra esperienza nell'utilizzo del database ClickHouse e i risultati preliminari dopo la sperimentazione pilota. È opportuno notare subito che i risultati sono stati impressionanti.


Database ClickHouse per le persone, o Tecnologie degli alieni

In seguito descriveremo più nel dettaglio come abbiamo impostato il sistema e quali componenti lo compongono. Ma ora vorremmo raccontare un po' di questo database in generale e perché merita attenzione. Il database ClickHouse è un database colonnare analitico ad alte prestazioni sviluppato da Yandex. È utilizzato nei servizi di Yandex, essendo originariamente l'archivio di dati principale per Yandex.Metrica. È un sistema open-source, gratuito. Dal punto di vista di un sviluppatore, mi è sempre interessato sapere come abbiano realizzato questo sistema, dato che gestiscono dati fantastici in dimensioni. E l'interfaccia utente di Metrica è molto flessibile e veloce. Alla prima conoscenza di questo database, l'impressione è stata: “Finalmente! È stato fatto 'per le persone'! A partire dal processo di installazione fino all'invio delle query.”

Questo database ha una soglia di ingresso molto bassa. Anche uno sviluppatore di media competenza può installare questo database in pochi minuti e iniziare ad utilizzarlo. Tutto funziona in modo preciso. Anche le persone che hanno poca familiarità con Linux riescono rapidamente a gestire l'installazione e a compiere operazioni semplici. Se in passato, alla parola Big Data, Hadoop, Google BigTable, HDFS, un normale sviluppatore si immaginava un discorso su terabyte, petabyte, e che le configurazioni e lo sviluppo per questi sistemi fossero compiti per superuomini, con l'arrivo del database ClickHouse abbiamo ottenuto uno strumento semplice e chiaro per risolvere un insieme di compiti fino a quel momento inaccessibile. È sufficiente una macchina relativamente media e cinque minuti per l'installazione. Quindi abbiamo ottenuto un database come, ad esempio, MySql, ma specificamente per la memorizzazione di miliardi di record! Una sorta di super archiviatore con linguaggio SQL. È come se fosse stato dato alle persone l'arma di extraterrestri.

Sul nostro sistema di raccolta dei log

Per la raccolta delle informazioni vengono utilizzati file di log IIS delle applicazioni web in formato standard (attualmente stiamo anche lavorando al parsing dei log delle applicazioni, ma l'obiettivo principale in questa fase pilot è la raccolta dei log IIS).

Non siamo riusciti, per vari motivi, a rinunciare completamente al stack ELK, e continuiamo a utilizzare i componenti LogStash e Filebeat, che hanno dimostrato di funzionare bene e sono piuttosto affidabili e prevedibili.

Lo schema generale di logging è illustrato nell'immagine sottostante:

Database ClickHouse per le persone, o Tecnologie degli alieni

Una caratteristica della registrazione dei dati nel database ClickHouse è l'inserimento sporadico (una volta al secondo) di record in grandi blocchi. Questa sembra essere la parte più "problemativa" che si incontra durante la prima esperienza con il database ClickHouse: lo schema diventa leggermente più complesso.
Qui ci ha molto aiutato un plugin per LogStash, che inserisce direttamente i dati in ClickHouse. Questo componente viene implementato sullo stesso server del database. In generale, ciò non è raccomandato, ma dal punto di vista pratico, per non dover gestire server separati, ora è distribuito sullo stesso server. Non abbiamo riscontrato né crash né conflitti di risorse con il database. Inoltre, va notato che il plugin prevede un meccanismo di retry in caso di errori. In caso di errori, il plugin scrive su disco un blocco di dati che non è riuscito a inserire (il formato del file è comodo: dopo la correzione, si può facilmente reinserire il blocco corretto tramite clickhouse-client).

L'elenco completo del software utilizzato nello schema è presentato nella tabella:

Elenco del software utilizzato

Nome

Descrizione

Link al pacchetto

NGINX

Reverse-proxy per limitare l'accesso alle porte e organizzare l'autenticazione

Attualmente non utilizzato nello schema

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

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

FileBeat

Trasmissione di file di log.

https://www.elastic.co/downloads/beats/filebeat (pacchetto per Windows 64bit).

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

LogStash

Raccoglitore di log.

Utilizzato per raccogliere log da FileBeat e anche per raccogliere log dalla coda RabbitMQ (per i server che si trovano in DMZ).

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

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

Logstash-output-clickhouse

Plugin Logstash per l'invio di log al database ClickHouse in blocchi

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

Archiviazione dei log 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. A partire da agosto 2018, nel repository di Yandex sono apparse build "normali" rpm per RHEL, quindi è possibile provare a utilizzare queste. Al momento dell'installazione, stavamo utilizzando pacchetti costruiti da Altinity.

Grafana

Visualizzazione dei log. Configurazione dei dashboard

https://grafana.com/

https://grafana.com/grafana/download

Redhat & Centos(64 Bit) – ultima versione

ClickHouse datasource per Grafana 4.6+

Plugin per Grafana con sorgente dati ClickHouse

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

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

LogStash

Router dei log da FileBeat alla coda RabbitMQ.

Nota. Sfortunatamente, FileBeat non ha un output diretto per RabbitMQ, quindi è necessario un anello intermedio sotto forma di Logstash.

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

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

RabbitMQ

Coda messaggi. Questo è un buffer di registrazioni dei log nella 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 (Necessario per RabbitMQ)

Ambiente di esecuzione Erlang. Necessario per il funzionamento di RabbitMQ.

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

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

La configurazione del server con il database ClickHouse è presentata nella seguente tabella:

Nome

Valore

Nota

Configurazione

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

È necessario prestare attenzione ai consigli per l'uso del database ClickHouse (https://clickhouse.yandex/docs/ru/operations/tips/)

Software di sistema comune

OS: Red Hat Enterprise Linux Server (Maipo)

JRE (Java 8)

 

Come si vede, è una normale workstation.

La struttura della tabella per la registrazione dei log appare come segue:

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;

Utilizziamo i valori predefiniti per il partizionamento (per mesi) e la granularità dell'indice. Tutti i campi corrispondono praticamente alle registrazioni del log IIS per la registrazione delle richieste http. Si noti separatamente i campi per la memorizzazione delle etichette utm (queste vengono analizzate durante l'inserimento nella tabella dal campo della stringa di query).

Sono stati inoltre aggiunti diversi campi di sistema per memorizzare informazioni sui sistemi, componenti, server. La descrizione di questi campi è riportata di seguito nella tabella. In una tabella memorizziamo i log di diversi sistemi.

Nome

Descrizione

Esempio

fld_app_name

Nome dell'applicazione/sistema
Valori consentiti:

  • site1.domain.com Sito esterno 1
  • site2.domain.com Sito esterno 2
  • internal-site1.domain.local Sito interno 1

site1.domain.com

fld_app_module

Modulo di sistema
Valori consentiti:

  • web — Sito web
  • svc — Servizio web del sito
  • intgr — Servizio web di integrazione
  • bo — Admin (BackOffice)

web

fld_website_name

Nome del sito in IIS

Su un server possono essere distribuiti diversi sistemi, o anche più istanze di un modulo di sistema.

web-main

fld_server_name

Nome del server

web1.domain.com

fld_log_file_name

Percorso al file di log sul server

C:\inetpub\logs\LogFiles
W3SVC1u_ex190711.log

Questo consente di costruire grafici in modo efficace in Grafana. Ad esempio, visualizzare le query dal frontend di un sistema specifico. È simile a un contatore di siti in Yandex.Metrica.

Ecco alcune statistiche sull'utilizzo del database negli ultimi due mesi.

Numero di record suddivisi per sistemi e relativi componenti

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 │
└──────────────────┴────────────────┴────────────┘
 
Totali:
┌─fld_app_name─┬─fld_app_module─┬─rows_count─┐
│              │                │  210522593 │
└──────────────┴────────────────┴────────────┘
 
11 righe nel set. Tempo trascorso: 4.874 sec. Elaborate 210,52 milioni di righe, 421,67 MB (43,19 milioni di righe/s, 86,51 MB/s).

Volume dei dati su 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 riga nel set. Tempo trascorso: 0.035 sec.

Grado di compressione dei dati nelle colonne

SELEZIONA
    nome,
    formatReadableSize(data_uncompressed_bytes) COME uncompressed,
    formatReadableSize(data_compressed_bytes) COME compresso,
    data_uncompressed_bytes / data_compressed_bytes COME compress_ratio
DA system.columns
DOVE table = 'log_web'
 
┌─nome────────────────────┬─uncompressed─┬─compresso───┬─────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 righe nel set. Trascorso: 0.005 sec.

Descrizione dei componenti utilizzati

FileBeat. Trasmissione dei log dei file

Questo componente monitora le modifiche nei file di log sul disco e invia le informazioni a LogStash. Si installa su tutti i server dove vengono scritti file di log (di solito, IIS). Funziona in modalità tail (cioè, invia solo le registrazioni aggiunte al file). Tuttavia, può essere impostato per inviare file interi. Questo è comodo quando è necessario caricare dati dei mesi precedenti. Basta mettere il file di log nella cartella e lo leggerà interamente.

Quando il servizio viene fermato, i dati smettono di essere trasmessi ulteriormente nel deposito.

Un esempio di configurazione appare come segue:

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. Raccolta log

Questo componente è progettato per ricevere registri dai log di FileBeat (o tramite la coda RabbitMQ), eseguire il parsing e inserire i dati a blocchi nel database ClickHouse.

Per l'inserimento in ClickHouse, si utilizza il plugin Logstash-output-clickhouse. Il plugin Logstash ha un meccanismo di retry per le richieste, ma durante una fermata programmata, è comunque meglio fermare il servizio stesso. Durante l'arresto, i messaggi si accumuleranno nella coda RabbitMQ, quindi se l'arresto è prolungato, è meglio fermare i Filebeat sui server. Nello schema in cui non si utilizza RabbitMQ (nella rete locale Filebeat invia direttamente i log a Logstash), i Filebeat funzionano in modo accettabile e sicuro, quindi per loro l'indisponibilità dell'output avviene senza conseguenze.

Un esempio di configurazione appare come segue:

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. Archivio log

I log di tutti i sistemi vengono salvati in un'unica tabella (vedi all'inizio dell'articolo). È destinata a memorizzare informazioni sulle richieste: tutti i parametri sono simili per diversi formati, ad esempio i log IIS, i log di apache e nginx. Per i log delle applicazioni, in cui vengono registrati, ad esempio, errori, messaggi informativi, avvisi, sarà prevista una tabella separata, con la struttura corrispondente (attualmente in fase di progettazione).

Nella progettazione della tabella è molto importante definire la chiave primaria (sulla quale i dati saranno ordinati durante la memorizzazione). Questo influisce sul grado di compressione dei dati e sulla velocità delle query. Nel nostro esempio, la chiave è
ORDER BY (fld_app_name, fld_app_module, logdatetime)
Cioè, in base al nome del sistema, al nome del componente del sistema e alla data dell'evento. Inizialmente, la data dell'evento era al primo posto. Dopo averla spostata all'ultimo posto, le query sono diventate circa il doppio più veloci. Modificare la chiave primaria richiederà di ricreare la tabella e ricaricare i dati, in modo che ClickHouse riordini i dati su disco. Questa è un'operazione pesante, quindi è consigliabile pianificare con largo anticipo quali dovrebbero essere inclusi nella chiave di ordinamento.

È inoltre importante notare che nelle ultime versioni è apparso il tipo di dati LowCardinality. Usandolo, si riduce drasticamente la dimensione dei dati compressi per quei campi che hanno una bassa cardinalità (poche opzioni).

Attualmente è in uso la versione 19.6 e prevediamo di provare ad aggiornare alla versione più recente. In queste versioni sono state introdotte funzionalità interessanti come la Granularità Adattiva, indici di Skipping e il codec DoubleDelta, per esempio.

Per impostazione predefinita, durante l'installazione, a livello di configurazione, è impostato il livello di registrazione trace. I log vengono ruotati e archiviati, ma si espandono fino a un gigabyte. Se non è necessario, si può impostare il livello warning, in tal caso la dimensione del log si riduce drasticamente. La configurazione della registrazione è definita nel file config.xml:


warning

Alcuni comandi utili

Poiché i pacchetti di installazione originali vengono assemblati per Debian, per altre versioni di Linux è necessario utilizzare pacchetti assemblati da Altinity. 

Qui puoi trovare istruzioni con collegamenti al loro repository: https://www.altinity.com/blog/2017/12/18/logstash-with-clickhouse
sudo yum search clickhouse-server
sudo yum install clickhouse-server.noarch

1. verifica dello stato
sudo systemctl status clickhouse-server

2. arresto del server
sudo systemctl stop clickhouse-server

3. avvio del server
sudo systemctl start clickhouse-server

Avvio per eseguire query in modalità multilinea (esecuzione dopo il punto e virgola ";")
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

Il plugin ClickHouse per LogStash in caso di errore su una linea salva l'intero pacchetto in un file /tmp/log_web_failed.json
Puoi correggere manualmente questo file e provare a caricarlo nel DB 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

uscita dalla riga di comando
quit;
## Configurazione TLS
https://www.altinity.com/blog/2019/3/5/clickhouse-networking-part-2

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

LogStash. Il router dei log da FileBeat alla coda RabbitMQ

Questo componente viene utilizzato per instradare i log provenienti da FileBeat alla coda RabbitMQ. Ci sono due aspetti qui:

  1. Sfortunatamente, FileBeat non ha un plugin di output per scrivere direttamente in RabbitMQ. E questa funzionalità, a giudicare dai problemi sul loro GitHub, non è prevista per il futuro. Esiste un plugin per Kafka, ma per motivi specifici non possiamo usarlo.
  2. Ci sono requisiti per la raccolta dei log nella DMZ. In base a questi, i log devono prima essere inseriti in una coda e poi LogStash legga esternamente dalle registrazioni della coda.

Pertanto, nel caso in cui i server si trovino nella DMZ, è necessario utilizzare uno schema leggermente più complesso. Un esempio di configurazione potrebbe apparire come segue:

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. Coda di messaggi

Questo componente viene utilizzato per il buffering delle registrazioni dei log nella DMZ. La registrazione avviene attraverso la combinazione Filebeat → LogStash. La lettura avviene dall'esterno della DMZ tramite LogStash. Quando si utilizza RabbitMQ, vengono elaborate circa 4.000 messaggi al secondo.

Il routing dei messaggi è impostato in base al nome del sistema, cioè sulla base dei dati di configurazione di FileBeat. Tutti i messaggi vanno a finire in un'unica coda. Se per qualche motivo il servizio delle code viene fermato, questo non comporterà la perdita di messaggi: i FileBeat riceveranno errori di connessione e interromperanno temporaneamente l'invio. Anche LogStash, che legge dalla coda, riceverà errori di rete e attenderà il ripristino della connessione. I dati in questo caso cesseranno ovviamente di essere scritti nel database.

Le seguenti istruzioni vengono utilizzate per creare e configurare le code:

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. Dashboard

Questo componente è utilizzato per la visualizzazione dei dati di monitoraggio. È necessario installare il plugin ClickHouse datasource per Grafana 4.6+. Abbiamo dovuto modificarlo un po' per migliorare l'efficienza dell'elaborazione dei filtri SQL nel dashboard.

Ad esempio, utilizziamo variabili e se non sono impostate nel campo filtro, vorremmo che non generasse una condizione nel WHERE di tipo (uriStem = » AND uriStem != »). In tal caso, ClickHouse leggerà la colonna uriStem. In generale, abbiamo provato diverse opzioni e alla fine abbiamo modificato il plugin (macro $valueIfEmpty), in modo che nel caso di un valore vuoto restituisca 1, senza menzionare la colonna stessa.

E ora puoi utilizzare una query del genere per il grafico

$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')

che si trasforma in questa SQL (si noti che i campi vuoti uriStem sono stati trasformati in semplicemente 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

Conclusione

L'arrivo del database ClickHouse è stato un evento significativo nel mercato. Era difficile immaginare che, del tutto gratuitamente, ci siamo dotati di uno strumento potente e pratico per lavorare con grandi volumi di dati. Certamente, con l'aumentare delle esigenze (ad esempio, sharding e replica su più server), lo schema diventerà più complesso. Ma dalle prime impressioni, lavorare con questo database è molto piacevole. È evidente che il prodotto è stato realizzato 'per le persone'.

Rispetto a ElasticSearch, i costi per la memorizzazione e l'elaborazione dei log, secondo le stime preliminari, si riducono da cinque a dieci volte. In altre parole, se per l'attuale volume di dati avremmo dovuto configurare un cluster di più macchine, utilizzando ClickHouse ci basta una sola macchina di modeste dimensioni. Sì, è ovvio che in ElasticSearch ci sono anche meccanismi di compressione dei dati su disco e altre funzionalità che consentono di ridurre significativamente il consumo di risorse, ma a confronto con ClickHouse, ciò richiederà costi maggiori.

Senza alcuna ottimizzazione speciale da parte nostra, con le impostazioni predefinite, il caricamento dei dati e le query dal database funzionano con un'incredibile rapidità. Attualmente, abbiamo pochi dati (circa 200 milioni di record), ma il server è debole. Questo strumento possiamo in futuro utilizzarlo anche per altri scopi, non legati alla memorizzazione dei log. Ad esempio, per analisi trasversali, nel campo della sicurezza, dell'apprendimento automatico.

Alla fine, un po' sui lati negativi e positivi.

Contro

  1. Caricamento dei record in grandi batch. Questo, da un lato, è una funzionalità, ma si devono comunque utilizzare componenti aggiuntivi per la memorizzazione temporanea dei record. Questo compito non è sempre semplice, ma è comunque risolvibile. E ci piacerebbe semplificare lo schema.
  2. Alcune funzionalità esotiche o nuove caratteristiche spesso si rompono nelle nuove versioni. Questo suscita preoccupazioni, riducendo la voglia di aggiornarsi all'ultima versione. Ad esempio, il motore delle tabelle Kafka è una funzionalità molto utile che consente di leggere direttamente eventi da Kafka, senza implementare consumatori. Tuttavia, a giudicare dal numero di issue su GitHub, ci asteniamo dall'usare questo motore in produzione. D'altra parte, se non si fanno movimenti bruschi e si utilizza la funzionalità principale, allora funziona in modo stabile.

Pro

  1. Non rallenta.
  2. Basso rischio di ingresso.
  3. Open-source.
  4. Gratuito.
  5. Si scala bene (sharding/rettifica 'out of the box')
  6. È presente nel registro del software russo raccomandato dal Ministero della Comunicazione.
  7. Presenza di supporto ufficiale da Yandex.

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster