ClickHouse-Datenbanken fĂŒr Menschen oder Technologien von Außerirdischen

Aleksey Lizunov, Leiter des Bereichs fĂŒr das Kompetenzzentrum der FernbedienungskanĂ€le der IT-Direktion von MKB

ClickHouse-Datenbanken fĂŒr Menschen oder Technologien von Außerirdischen

Als Alternative zum ELK-Stack (ElasticSearch, Logstash, Kibana) fĂŒhren wir Forschungsarbeiten zur Nutzung der Datenbank ClickHouse als Datenspeicher fĂŒr Logs durch.

In diesem Artikel möchten wir ĂŒber unsere Erfahrungen mit der Datenbank ClickHouse und ĂŒber die vorlĂ€ufigen Ergebnisse aus der Pilotnutzung berichten. Es ist sofort zu beachten, dass die Ergebnisse beeindruckend sind.


ClickHouse-Datenbanken fĂŒr Menschen oder Technologien von Außerirdischen

Im Folgenden werden wir detailliert beschreiben, wie unser System eingerichtet ist und aus welchen Komponenten es besteht. ZunĂ€chst möchten wir jedoch ein wenig ĂŒber diese Datenbank im Allgemeinen sprechen und warum es sich lohnt, ihr Aufmerksamkeit zu schenken. Die Datenbank ClickHouse ist eine hochleistungsfĂ€hige analytische Spalten-Datenbank von Yandex. Sie wird in den Diensten von Yandex verwendet und war ursprĂŒnglich der Hauptdatenspeicher fĂŒr Yandex.Metrik. Es handelt sich um ein Open-Source-System, das kostenlos ist. Aus der Sicht eines Entwicklers war ich immer neugierig, wie sie das umgesetzt haben, denn dort gibt es fantastisch große Daten. Auch die BenutzeroberflĂ€che von Metrik ist sehr flexibel und arbeitet schnell. Bei der ersten Begegnung mit dieser Datenbank ist der Eindruck: „Endlich! Es ist ‚fĂŒr die Menschen‘ gemacht! Vom Installationsprozess bis zur Übermittlung von Anfragen“.

Diese Datenbank hat eine sehr niedrige Eintrittsbarriere. Selbst ein Entwickler mit durchschnittlichem Know-how kann diese Datenbank in wenigen Minuten installieren und nutzen. Alles funktioniert reibungslos. Selbst Personen, die mit Linux wenig vertraut sind, können die Installation ziemlich schnell bewĂ€ltigen und einfachste Operationen durchfĂŒhren. FrĂŒher hatten gewöhnliche Entwickler beim Wort Big Data, Hadoop, Google BigTable, HDFS, Vorstellungen, dass es dabei um Terabytes oder Petabytes ging und dass die Einrichtung und Entwicklung fĂŒr diese Systeme von ĂŒbermenschlichen Wesen durchgefĂŒhrt wird. Mit dem Aufkommen der Datenbank ClickHouse haben wir ein einfaches, verstĂ€ndliches Werkzeug erhalten, mit dem wir einen zuvor unerreichbaren Aufgabenbereich bewĂ€ltigen können. Es genĂŒgt eine recht bescheidene Maschine und fĂŒnf Minuten fĂŒr die Installation. Wir haben also eine Datenbank, wie z. B. MySql, aber nur fĂŒr die Speicherung von Milliarden von DatensĂ€tzen! Eine Art Superarchivator mit SQL-Sprache. Es ist, als hĂ€tten die Menschen eine Waffe von Außerirdischen erhalten.

Über unser System zur Protokollsammlung

Zur Informationssammlung werden Standardformat-Logdateien von IIS-Webanwendungen verwendet (außerdem beschĂ€ftigen wir uns derzeit auch mit dem Parsen von Anwendungslogs, aber unser Hauptziel in der Pilotphase ist die Sammlung von IIS-Logs).

VollstĂ€ndig auf den ELK-Stack konnten wir aus verschiedenen GrĂŒnden nicht verzichten, und wir nutzen weiterhin die Komponenten LogStash und Filebeat, die sich gut bewĂ€hrt haben und zuverlĂ€ssig sowie vorhersehbar arbeiten.

Das allgemeine Logging-Schema ist im folgenden Bild dargestellt:

ClickHouse-Datenbanken fĂŒr Menschen oder Technologien von Außerirdischen

Ein besonderes Merkmal der Datenspeicherung in der ClickHouse-Datenbank ist die seltene (einmal pro Sekunde) EinfĂŒgung von DatensĂ€tzen in großen Paketen. Dies scheint die „problematischste“ Komponente zu sein, mit der man beim ersten Arbeiten mit ClickHouse konfrontiert wird: das Schema wird ein wenig komplizierter.
Hier half das LogStash-Plugin sehr, das Daten direkt in ClickHouse einfĂŒgt. Dieses Modul wird auf demselben Server bereitgestellt wie die Datenbank selbst. Das sollte man eigentlich nicht machen, aber aus praktischer Sicht, um separate Server zu vermeiden, bleibt es auf demselben Server. Wir haben keine AusfĂ€lle oder Ressourcenkonflikte mit der Datenbank beobachtet. Außerdem ist zu erwĂ€hnen, dass das Plugin einen Retry-Mechanismus im Fehlerfall hat. Im Fehlerfall schreibt das Plugin eine Gruppe von Daten, die nicht eingefĂŒgt werden konnten, auf die Festplatte (das Dateiformat ist praktisch: nach der Korrektur kann das korrigierte Paket leicht mit dem clickhouse-client eingefĂŒgt werden).

Die vollstÀndige Liste der in dem Schema verwendeten Software ist in der Tabelle dargestellt:

Liste der verwendeten Software

Bezeichnung

Beschreibung

Link zum Distribution

NGINX

Reverse-Proxy zur ZugriffsbeschrĂ€nkung ĂŒber Ports und zur Organisation der Authentifizierung

Derzeit nicht im Schema verwendet

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

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

FileBeat

Übertragung von Logdateien.

https://www.elastic.co/downloads/beats/filebeat (Distribution fĂŒr Windows 64bit).

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

LogStash

Logsammler.

Wird zum Sammeln von Logs von FileBeat sowie zum Sammeln von Logs aus der RabbitMQ-Warteschlange verwendet (fĂŒr Server, die sich in der DMZ befinden).

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

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

Logstash-output-clickhouse

Logstash-Plugin zur Übertragung von Logs in die ClickHouse-Datenbank in Paketen

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

Logspeicher 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

Hinweis. Seit August 2018 sind im Yandex-Repository „normale“ RPM-Builds fĂŒr RHEL erhĂ€ltlich, daher können diese ausprobiert werden. Zum Zeitpunkt der Installation verwendeten wir Pakete, die von Altinity erstellt wurden.

Grafana

Logvisualisierung. Dashboard-Einstellungen

https://grafana.com/

https://grafana.com/grafana/download

Redhat & Centos(64 Bit) – die neueste Version

ClickHouse-Datenquelle fĂŒr Grafana 4.6+

Plugin fĂŒr Grafana mit der ClickHouse-Datenquelle

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

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

LogStash

Der Log-Router von FileBeat in die RabbitMQ-Warteschlange.

Hinweis. Leider gibt es bei FileBeat keinen direkten Output in RabbitMQ, daher ist ein Zwischenschritt in Form von Logstash erforderlich.

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

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

RabbitMQ

Nachrichtenwarteschlange. Dies ist ein Puffer fĂŒr LogeintrĂ€ge in der 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 (Notwendig fĂŒr RabbitMQ)

Erlang-Laufzeitumgebung. Erforderlich fĂŒr die Funktion von RabbitMQ.

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

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

Die Serverkonfiguration mit der ClickHouse-Datenbank ist in der folgenden Tabelle dargestellt:

Bezeichnung

Wert

Hinweis

Konfiguration

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

Es ist wichtig, die Hinweise zur Nutzung der ClickHouse-Datenbank zu beachten (https://clickhouse.yandex/docs/ru/operations/tips/)

Allgemeine Systemsoftware

Betriebssystem: Red Hat Enterprise Linux Server (Maipo)

JRE (Java 8)

 

Wie zu sehen ist, handelt es sich um einen ganz gewöhnlichen Arbeitsplatz.

Die Tabellenstruktur zur Speicherung von Logs sieht folgendermaßen aus:

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;

Wir verwenden die Standardwerte fĂŒr die Partitionierung (monatlich) und die IndexgranularitĂ€t. Alle Felder entsprechen praktisch den LogeintrĂ€gen des IIS fĂŒr die Registrierung von HTTP-Anfragen. Besonders hervorzuheben sind die separaten Felder zur Speicherung von UTM-Parametern (diese werden beim EinfĂŒgen in die Tabelle aus dem Query-String-Feld geparst).

In der Tabelle wurden auch mehrere Systemfelder zur Speicherung von Informationen ĂŒber Systeme, Komponenten und Server hinzugefĂŒgt. Die Beschreibung dieser Felder finden Sie in der Tabelle unten. In einer Tabelle speichern wir Logs von mehreren Systemen.

Bezeichnung

Beschreibung

Beispiel

fld_app_name

Name der Anwendung/System
ZulÀssige Werte:

  • site1.domain.com Externer Standort 1
  • site2.domain.com Externer Standort 2
  • internal-site1.domain.local Interner Standort 1

site1.domain.com

fld_app_module

Modul des Systems
ZulÀssige Werte:

  • web — Webseite
  • svc — Webdienst der Seite
  • intgr — Integrations-Webdienst
  • bo — Admin-Bereich (BackOffice)

web

fld_website_name

Name der Website im IIS

Auf einem Server können mehrere Systeme oder sogar mehrere Instanzen eines Moduls installiert sein.

web-main

fld_server_name

Servername

web1.domain.com

fld_log_file_name

Pfad zur Logdatei auf dem Server

C:\inetpub\logs\LogFiles
W3SVC1u_ex190711.log

Dies ermöglicht eine effiziente Erstellung von Grafiken in Grafana. Zum Beispiel das Betrachten von Anfragen vom Frontend eines bestimmten Systems. Es Àhnelt dem ZÀhler einer Website in Yandex.Metrica.

Hier sind einige Statistiken zur Nutzung der Datenbank ĂŒber zwei Monate.

Anzahl der EintrÀge nach Systemen und deren Komponenten

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

Datenvolumen auf der Festplatte

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.

Kompressionsrate der Daten in den Spalten

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 Zeilen im Set. Verstrichene Zeit: 0,005 Sek.

Beschreibung der verwendeten Komponenten

FileBeat. Übertragung von Dateilogs

Diese Komponente ĂŒberwacht Änderungen in Logdateien auf der Festplatte und ĂŒbertrĂ€gt Informationen an LogStash. Sie wird auf allen Servern installiert, auf denen Logdateien geschrieben werden (in der Regel IIS). Sie arbeitet im Tail-Modus (d. h. sie ĂŒbertrĂ€gt nur die hinzugefĂŒgten EintrĂ€ge in die Datei). Separat kann sie auch so konfiguriert werden, dass ganze Dateien ĂŒbertragen werden. Das ist praktisch, wenn Daten der vorhergehenden Monate hochgeladen werden mĂŒssen. Einfach die Logdatei in den Ordner legen und sie wird diese vollstĂ€ndig lesen.

Bei der Deaktivierung des Dienstes werden die Daten nicht mehr ins Speicher weitergeleitet.

Ein Beispiel fĂŒr die Konfiguration sieht folgendermaßen aus:

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

Dieses Modul dient dazu, ProtokolleintrĂ€ge von FileBeat (entweder ĂŒber eine RabbitMQ-Warteschlange) zu empfangen, zu parsen und gebĂŒndelt in die ClickHouse-Datenbank einzufĂŒgen.

FĂŒr die EinfĂŒgung in ClickHouse wird das Logstash-output-clickhouse-Plugin verwendet. Logstash hat einen Mechanismus fĂŒr die Wiederholung von Anfragen, aber bei einer regulĂ€ren Deaktivierung ist es besser, den Dienst selbst zu stoppen. Bei der Deaktivierung sammeln sich Nachrichten in der RabbitMQ-Warteschlange, daher ist es besser, die Filebeats auf den Servern zu stoppen, wenn der Halt lĂ€nger dauert. In einem Setup, das RabbitMQ nicht verwendet (Filebeat sendet Protokolle direkt an Logstash innerhalb des lokalen Netzwerks), funktionieren Filebeats völlig akzeptabel und sicher, sodass ein Ausfall des Outputs keine Folgen hat.

Ein Beispiel fĂŒr die Konfiguration sieht folgendermaßen aus:

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

Die Protokolle aller Systeme werden in einer Tabelle gespeichert (siehe am Anfang des Artikels). Sie dient der Speicherung von Informationen ĂŒber Anfragen: Alle Parameter sind fĂŒr verschiedene Formate Ă€hnlich, beispielsweise IIS-Protokolle, Apache- und Nginx-Protokolle. FĂŒr Anwendungsprotokolle, in denen beispielsweise Fehler, Informationen und Warnungen registriert werden, wird eine separate Tabelle mit einer entsprechenden Struktur vorgesehen (momentan in der Planungsphase).

Bei der Planung der Tabelle ist es sehr wichtig, sich auf den PrimĂ€rschlĂŒssel festzulegen (nach dem die Daten beim Speichern sortiert werden). Dies beeinflusst das Maß der Datenkompression und die Geschwindigkeit der Abfragen. In unserem Beispiel ist der SchlĂŒssel
ORDER BY (fld_app_name, fld_app_module, logdatetime)
d. h. nach dem Systemnamen, dem Namen der Systemkomponente und dem Datum des Ereignisses. UrsprĂŒnglich stand das Datum des Ereignisses an erster Stelle. Nachdem es an die letzte Stelle verschoben wurde, arbeiteten die Abfragen etwa doppelt so schnell. Eine Änderung des PrimĂ€rschlĂŒssels erfordert die Neuerstellung der Tabelle und das erneute Hochladen der Daten, damit ClickHouse die Daten auf der Festplatte neu sortiert. Dies ist ein aufwendiger Vorgang, daher ist es ratsam, im Voraus genau zu ĂŒberlegen, was in den SortierschlĂŒssel aufgenommen werden soll.

Es sollte auch erwĂ€hnt werden, dass in den letzten Versionen der Datentyp LowCardinality eingefĂŒhrt wurde. Bei dessen Verwendung wird die GrĂ¶ĂŸe der komprimierten Daten fĂŒr Felder mit niedriger KardinalitĂ€t (wenig Varianten) erheblich reduziert.

Derzeit wird Version 19.6 verwendet, und wir planen, die Version auf die neueste zu aktualisieren. Darin sind großartige Funktionen wie Adaptive Granularity, Skipping Indices und der DoubleDelta-Codec enthalten.

Bei der Installation ist standardmĂ€ĂŸig das Protokollierungsniveau 'trace' in der Konfiguration festgelegt. Die Protokolle werden rotiert und archiviert, wobei sie auf bis zu einem Gigabyte erweitert werden. Wenn dies nicht notwendig ist, kann das Protokollierungsniveau auf 'warning' gesetzt werden, wodurch die GrĂ¶ĂŸe des Protokolls stark reduziert wird. Die Protokollierungseinstellungen werden in der Datei config.xml festgelegt:


warning

Einige nĂŒtzliche Befehle

Da die ursprĂŒnglichen Installationspakete fĂŒr Debian zusammengestellt werden, mĂŒssen fĂŒr andere Linux-Versionen die Pakete verwendet werden, die von Altinity erstellt wurden.
 
Hier ist der Link zu den Anleitungen mit Links zu ihrem Repository: https://www.altinity.com/blog/2017/12/18/logstash-with-clickhouse
sudo yum search clickhouse-server
sudo yum install clickhouse-server.noarch
  
1. ÜberprĂŒfen des Status
sudo systemctl status clickhouse-server
 
2. Stoppen des Servers
sudo systemctl stop clickhouse-server
 
3. Starten des Servers
sudo systemctl start clickhouse-server
 
Starten zur AusfĂŒhrung von Abfragen im mehrzeiligen Modus (auszufĂŒhren nach dem Zeichen ";")
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
 
Das ClickHouse-Plugin fĂŒr LogStash speichert im Fall eines Fehlers in einer Zeile das gesamte Paket in der Datei /tmp/log_web_failed.json
Man kann diese Datei manuell bearbeiten und versuchen, sie manuell in die Datenbank hochzuladen:
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
 
Beenden der Eingabeaufforderung
quit;
## TLS-Konfiguration
https://www.altinity.com/blog/2019/3/5/clickhouse-networking-part-2
 
openssl s_client -connect log.domain.com:9440 < /dev/null

LogStash. Der Protokollrouter von FileBeat zur RabbitMQ-Warteschlange

Diese Komponente wird zur Weiterleitung von Protokollen verwendet, die von FileBeat an die RabbitMQ-Warteschlange gesendet werden. Hier gibt es zwei Punkte:

  1. Leider verfĂŒgt FileBeat nicht ĂŒber ein Ausgabemodul, um direkt in RabbitMQ zu schreiben. Laut einer Anfrage auf ihrem GitHub wird eine solche FunktionalitĂ€t nicht umgesetzt. Es gibt ein Plugin fĂŒr Kafka, aber aus bestimmten GrĂŒnden können wir es bei uns nicht verwenden.
  2. Es gibt Anforderungen fĂŒr das Sammeln von Protokollen in der DMZ. GemĂ€ĂŸ diesen Anforderungen mĂŒssen die Protokolle zunĂ€chst in der Warteschlange abgelegt werden, und dann liest LogStash die EintrĂ€ge von außen aus der Warteschlange.

Daher mĂŒssen wir fĂŒr den Fall, dass die Server in der DMZ stehen, ein etwas komplizierteres Schema verwenden. Ein Beispiel fĂŒr die Konfiguration sieht folgendermaßen aus:

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

Diese Komponente wird zur Pufferung von ProtokolldatensĂ€tzen in der DMZ verwendet. Das Schreiben erfolgt ĂŒber die Verbindung Filebeat → LogStash. Das Lesen erfolgt von außerhalb der DMZ ĂŒber LogStash. Bei der Verwendung von RabbitMQ werden etwa 4000 Nachrichten pro Sekunde verarbeitet.

Das Routing der Nachrichten erfolgt nach dem Systemnamen, d. h. basierend auf den Daten der FileBeat-Konfiguration. Alle Nachrichten landen in einer Warteschlange. Wenn der Warteschendienst aus irgendeinem Grund gestoppt wird, kommt es nicht zu einem Verlust von Nachrichten: FileBeats erhalten Verbindungsfehler und unterbrechen vorĂŒbergehend das Senden. Auch LogStash, das aus der Warteschlange liest, erhĂ€lt Netzwerkfehler und wartet, bis die Verbindung wiederhergestellt ist. Dabei werden natĂŒrlich keine Daten mehr in die Datenbank geschrieben.

Die folgenden Anweisungen werden zur Erstellung und Konfiguration von Warteschlangen verwendet:

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

Diese Komponente wird zur Visualisierung von Überwachungsdaten verwendet. Dazu muss das Plugin ClickHouse-Datenquelle fĂŒr Grafana 4.6+ installiert werden. Wir mussten es ein wenig anpassen, um die Effizienz der Verarbeitung von SQL-Filtern im Dashboard zu erhöhen.

Zum Beispiel verwenden wir Variablen, und wenn diese im Filterfeld nicht gesetzt sind, möchten wir, dass keine Bedingung in WHERE in der Form (uriStem = » AND uriStem != ») generiert wird. In diesem Fall wird ClickHouse die Spalte uriStem lesen. Insgesamt haben wir verschiedene Varianten ausprobiert und schließlich das Plugin (Makro $valueIfEmpty) so angepasst, dass es im Falle eines leeren Wertes 1 zurĂŒckgibt, ohne die Spalte selbst zu erwĂ€hnen.

Und jetzt kann dieser Abfrage fĂŒr das Diagramm verwendet werden.

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

das sich in folgendes SQL umwandelt (bitte beachten Sie, dass leere uriStem-Felder einfach in 1 umgewandelt wurden)

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

Fazit

Das Erscheinen der ClickHouse-Datenbank war ein bedeutendes Ereignis auf dem Markt. Es war schwer vorstellbar, dass wir in einem Moment völlig kostenlos mit einem leistungsstarken und praktischen Werkzeug fĂŒr die Verarbeitung großer Daten ausgestattet wurden. SelbstverstĂ€ndlich wird das Schema mit steigenden Anforderungen (zum Beispiel Sharding und Replikation auf mehreren Servern) komplexer. Aber nach den ersten EindrĂŒcken ist die Arbeit mit dieser Datenbank sehr angenehm. Man erkennt, dass das Produkt "fĂŒr die Menschen" gemacht wurde.

Im Vergleich zu ElasticSearch werden die Kosten fĂŒr die Speicherung und Verarbeitung von Protokollen vorlĂ€ufigen SchĂ€tzungen zufolge um das FĂŒnf- bis Zehnfache reduziert. Mit anderen Worten, wenn wir fĂŒr das aktuelle Datenvolumen einen Cluster aus mehreren Maschinen einrichten mĂŒssten, ist fĂŒr die Nutzung von ClickHouse eine schwache Maschine ausreichend. Ja, natĂŒrlich gibt es auch in ElasticSearch Mechanismen zur Datenkompression auf der Festplatte und andere Funktionen, die den Ressourcenverbrauch deutlich senken, aber im Vergleich zu ClickHouse erfordern diese einen höheren Aufwand.

Ohne besondere Optimierungen von unserer Seite und mit den Standard Einstellungen arbeiten der Datenupload und die Abfragen aus der Datenbank mit erstaunlicher Geschwindigkeit. Wir haben bisher nur wenig Daten (etwa 200 Millionen DatensĂ€tze), aber der Server ist schwach. Dieses Werkzeug können wir in Zukunft auch fĂŒr andere Zwecke nutzen, die nicht mit der Speicherung von Protokollen verbunden sind, zum Beispiel fĂŒr Durchanalyse, im Sicherheitsbereich, maschinelles Lernen.

Zum Schluss ein wenig ĂŒber die Vor- und Nachteile.

Nachteile

  1. Das Hochladen von DatensĂ€tzen in großen Chargen. Dies ist einerseits ein Feature, aber es mĂŒssen trotzdem zusĂ€tzliche Komponenten zur Pufferung der DatensĂ€tze verwendet werden. Diese Aufgabe ist nicht immer einfach, aber dennoch lösbar. Und wir möchten die Struktur vereinfachen.
  2. Einige exotische Funktionen oder neue Features brechen oft in neuen Versionen. Das fĂŒhrt zu Bedenken und verringert den Wunsch, auf die neue Version zu aktualisieren. Zum Beispiel ist die Kafka-Tabellen-Engine ein sehr nĂŒtzliches Feature, das es ermöglicht, Ereignisse direkt aus Kafka zu lesen, ohne Konsumenten zu implementieren. Aber judging by the number of issues on GitHub, scheuen wir uns bisher, diese Engine in der Produktion zu verwenden. Wenn man jedoch keine drastischen Schritte unternimmt und nur die Grundfunktionen nutzt, funktioniert sie stabil.

Vorteile

  1. Bremst nicht.
  2. Niedriger Einstieg.
  3. Open Source.
  4. Kostenlos.
  5. Gut skalierbar (Sharding/Replication „out of the box“)
  6. Ist im Register der empfohlenen russischen Software des MinKomSvyaz enthalten.
  7. Offizielle UnterstĂŒtzung von Yandex.

Quelle: habr.com

ZuverlĂ€ssiges Hosting fĂŒr Websites mit DDoS-Schutz kaufen, VPS VDS Server đŸ”„ ZuverlĂ€ssiges Hosting fĂŒr Websites mit DDoS-Schutz kaufen, VPS VDS Server - ProHoster