Database ClickHouse per esseri umani o tecnologie aliene

Aleksey Lizunov, capo del Centro di competenza per i canali di servizio remoto della direzione delle tecnologie dell'informazione dell'MKB

Database ClickHouse per esseri umani o tecnologie aliene

In alternativa allo stack ELK (ElasticSearch, Logstash, Kibana), stiamo effettuando ricerche sull'utilizzo del database ClickHouse come archivio dati per i log.

In questo articolo, vorremmo parlare della nostra esperienza nell'utilizzo del database ClickHouse e dei risultati preliminari dell'operazione pilota. Va notato subito che i risultati sono stati impressionanti.


Database ClickHouse per esseri umani o tecnologie aliene

Successivamente, descriveremo in modo più dettagliato come è configurato il nostro sistema e quali componenti è costituito. Ma ora vorrei parlare un po' di questo database nel suo insieme e del motivo per cui vale la pena prestare attenzione. Il database ClickHouse è un database colonnare analitico ad alte prestazioni di Yandex. Viene utilizzato nei servizi Yandex, inizialmente è l'archivio dati principale per Yandex.Metrica. Sistema open source, gratuito. Dal punto di vista di uno sviluppatore, mi sono sempre chiesto come l'hanno implementato, perché ci sono dati incredibilmente grandi. E la stessa interfaccia utente di Metrica è molto flessibile e veloce. Alla prima conoscenza di questo database, l'impressione è: “Bene, finalmente! Fatto per le persone! A partire dal processo di installazione e termina con l'invio di richieste.

Questo database ha una soglia di ingresso molto bassa. Anche uno sviluppatore medio esperto può installare questo database in pochi minuti e iniziare a usarlo. Tutto funziona chiaramente. Anche le persone che non conoscono Linux possono gestire rapidamente l'installazione ed eseguire le operazioni più semplici. Se prima, con le parole Big Data, Hadoop, Google BigTable, HDFS, un normale sviluppatore aveva l'idea che si trattasse di alcuni terabyte, petabyte, che alcuni superumani sono impegnati nelle impostazioni e nello sviluppo di questi sistemi, quindi con l'avvento di ClickHouse database, abbiamo uno strumento semplice e comprensibile con il quale è possibile risolvere una gamma di compiti precedentemente irraggiungibile. Ci vogliono solo una macchina abbastanza media e cinque minuti per l'installazione. Cioè, abbiamo un database come, ad esempio, MySql, ma solo per archiviare miliardi di record! Un certo super-archiviatore con il linguaggio SQL. È come se alle persone fossero state consegnate le armi degli alieni.

Informazioni sul nostro sistema di registrazione

Per raccogliere informazioni, vengono utilizzati i file di registro IIS delle applicazioni Web in formato standard (attualmente stiamo anche analizzando i registri delle applicazioni, ma l'obiettivo principale nella fase pilota è raccogliere i registri IIS).

Per vari motivi, non abbiamo potuto abbandonare completamente lo stack ELK e continuiamo a utilizzare i componenti LogStash e Filebeat, che si sono dimostrati validi e funzionano in modo abbastanza affidabile e prevedibile.

Lo schema generale di registrazione è mostrato nella figura seguente:

Database ClickHouse per esseri umani o tecnologie aliene

Una caratteristica della scrittura dei dati nel database ClickHouse è l'inserimento raro (una volta al secondo) di record in batch di grandi dimensioni. Questa, a quanto pare, è la parte più “problematica” che si incontra quando si lavora per la prima volta con il database ClickHouse: lo schema diventa un po' più complicato.
Il plugin per LogStash, che inserisce direttamente i dati in ClickHouse, ha aiutato molto qui. Questo componente viene distribuito sullo stesso server del database stesso. Quindi, in generale, non è consigliabile farlo, ma da un punto di vista pratico, in modo da non produrre server separati mentre è distribuito sullo stesso server. Non abbiamo osservato errori o conflitti di risorse con il database. Inoltre, va notato che il plugin ha un meccanismo di riprova in caso di errori. E in caso di errori, il plug-in scrive su disco un batch di dati che non è stato possibile inserire (il formato del file è conveniente: dopo la modifica, è possibile inserire facilmente il batch corretto utilizzando clickhouse-client).

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

Elenco dei software utilizzati

Nome

descrizione

Collegamento di distribuzione

Nginx

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

Attualmente non utilizzato nello schema

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

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

FileBeat

Trasferimento di registri di file.

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

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

logstash

Raccoglitore di registri.

Utilizzato per raccogliere i log da FileBeat, nonché per raccogliere i log dalla coda RabbitMQ (per i server che si trovano nella DMZ).

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

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

Logstash-output-clickhouse

Plugin Loagstash per il trasferimento dei registri al database ClickHouse in batch

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

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

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

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

CliccaCasa

Archiviazione del registro 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 Yandex sono apparse build rpm "normali" per RHEL, quindi puoi provare a usarle. Al momento dell'installazione, utilizzavamo i pacchetti creati da Altinity.

graminacee

Visualizzazione del registro. Impostazione dashboard

https://grafana.com/

https://grafana.com/grafana/download

Redhat e Centos (64 bit) - ultima versione

Origine dati ClickHouse per Grafana 4.6+

Plugin per Grafana con origine dati ClickHouse

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

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

logstash

Registra il router da FileBeat alla coda RabbitMQ.

Nota. Sfortunatamente, FileBeat non ha l'output direttamente su RabbitMQ, quindi è necessario un collegamento intermedio sotto forma di Logstash

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

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

RabbitMQ

coda di messaggi. Questo è il buffer di registro 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 (richiesto per RabbitMQ)

Tempo di esecuzione di 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 suggerimenti per il funzionamento del database ClickHouse (https://clickhouse.yandex/docs/ru/operations/tips/)

Software di sistema generale

Sistema operativo: Red Hat Enterprise Linux Server (Maipo)

JRE (Java 8)

 

Come puoi vedere, questa è una normale postazione di lavoro.

La struttura della tabella per la memorizzazione dei log è la seguente:

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 il partizionamento predefinito (per mese) e la granularità dell'indice. Tutti i campi corrispondono praticamente alle voci del registro IIS per la registrazione delle richieste http. Separatamente, notiamo che ci sono campi separati per la memorizzazione dei tag utm (vengono analizzati nella fase di inserimento nella tabella dal campo della stringa di query).

Inoltre, diversi campi di sistema sono stati aggiunti alla tabella per memorizzare informazioni su sistemi, componenti, server. Vedere la tabella seguente per una descrizione di questi campi. In una tabella, memorizziamo i log per diversi sistemi.

Nome

descrizione

esempio

fld_nome_app

Nome dell'applicazione/sistema
Valori validi:

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

sito1.dominio.com

fld_app_module

Modulo di sistema
Valori validi:

  • web - Sito web
  • svc - Servizio del sito web
  • intgr - Servizio Web di integrazione
  • bo - Amministratore (BackOffice)

sito web

fld_nome_sito web

Nome del sito in IIS

Diversi sistemi possono essere distribuiti su un server o anche più istanze di un modulo di sistema

web principale

nome_server_fld

Nome del server

web1.dominio.com

fld_log_nome_file

Percorso del file di registro sul server

C:inetpublogsLogFiles
W3SVC1u_ex190711.log

Ciò ti consente di creare grafici in modo efficiente in Grafana. Ad esempio, visualizza le richieste dal frontend di un particolare sistema. Questo è simile al contatore del sito in Yandex.Metrica.

Ecco alcune statistiche sull'utilizzo del database per 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 │
└──────────────────┴────────────────┴────────────┘
 
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.)

La quantità di dati sul 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.

Grado di compressione dei dati nelle colonne

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.

Descrizione dei componenti usati

FileBeat. Trasferimento dei registri dei file

Questo componente tiene traccia delle modifiche apportate ai file di registro su disco e passa le informazioni a LogStash. Installato su tutti i server in cui vengono scritti i file di registro (di solito IIS). Funziona in modalità coda (ovvero trasferisce solo i record aggiunti al file). Ma separatamente può essere configurato per trasferire interi file. Questo è utile quando è necessario scaricare i dati dei mesi precedenti. Basta mettere il file di registro in una cartella e lo leggerà nella sua interezza.

Quando il servizio viene interrotto, i dati non vengono più trasferiti all'archivio.

Una configurazione di esempio è simile a questa:

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. Raccoglitore di registri

Questo componente è progettato per ricevere voci di registro da FileBeat (o tramite la coda RabbitMQ), analizzare e inserire batch nel database ClickHouse.

Per l'inserimento in ClickHouse, viene utilizzato il plug-in Logstash-output-clickhouse. Il plug-in Logstash ha un meccanismo di ripetizione della richiesta, ma con un arresto regolare è meglio interrompere il servizio stesso. Quando vengono arrestati, i messaggi verranno accumulati nella coda RabbitMQ, quindi se l'interruzione dura a lungo, è meglio interrompere Filebeats sui server. In uno schema in cui non viene utilizzato RabbitMQ (sulla rete locale, Filebeat invia direttamente i log a Logstash), Filebeats funziona in modo abbastanza accettabile e sicuro, quindi per loro l'indisponibilità dell'output passa senza conseguenze.

Una configurazione di esempio è simile a questa:

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"
        }
    }
 
}

pipeline.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. Archiviazione del registro

I registri per tutti i sistemi sono archiviati in una tabella (vedere all'inizio dell'articolo). Ha lo scopo di memorizzare informazioni sulle richieste: tutti i parametri sono simili per diversi formati, come i log IIS, Apache e nginx. Per i log dell'applicazione, in cui sono registrati, ad esempio, errori, messaggi informativi, avvisi, verrà fornita una tabella separata con la struttura appropriata (attualmente in fase di progettazione).

Quando si progetta una tabella, è molto importante decidere la chiave primaria (in base alla quale i dati verranno ordinati durante l'archiviazione). Il grado di compressione dei dati e la velocità delle query dipendono da questo. Nel nostro esempio, la chiave è
ORDINA PER (fld_app_name, fld_app_module, logdatetime)
Cioè, con il nome del sistema, il nome del componente del sistema e la data dell'evento. Inizialmente, la data dell'evento veniva prima. Dopo averlo spostato all'ultimo posto, le query hanno iniziato a funzionare circa il doppio più velocemente. La modifica della chiave primaria richiederà la ricreazione della tabella e il ricaricamento dei dati in modo che ClickHouse riordini i dati su disco. Questa è un'operazione pesante, quindi è una buona idea pensare molto a cosa dovrebbe essere incluso nella chiave di ordinamento.

Va inoltre notato che il tipo di dati LowCardinality è apparso in versioni relativamente recenti. Quando lo si utilizza, la dimensione dei dati compressi viene drasticamente ridotta per quei campi che hanno una cardinalità bassa (poche opzioni).

La versione 19.6 è attualmente in uso e prevediamo di provare ad aggiornarla all'ultima versione. Hanno caratteristiche meravigliose come la granularità adattiva, gli indici di salto e il codec DoubleDelta, per esempio.

Per impostazione predefinita, durante l'installazione, il livello di registrazione è impostato su trace. I log vengono ruotati e archiviati, ma allo stesso tempo si espandono fino a un gigabyte. Se non è necessario, è possibile impostare il livello di avviso, quindi la dimensione del registro viene drasticamente ridotta. L'impostazione di registrazione è impostata nel file config.xml:

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

Alcuni comandi utili

Поскольку оригинальные пакеты установки собираются по 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. Registra il router da FileBeat alla coda RabbitMQ

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

  1. Sfortunatamente, FileBeat non ha un plug-in di output per scrivere direttamente su RabbitMQ. E tale funzionalità, a giudicare dal problema sul loro github, non è prevista per l'implementazione. C'è un plugin per Kafka, ma per qualche motivo non possiamo usarlo a casa.
  2. Esistono requisiti per la raccolta dei registri nella DMZ. Sulla base di essi, i log devono prima essere aggiunti alla coda e quindi LogStash legge le voci dalla coda dall'esterno.

Pertanto, è per il caso in cui i server si trovano nella DMZ che è necessario utilizzare uno schema così leggermente complicato. Una configurazione di esempio è simile a questa:

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
    }
}

ConiglioMQ. coda di messaggi

Questo componente viene utilizzato per memorizzare le voci di registro nella DMZ. La registrazione viene eseguita tramite una serie di Filebeat → LogStash. La lettura viene eseguita dall'esterno della DMZ tramite LogStash. Quando si opera tramite RabboitMQ, vengono elaborati circa 4mila messaggi al secondo.

Il routing dei messaggi è configurato in base al nome del sistema, ovvero in base ai dati di configurazione di FileBeat. Tutti i messaggi vanno in una coda. Se per qualche motivo il servizio di accodamento viene interrotto, ciò non comporterà la perdita di messaggi: FileBeats riceverà errori di connessione e sospenderà temporaneamente l'invio. E LogStash che legge dalla coda riceverà anche errori di rete e attenderà il ripristino della connessione. In questo caso, i dati, ovviamente, non verranno più 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. Cruscotti

Questo componente viene utilizzato per visualizzare i dati di monitoraggio. In questo caso, è necessario installare l'origine dati ClickHouse per il plug-in Grafana 4.6+. Abbiamo dovuto modificarlo un po' per migliorare l'efficienza dell'elaborazione dei filtri SQL sulla dashboard.

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

E ora puoi usare questa query 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 traduce in questo SQL (si noti che i campi uriStem vuoti sono stati convertiti in solo 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'aspetto del database ClickHouse è diventato un evento di riferimento nel mercato. Era difficile immaginare che, in modo completamente gratuito, in un istante fossimo armati di uno strumento potente e pratico per lavorare con i big data. Naturalmente, con l'aumento delle esigenze (ad esempio, sharding e replica su più server), lo schema diventerà più complicato. Ma alla prima impressione, lavorare con questo database è molto piacevole. Si può vedere che il prodotto è fatto "per le persone".

Rispetto a ElasticSearch, si stima che il costo di archiviazione ed elaborazione dei log sia ridotto da cinque a dieci volte. In altre parole, se per l'attuale quantità di dati dovessimo creare un cluster di più macchine, quando utilizziamo ClickHouse ci basta una macchina a basso consumo. Sì, certo, ElasticSearch ha anche meccanismi di compressione dei dati su disco e altre funzionalità che possono ridurre significativamente il consumo di risorse, ma rispetto a ClickHouse, questo sarà più costoso.

Senza particolari ottimizzazioni da parte nostra, con le impostazioni predefinite, il caricamento dei dati e la selezione dal database funziona a una velocità sorprendente. Non abbiamo ancora molti dati (circa 200 milioni di record), ma il server stesso è debole. Possiamo utilizzare questo strumento in futuro per altri scopi non correlati alla memorizzazione dei registri. Ad esempio, per l'analisi end-to-end, nel campo della sicurezza, dell'apprendimento automatico.

Alla fine, un po 'di pro e contro.

Contro

  1. Caricamento di record in lotti di grandi dimensioni. Da un lato, questa è una funzionalità, ma devi comunque utilizzare componenti aggiuntivi per il buffering dei record. Questo compito non è sempre facile, ma comunque risolvibile. E vorrei semplificare lo schema.
  2. Alcune funzionalità esotiche o nuove funzionalità spesso si rompono nelle nuove versioni. Ciò causa preoccupazione, riducendo il desiderio di eseguire l'aggiornamento a una nuova versione. Ad esempio, il motore di tabelle Kafka è una funzionalità molto utile che consente di leggere direttamente gli eventi da Kafka, senza implementare i consumatori. Ma a giudicare dal numero di problemi su github, stiamo ancora attenti a non utilizzare questo motore in produzione. Tuttavia, se non fai gesti improvvisi di lato e utilizzi la funzionalità principale, allora funziona stabilmente.

Pro

  1. Non rallenta.
  2. Soglia di ingresso bassa.
  3. Open source.
  4. Gratuito.
  5. Si adatta bene (sharding/replica fuori dagli schemi)
  6. Incluso nel registro del software russo raccomandato dal Ministero delle comunicazioni.
  7. La presenza del supporto ufficiale di Yandex.

Fonte: habr.com

Aggiungi un commento