ClickHouse Database for Humans sau Alien Technologies

Aleksey Lizunov, șeful Centrului de competențe pentru canalele de servicii la distanță al Direcției Tehnologii Informaționale a MKB

ClickHouse Database for Humans sau Alien Technologies

Ca alternativă la stiva ELK (ElasticSearch, Logstash, Kibana), facem cercetări cu privire la utilizarea bazei de date ClickHouse ca depozit de date pentru jurnale.

În acest articol, am dori să vorbim despre experiența noastră de utilizare a bazei de date ClickHouse și despre rezultatele preliminare ale operațiunii pilot. Trebuie remarcat imediat că rezultatele au fost impresionante.


ClickHouse Database for Humans sau Alien Technologies

În continuare, vom descrie mai detaliat cum este configurat sistemul nostru și din ce componente este compus. Dar acum aș vrea să vorbesc puțin despre această bază de date în ansamblu și de ce merită să fiu atent. Baza de date ClickHouse este o bază de date coloană analitică de înaltă performanță de la Yandex. Este folosit în serviciile Yandex, inițial este principala stocare de date pentru Yandex.Metrica. Sistem open-source, gratuit. Din punctul de vedere al dezvoltatorului, întotdeauna m-am întrebat cum l-au implementat, pentru că există date fantastic de mari. Iar interfața cu utilizatorul Metrica în sine este foarte flexibilă și rapidă. La prima cunoaștere cu această bază de date, impresia este: „Păi, în sfârșit! Făcut pentru oameni! Începând de la procesul de instalare și terminând cu trimiterea solicitărilor.

Această bază de date are un prag de intrare foarte scăzut. Chiar și un dezvoltator cu calificare medie poate instala această bază de date în câteva minute și poate începe să o folosească. Totul funcționează clar. Chiar și oamenii care sunt noi în Linux se pot ocupa rapid de instalare și pot face cele mai simple operațiuni. Dacă mai devreme, cu cuvintele Big Data, Hadoop, Google BigTable, HDFS, un dezvoltator obișnuit avea idei că era vorba despre niște terabytes, petabytes, că unii supraoameni sunt angajați în setările și dezvoltarea acestor sisteme, atunci odată cu apariția ClickHouse baza de date, avem un instrument simplu, ușor de înțeles, cu care puteți rezolva o gamă de sarcini de neatins anterior. Este nevoie de doar o singură mașină destul de medie și cinci minute pentru a instala. Adică, avem o bază de date precum, de exemplu, MySql, dar numai pentru stocarea miliardelor de înregistrări! Un anume super-arhivar cu limbajul SQL. Parcă oamenilor li s-au dat armele extratereștrilor.

Despre sistemul nostru de înregistrare

Pentru a colecta informații, sunt utilizate fișierele jurnal IIS ale aplicațiilor web în format standard (în prezent analizăm și jurnalele aplicațiilor, dar scopul principal în etapa pilot este colectarea jurnalelor IIS).

Din diverse motive, nu am putut abandona complet stiva ELK și continuăm să folosim componentele LogStash și Filebeat, care s-au dovedit bine și funcționează destul de fiabil și previzibil.

Schema generală de înregistrare este prezentată în figura de mai jos:

ClickHouse Database for Humans sau Alien Technologies

O caracteristică de scriere a datelor în baza de date ClickHouse este inserarea rar (o dată pe secundă) de înregistrări în loturi mari. Aceasta, aparent, este cea mai „problematică” parte pe care o întâlniți când lucrați pentru prima dată cu baza de date ClickHouse: schema devine puțin mai complicată.
Pluginul pentru LogStash, care inserează direct date în ClickHouse, a ajutat foarte mult aici. Această componentă este implementată pe același server ca baza de date în sine. Deci, în general, nu este recomandat să o faceți, ci din punct de vedere practic, pentru a nu produce servere separate în timp ce este implementat pe același server. Nu am observat erori sau conflicte de resurse cu baza de date. În plus, trebuie remarcat faptul că pluginul are un mecanism de reîncercare în caz de erori. Și în caz de erori, pluginul scrie pe disc un lot de date care nu a putut fi introdus (formatul fișierului este convenabil: după editare, puteți introduce cu ușurință lotul corectat folosind clickhouse-client).

O listă completă a software-ului utilizat în schemă este prezentată în tabel:

Lista software-ului folosit

Nume

descriere

Link de distribuție

Nginx

Reverse-proxy pentru a restricționa accesul prin porturi și pentru a organiza autorizarea

Momentan nu este utilizat în schemă

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

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

FileBeat

Transferul jurnalelor de fișiere.

https://www.elastic.co/downloads/beats/filebeat (kit de distribuție pentru Windows 64 de biți).

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

logstash

Colector de bușteni.

Folosit pentru a colecta jurnale din FileBeat, precum și pentru a colecta jurnale din coada RabbitMQ (pentru serverele care se află în DMZ.)

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

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

Logstash-output-clickhouse

Plugin Loagstash pentru transferul jurnalelor în baza de date ClickHouse în loturi

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

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

/usr/share/logstash/bin/logstash-plugin instalează logstash-filter-prune

/usr/share/logstash/bin/logstash-plugin instalează logstash-filter-multiline

Faceți clic pe Casă

Stocarea jurnalelor 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

Notă. Începând din august 2018, versiunile „normale” rpm pentru RHEL au apărut în depozitul Yandex, așa că puteți încerca să le utilizați. La momentul instalării, folosim pachete construite de Altinity.

grafana

Vizualizare jurnal. Configurarea tablourilor de bord

https://grafana.com/

https://grafana.com/grafana/download

Redhat & Centos(64 Bit) - cea mai recentă versiune

Sursă de date ClickHouse pentru Grafana 4.6+

Plugin pentru Grafana cu sursa de date ClickHouse

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

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

logstash

Înregistrați routerul de la FileBeat la coada RabbitMQ.

Notă. Din păcate, FileBeat nu are ieșire direct la RabbitMQ, așa că este necesară o legătură intermediară sub formă de Logstash

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

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

Iepure MQ

coada de mesaje. Acesta este buffer-ul de jurnal din 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 (Necesar pentru RabbitMQ)

Timp de rulare Erlang. Necesar pentru ca RabbitMQ să funcționeze

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

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

Configurația serverului cu baza de date ClickHouse este prezentată în următorul tabel:

Nume

Valoare

Nota

configurație

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

Este necesar să fiți atenți la sfaturile pentru operarea bazei de date ClickHouse (https://clickhouse.yandex/docs/ru/operations/tips/)

Software de sistem general

OS: Red Hat Enterprise Linux Server (Maipo)

JRE (Java 8)

 

După cum puteți vedea, aceasta este o stație de lucru obișnuită.

Structura tabelului pentru stocarea jurnalelor este următoarea:

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;

Folosim partiționarea implicită (pe lună) și granularitatea indexului. Toate câmpurile corespund practic intrărilor de jurnal IIS pentru înregistrarea cererilor http. Separat, observăm că există câmpuri separate pentru stocarea etichetelor utm (sunt analizate în etapa de inserare în tabel din câmpul șir de interogare).

De asemenea, mai multe câmpuri de sistem au fost adăugate în tabel pentru a stoca informații despre sisteme, componente, servere. Consultați tabelul de mai jos pentru o descriere a acestor câmpuri. Într-un singur tabel, stocăm jurnalele pentru mai multe sisteme.

Nume

descriere

Exemplu

fld_app_name

Numele aplicației/sistemului
Valori valide:

  • site1.domain.com Site extern 1
  • site2.domain.com Site extern 2
  • internal-site1.domain.local Site intern 1

site1.domeniu.com

fld_app_module

Modul de sistem
Valori valide:

  • web - Website
  • svc - Serviciu de site web
  • intgr - Serviciu web de integrare
  • bo - Administrator (BackOffice)

web

fld_website_name

Numele site-ului în IIS

Mai multe sisteme pot fi implementate pe un server sau chiar mai multe instanțe ale unui singur modul de sistem

principalul web

fld_server_name

Numele serverului

web1.domain.com

fld_log_file_name

Calea către fișierul jurnal de pe server

C:inetpublogsLogFiles
W3SVC1u_ex190711.log

Acest lucru vă permite să construiți eficient grafice în Grafana. De exemplu, vizualizați cererile de la interfața unui anumit sistem. Acesta este similar cu contorul site-ului din Yandex.Metrica.

Iată câteva statistici privind utilizarea bazei de date timp de două luni.

Numărul de înregistrări defalcate pe sisteme și componentele acestora

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

Cantitatea de date de pe disc

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.

Gradul de compresie a datelor în coloane

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.

Descrierea componentelor utilizate

FileBeat. Transferul jurnalelor de fișiere

Această componentă urmărește modificările aduse fișierelor jurnal de pe disc și transmite informațiile către LogStash. Instalat pe toate serverele pe care sunt scrise fișiere jurnal (de obicei IIS). Funcționează în modul coadă (adică transferă numai înregistrările adăugate în fișier). Dar separat poate fi configurat pentru a transfera fișiere întregi. Acest lucru este util atunci când trebuie să descărcați date din lunile precedente. Puneți fișierul jurnal într-un folder și îl va citi în întregime.

Când serviciul este oprit, datele nu mai sunt transferate în continuare în stocare.

Un exemplu de configurație arată astfel:

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. Colector de bușteni

Această componentă este concepută pentru a primi intrări de jurnal de la FileBeat (sau prin coada RabbitMQ), analizând și inserând loturi în baza de date ClickHouse.

Pentru inserarea în ClickHouse, se folosește pluginul Logstash-output-clickhouse. Pluginul Logstash are un mecanism de reîncercare a interogării, dar cu o oprire regulată, este mai bine să opriți serviciul în sine. Când sunt oprite, mesajele vor fi acumulate în coada RabbitMQ, deci dacă oprirea este pentru o perioadă lungă de timp, atunci este mai bine să opriți Filebeats pe servere. Într-o schemă în care RabbitMQ nu este utilizat (în rețeaua locală, Filebeat trimite direct jurnalele către Logstash), Filebeats funcționează destul de acceptabil și în siguranță, astfel încât pentru ei indisponibilitatea ieșirii trece fără consecințe.

Un exemplu de configurație arată astfel:

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

conducte.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. Stocarea jurnalelor

Jurnalele pentru toate sistemele sunt stocate într-un singur tabel (vezi la începutul articolului). Este destinat să stocheze informații despre solicitări: toți parametrii sunt similari pentru diferite formate, cum ar fi jurnalele IIS, jurnalele apache și nginx. Pentru jurnalele de aplicații, în care, de exemplu, sunt înregistrate erori, mesaje informative, avertismente, va fi furnizat un tabel separat cu structura corespunzătoare (în prezent în stadiul de proiectare).

Atunci când proiectați un tabel, este foarte important să decideți asupra cheii primare (după care datele vor fi sortate în timpul stocării). Gradul de compresie a datelor și viteza de interogare depind de acest lucru. În exemplul nostru, cheia este
ORDER BY (fld_app_name, fld_app_module, logdatetime)
Adică după numele sistemului, numele componentei sistemului și data evenimentului. Inițial, data evenimentului a fost pe primul loc. După ce l-am mutat în ultimul loc, interogările au început să funcționeze de aproximativ două ori mai repede. Schimbarea cheii primare va necesita recrearea tabelului și reîncărcarea datelor, astfel încât ClickHouse să resorteze datele de pe disc. Aceasta este o operațiune grea, așa că este o idee bună să vă gândiți mult la ce ar trebui să fie inclus în cheia de sortare.

De asemenea, trebuie remarcat faptul că tipul de date LowCardinality a apărut în versiuni relativ recente. Când îl utilizați, dimensiunea datelor comprimate este redusă drastic pentru acele câmpuri care au cardinalitate scăzută (puține opțiuni).

Versiunea 19.6 este în uz în prezent și intenționăm să încercăm actualizarea la cea mai recentă versiune. Au caracteristici minunate precum Adaptive Granularity, Skipping index și codecul DoubleDelta, de exemplu.

În mod implicit, în timpul instalării, nivelul de înregistrare este setat la urmărire. Jurnalele sunt rotite și arhivate, dar în același timp se extind până la un gigaoctet. Dacă nu este nevoie, atunci puteți seta nivelul de avertizare, atunci dimensiunea jurnalului este redusă drastic. Setarea de înregistrare este setată în fișierul config.xml:

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

Câteva comenzi utile

Поскольку оригинальные пакеты установки собираются по 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. Înregistrați routerul de la FileBeat la coada RabbitMQ

Această componentă este utilizată pentru a direcționa jurnalele care vin de la FileBeat către coada RabbitMQ. Există două puncte aici:

  1. Din păcate, FileBeat nu are un plugin de ieșire pentru a scrie direct în RabbitMQ. Și o astfel de funcționalitate, judecând după problema de pe github-ul lor, nu este planificată pentru implementare. Există un plugin pentru Kafka, dar din anumite motive nu îl putem folosi acasă.
  2. Există cerințe pentru colectarea jurnalelor în DMZ. Pe baza acestora, jurnalele trebuie mai întâi adăugate la coadă și apoi LogStash citește intrările din coadă din exterior.

Prin urmare, în cazul în care serverele sunt situate în DMZ, trebuie să utilizați o schemă atât de ușor complicată. Un exemplu de configurație arată astfel:

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. coada de mesaje

Această componentă este utilizată pentru a stoca intrările de jurnal în DMZ. Înregistrarea se face printr-o mulțime de Filebeat → LogStash. Citirea se face din afara DMZ prin LogStash. Când se operează prin RabboitMQ, sunt procesate aproximativ 4 mii de mesaje pe secundă.

Rutarea mesajelor este configurată după numele sistemului, adică pe baza datelor de configurare FileBeat. Toate mesajele merg într-o singură coadă. Dacă din anumite motive serviciul de așteptare este oprit, atunci acest lucru nu va duce la pierderea mesajelor: FileBeats va primi erori de conexiune și va suspenda temporar trimiterea. Și LogStash care citește din coadă va primi, de asemenea, erori de rețea și va aștepta ca conexiunea să fie restabilită. În acest caz, datele, desigur, nu vor mai fi scrise în baza de date.

Următoarele instrucțiuni sunt folosite pentru a crea și configura cozi:

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. Tablouri de bord

Această componentă este utilizată pentru a vizualiza datele de monitorizare. În acest caz, trebuie să instalați sursa de date ClickHouse pentru pluginul Grafana 4.6+. A trebuit să o modificăm puțin pentru a îmbunătăți eficiența procesării filtrelor SQL pe tabloul de bord.

De exemplu, folosim variabile, iar dacă acestea nu sunt setate în câmpul de filtru, atunci ne-am dori să nu genereze o condiție în WHERE a formularului ( uriStem = » AND uriStem != » ). În acest caz, ClickHouse va citi coloana uriStem. În general, am încercat diferite opțiuni și în cele din urmă am corectat pluginul (macro-ul $valueIfEmpty) astfel încât în ​​cazul unei valori goale să returneze 1, fără a menționa coloana în sine.

Și acum puteți folosi această interogare pentru grafic

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

care se traduce în acest SQL (rețineți că câmpurile uriStem goale au fost convertite la doar 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

Concluzie

Apariția bazei de date ClickHouse a devenit un eveniment marcant pe piață. Era greu de imaginat că, complet gratuit, într-o clipă am fost înarmați cu un instrument puternic și practic pentru a lucra cu date mari. Desigur, odată cu creșterea nevoilor (de exemplu, fragmentarea și replicarea pe mai multe servere), schema va deveni mai complicată. Dar la primele impresii, lucrul cu această bază de date este foarte plăcut. Se poate observa că produsul este făcut „pentru oameni”.

În comparație cu ElasticSearch, costul stocării și procesării jurnalelor este estimat a fi redus de cinci până la zece ori. Cu alte cuvinte, dacă pentru cantitatea actuală de date ar trebui să setăm un cluster de mai multe mașini, atunci când folosim ClickHouse, o singură mașină cu putere redusă este suficientă pentru noi. Da, desigur, ElasticSearch are și mecanisme de comprimare a datelor pe disc și alte caracteristici care pot reduce semnificativ consumul de resurse, dar în comparație cu ClickHouse, acest lucru va fi mai scump.

Fără optimizări speciale din partea noastră, la setările implicite, încărcarea datelor și selectarea din baza de date funcționează cu o viteză uimitoare. Nu avem încă multe date (aproximativ 200 de milioane de înregistrări), dar serverul în sine este slab. Putem folosi acest instrument în viitor și în alte scopuri care nu sunt legate de stocarea jurnalelor. De exemplu, pentru analiza end-to-end, în domeniul securității, învățarea automată.

La final, puțin despre argumentele pro și contra.

Contra

  1. Încărcarea înregistrărilor în loturi mari. Pe de o parte, aceasta este o caracteristică, dar încă trebuie să utilizați componente suplimentare pentru stocarea înregistrărilor în tampon. Această sarcină nu este întotdeauna ușoară, dar totuși rezolvabilă. Și aș dori să simplific schema.
  2. Unele funcționalități exotice sau caracteristici noi apar adesea în versiuni noi. Acest lucru provoacă îngrijorare, reducând dorința de a face upgrade la o versiune nouă. De exemplu, motorul de tabel Kafka este o caracteristică foarte utilă care vă permite să citiți direct evenimentele din Kafka, fără a implementa consumatorii. Dar judecând după numărul de probleme de pe github, suntem încă atenți să nu folosim acest motor în producție. Cu toate acestea, dacă nu faci gesturi bruște în lateral și folosești funcționalitatea principală, atunci funcționează stabil.

Pro

  1. Nu încetinește.
  2. Prag de intrare scăzut.
  3. Sursa deschisa.
  4. Gratuit.
  5. Se scalează bine (sharding/replicare din cutie)
  6. Inclus în registrul software-ului rusesc recomandat de Ministerul Comunicațiilor.
  7. Prezența sprijinului oficial din partea Yandex.

Sursa: www.habr.com

Adauga un comentariu