ClickHouse-Datenbank für Menschen oder außerirdische Technologien

Aleksey Lizunov, Leiter des Kompetenzzentrums für Remote-Service-Kanäle der Direktion für Informationstechnologien des MKB

ClickHouse-Datenbank für Menschen oder außerirdische Technologien

Als Alternative zum ELK-Stack (ElasticSearch, Logstash, Kibana) forschen wir an der Verwendung der ClickHouse-Datenbank als Datenspeicher für Protokolle.

In diesem Artikel möchten wir über unsere Erfahrungen mit der Nutzung der ClickHouse-Datenbank und die vorläufigen Ergebnisse des Pilotbetriebs sprechen. Es sollte sofort festgestellt werden, dass die Ergebnisse beeindruckend waren.


ClickHouse-Datenbank für Menschen oder außerirdische Technologien

Als nächstes beschreiben wir genauer, wie unser System konfiguriert ist und aus welchen Komponenten es besteht. Aber jetzt möchte ich ein wenig über diese Datenbank als Ganzes sprechen und warum es sich lohnt, ihr Aufmerksamkeit zu schenken. Die ClickHouse-Datenbank ist eine leistungsstarke analytische Spaltendatenbank von Yandex. Es wird in Yandex-Diensten verwendet und ist zunächst der Hauptdatenspeicher für Yandex.Metrica. Open-Source-System, kostenlos. Aus Entwicklersicht habe ich mich immer gefragt, wie sie das umgesetzt haben, denn es gibt fantastisch große Datenmengen. Und die Benutzeroberfläche von Metrica selbst ist sehr flexibel und schnell. Beim ersten Kennenlernen dieser Datenbank entsteht der Eindruck: „Na endlich! Für die Menschen gemacht! Beginnend mit dem Installationsprozess und endend mit dem Versenden von Anfragen.

Diese Datenbank hat eine sehr niedrige Eintrittsschwelle. Selbst ein durchschnittlich erfahrener Entwickler kann diese Datenbank in wenigen Minuten installieren und mit der Nutzung beginnen. Alles funktioniert eindeutig. Selbst Linux-Neulinge können die Installation schnell bewältigen und die einfachsten Vorgänge ausführen. Wenn früher, mit den Worten Big Data, Hadoop, Google BigTable, HDFS, ein gewöhnlicher Entwickler die Vorstellung hatte, dass es um einige Terabyte, Petabyte geht, dass einige Übermenschen mit der Konfiguration und Entwicklung dieser Systeme beschäftigt sind, dann mit dem Aufkommen von ClickHouse Datenbank haben wir ein einfaches, verständliches Werkzeug erhalten, mit dem Sie ein bisher unerreichbares Aufgabenspektrum lösen können. Die Installation dauert nur eine relativ durchschnittliche Maschine und fünf Minuten. Das heißt, wir haben eine Datenbank wie zum Beispiel MySql, aber nur zum Speichern von Milliarden von Datensätzen! Ein gewisser Super-Archiver mit der SQL-Sprache. Es ist, als ob den Menschen die Waffen von Außerirdischen in die Hand gedrückt würden.

Über unser Protokollierungssystem

Zum Sammeln von Informationen werden IIS-Protokolldateien von Webanwendungen im Standardformat verwendet (wir analysieren derzeit auch Anwendungsprotokolle, aber das Hauptziel in der Pilotphase ist das Sammeln von IIS-Protokollen).

Aus verschiedenen Gründen konnten wir nicht ganz auf den ELK-Stack verzichten und verwenden weiterhin die Komponenten LogStash und Filebeat, die sich bewährt haben und recht zuverlässig und vorhersehbar funktionieren.

Das allgemeine Protokollierungsschema ist in der folgenden Abbildung dargestellt:

ClickHouse-Datenbank für Menschen oder außerirdische Technologien

Ein Merkmal des Schreibens von Daten in die ClickHouse-Datenbank ist das seltene (einmal pro Sekunde) Einfügen von Datensätzen in großen Stapeln. Dies ist offenbar der „problematischste“ Teil, auf den Sie stoßen, wenn Sie zum ersten Mal mit der ClickHouse-Datenbank arbeiten: Das Schema wird etwas komplizierter.
Hier hat das Plugin für LogStash, das Daten direkt in ClickHouse einfügt, sehr geholfen. Diese Komponente wird auf demselben Server bereitgestellt wie die Datenbank selbst. Daher wird dies im Allgemeinen nicht empfohlen, aus praktischer Sicht jedoch, um keine separaten Server zu erstellen, während es auf demselben Server bereitgestellt wird. Wir haben keine Ausfälle oder Ressourcenkonflikte mit der Datenbank beobachtet. Darüber hinaus ist zu beachten, dass das Plugin über einen Wiederholungsmechanismus im Fehlerfall verfügt. Und im Fehlerfall schreibt das Plugin einen Datenstapel, der nicht eingefügt werden konnte, auf die Festplatte (das Dateiformat ist praktisch: Nach der Bearbeitung können Sie den korrigierten Stapel einfach mit Clickhouse-Client einfügen).

Eine vollständige Liste der im Schema verwendeten Software ist in der Tabelle aufgeführt:

Liste der verwendeten Software

Name

Beschreibung

Vertriebslink

NGINX

Reverse-Proxy, um den Zugriff nach Ports einzuschränken und die Autorisierung zu organisieren

Wird derzeit im Schema nicht verwendet

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

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

FileBeat

Übertragung von Dateiprotokollen.

https://www.elastic.co/downloads/beats/filebeat (Verteilungskit für Windows 64bit).

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

Logstash

Protokollsammler.

Wird zum Sammeln von Protokollen von FileBeat sowie zum Sammeln von Protokollen aus der RabbitMQ-Warteschlange (für Server in der DMZ) verwendet.

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

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

Logstash-Ausgabe-Clickhouse

Loagstash-Plugin zum stapelweisen Übertragen von Protokollen in die ClickHouse-Datenbank

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

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

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

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

Clickhouse

Protokollspeicher 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

Notiz. Ab August 2018 erschienen im Yandex-Repository „normale“ RPM-Builds für RHEL, sodass Sie sie ausprobieren können. Zum Zeitpunkt der Installation verwendeten wir von Altinity erstellte Pakete.

Grafana

Protokollvisualisierung. Dashboards einrichten

https://grafana.com/

https://grafana.com/grafana/download

Redhat & Centos (64 Bit) – neueste Version

ClickHouse-Datenquelle für Grafana 4.6+

Plugin für Grafana mit ClickHouse-Datenquelle

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

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

Logstash

Log-Router von FileBeat zur RabbitMQ-Warteschlange.

Notiz. Leider gibt FileBeat nicht direkt an RabbitMQ aus, daher ist ein Zwischenlink 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 der Protokollpuffer 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 (erforderlich für RabbitMQ)

Erlang-Laufzeit. Erforderlich, damit RabbitMQ funktioniert

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 wird in der folgenden Tabelle dargestellt:

Name

Wert

Beachten

Konfiguration

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

Beachten Sie unbedingt die Tipps zum Betrieb der ClickHouse-Datenbank (https://clickhouse.yandex/docs/ru/operations/tips/)

Allgemeine Systemsoftware

Betriebssystem: Red Hat Enterprise Linux Server (Maipo)

JRE (Java 8)

 

Wie Sie sehen, handelt es sich um eine gewöhnliche Workstation.

Die Struktur der Tabelle zum Speichern von Protokollen ist wie folgt:

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 Standardpartitionierung (nach Monat) und die Indexgranularität. Alle Felder entsprechen praktisch IIS-Protokolleinträgen zur Protokollierung von http-Anfragen. Unabhängig davon stellen wir fest, dass es separate Felder zum Speichern von utm-Tags gibt (sie werden beim Einfügen in die Tabelle aus dem Abfragezeichenfolgenfeld analysiert).

Außerdem wurden der Tabelle mehrere Systemfelder hinzugefügt, um Informationen über Systeme, Komponenten und Server zu speichern. Eine Beschreibung dieser Felder finden Sie in der folgenden Tabelle. In einer Tabelle speichern wir Protokolle für mehrere Systeme.

Name

Beschreibung

Beispiel

fld_app_name

Anwendungs-/Systemname
Gültige Werte:

  • site1.domain.com Externe Seite 1
  • site2.domain.com Externe Seite 2
  • internal-site1.domain.local Interne Site 1

site1.domain.com

fld_app_module

Systemmodul
Gültige Werte:

  • web - Website
  • svc – Website-Webdienst
  • intgr – Integrations-Webdienst
  • bo – Admin (BackOffice)

Netz

fld_website_name

Site-Name in IIS

Auf einem Server können mehrere Systeme oder sogar mehrere Instanzen eines Systemmoduls bereitgestellt werden

Web-Hauptseite

fld_server_name

Servername

web1.domain.com

fld_log_file_name

Pfad zur Protokolldatei auf dem Server

C:inetpublogsLogFiles
W3SVC1u_ex190711.log

Dadurch können Sie Diagramme in Grafana effizient erstellen. Zeigen Sie beispielsweise Anfragen vom Frontend eines bestimmten Systems an. Dies ähnelt dem Site-Zähler in Yandex.Metrica.

Hier finden Sie einige Statistiken zur Nutzung der Datenbank für zwei Monate.

Anzahl der Datensätze, aufgeschlüsselt nach Systemen und ihren 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.)

Die Datenmenge 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.

Grad der Datenkomprimierung in 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 rows in set. Elapsed: 0.005 sec.

Beschreibung der verwendeten Komponenten

Filebeat. Dateiprotokolle übertragen

Diese Komponente verfolgt Änderungen an Protokolldateien auf der Festplatte und übergibt die Informationen an LogStash. Wird auf allen Servern installiert, auf denen Protokolldateien geschrieben werden (normalerweise IIS). Funktioniert im Tail-Modus (d. h. es werden nur die hinzugefügten Datensätze in die Datei übertragen). Aber separat kann es so konfiguriert werden, dass ganze Dateien übertragen werden. Dies ist nützlich, wenn Sie Daten aus früheren Monaten herunterladen müssen. Legen Sie die Protokolldatei einfach in einem Ordner ab und sie wird vollständig gelesen.

Bei Beendigung des Dienstes werden die Daten nicht mehr weiter in den Speicher übertragen.

Eine Beispielkonfiguration sieht so 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

Diese Komponente dient dazu, Protokolleinträge von FileBeat (oder über die RabbitMQ-Warteschlange) zu empfangen, Stapel zu analysieren und in die ClickHouse-Datenbank einzufügen.

Zum Einfügen in ClickHouse wird das Plugin Logstash-output-clickhouse verwendet. Das Logstash-Plugin verfügt über einen Mechanismus zur Wiederholung von Anfragen. Bei einem regelmäßigen Herunterfahren ist es jedoch besser, den Dienst selbst zu stoppen. Beim Anhalten sammeln sich Nachrichten in der RabbitMQ-Warteschlange an. Wenn der Stopp also über einen längeren Zeitraum erfolgt, ist es besser, Filebeats auf den Servern zu stoppen. In einem Schema, in dem RabbitMQ nicht verwendet wird (im lokalen Netzwerk sendet Filebeat Protokolle direkt an Logstash), funktionieren Filebeats recht akzeptabel und sicher, sodass die Nichtverfügbarkeit der Ausgabe für sie ohne Folgen bleibt.

Eine Beispielkonfiguration sieht so 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. Protokollspeicher

Protokolle für alle Systeme werden in einer Tabelle gespeichert (siehe am Anfang des Artikels). Es soll Informationen über Anfragen speichern: Alle Parameter sind für verschiedene Formate ähnlich, wie z. B. IIS-Protokolle, Apache- und Nginx-Protokolle. Für Anwendungsprotokolle, in denen beispielsweise Fehler, Informationsmeldungen, Warnungen aufgezeichnet werden, wird eine separate Tabelle mit entsprechender Struktur bereitgestellt (derzeit im Entwurfsstadium).

Beim Entwerfen einer Tabelle ist es sehr wichtig, den Primärschlüssel festzulegen (nach dem die Daten während der Speicherung sortiert werden). Davon hängen der Grad der Datenkomprimierung und die Abfragegeschwindigkeit ab. In unserem Beispiel ist der Schlüssel
ORDER BY (fld_app_name, fld_app_module, logdatetime)
Das heißt, durch den Namen des Systems, den Namen der Systemkomponente und das Datum des Ereignisses. Zunächst stand das Datum der Veranstaltung an erster Stelle. Nach dem Verschieben an die letzte Stelle begannen Abfragen etwa doppelt so schnell zu funktionieren. Wenn Sie den Primärschlüssel ändern, müssen Sie die Tabelle neu erstellen und die Daten neu laden, damit ClickHouse die Daten auf der Festplatte neu sortiert. Da es sich hierbei um einen aufwändigen Vorgang handelt, empfiehlt es sich, gründlich darüber nachzudenken, was in den Sortierschlüssel aufgenommen werden soll.

Es sollte auch beachtet werden, dass der Datentyp LowCardinality in relativ neuen Versionen aufgetaucht ist. Bei der Verwendung wird die Größe der komprimierten Daten für Felder mit geringer Kardinalität (wenige Optionen) drastisch reduziert.

Derzeit wird Version 19.6 verwendet und wir planen, ein Update auf die neueste Version zu versuchen. Sie verfügen beispielsweise über so wunderbare Funktionen wie Adaptive Granularity, Skipping-Indizes und den DoubleDelta-Codec.

Standardmäßig ist während der Installation die Protokollierungsstufe auf „Trace“ eingestellt. Die Protokolle werden rotiert und archiviert, gleichzeitig erweitern sie sich aber auf bis zu ein Gigabyte. Wenn kein Bedarf besteht, können Sie die Warnstufe festlegen, dann wird die Größe des Protokolls drastisch reduziert. Die Protokollierungseinstellung wird in der Datei config.xml festgelegt:

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

Einige nützliche Befehle

Поскольку оригинальные пакеты установки собираются по 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. Log-Router von FileBeat zur RabbitMQ-Warteschlange

Diese Komponente wird verwendet, um von FileBeat kommende Protokolle an die RabbitMQ-Warteschlange weiterzuleiten. Hier gibt es zwei Punkte:

  1. Leider verfügt FileBeat nicht über ein Ausgabe-Plugin, um direkt in RabbitMQ zu schreiben. Und eine solche Funktionalität ist, dem Problem auf ihrem Github nach zu urteilen, nicht für die Implementierung geplant. Es gibt ein Plugin für Kafka, aber aus irgendeinem Grund können wir es zu Hause nicht verwenden.
  2. Für das Sammeln von Protokollen in der DMZ gelten Anforderungen. Darauf aufbauend müssen die Logs zunächst zur Warteschlange hinzugefügt werden und anschließend liest LogStash die Einträge aus der Warteschlange von außen.

Daher muss man für den Fall, dass sich Server in der DMZ befinden, ein derart etwas kompliziertes Schema verwenden. Eine Beispielkonfiguration sieht so 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 dient der Pufferung von Protokolleinträgen in der DMZ. Die Aufzeichnung erfolgt über eine Reihe von Filebeat → LogStash. Das Auslesen erfolgt von außerhalb der DMZ über LogStash. Beim Betrieb über RabboitMQ werden etwa 4 Nachrichten pro Sekunde verarbeitet.

Die Nachrichtenweiterleitung wird nach Systemnamen konfiguriert, d. h. basierend auf FileBeat-Konfigurationsdaten. Alle Nachrichten werden in eine Warteschlange gestellt. Wenn der Warteschlangendienst aus irgendeinem Grund gestoppt wird, führt dies nicht zum Verlust von Nachrichten: FileBeats empfängt Verbindungsfehler und unterbricht den Versand vorübergehend. Und LogStash, das aus der Warteschlange liest, empfängt auch Netzwerkfehler und wartet auf die Wiederherstellung der Verbindung. In diesem Fall werden die Daten natürlich nicht mehr in die Datenbank geschrieben.

Die folgenden Anweisungen werden zum Erstellen und Konfigurieren 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 dient der Visualisierung von Überwachungsdaten. In diesem Fall müssen Sie die ClickHouse-Datenquelle für das Grafana 4.6+-Plugin installieren. Wir mussten es ein wenig optimieren, um die Effizienz der Verarbeitung von SQL-Filtern im Dashboard zu verbessern.

Wir verwenden beispielsweise Variablen, und wenn diese nicht im Filterfeld festgelegt sind, möchten wir, dass keine Bedingung im WHERE des Formulars generiert wird ( uriStem = » AND uriStem != » ). In diesem Fall liest ClickHouse die uriStem-Spalte. Im Allgemeinen haben wir verschiedene Optionen ausprobiert und schließlich das Plugin (das $valueIfEmpty-Makro) so korrigiert, dass es im Falle eines leeren Werts 1 zurückgibt, ohne die Spalte selbst zu erwähnen.

Und jetzt können Sie diese Abfrage für das Diagramm verwenden

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

was in dieses SQL übersetzt wird (beachten Sie, dass die leeren uriStem-Felder in nur 1 konvertiert 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

Abschluss

Das Erscheinen der ClickHouse-Datenbank ist zu einem Meilenstein auf dem Markt geworden. Es war schwer vorstellbar, dass wir völlig kostenlos im Handumdrehen mit einem leistungsstarken und praktischen Tool für die Arbeit mit Big Data ausgestattet waren. Natürlich wird das Schema mit steigenden Anforderungen (z. B. Sharding und Replikation auf mehrere Server) komplizierter. Aber auf den ersten Blick ist die Arbeit mit dieser Datenbank sehr angenehm. Man erkennt, dass das Produkt „für Menschen“ gemacht ist.

Im Vergleich zu ElasticSearch werden die Kosten für die Speicherung und Verarbeitung von Protokollen schätzungsweise um das Fünf- bis Zehnfache reduziert. Mit anderen Worten: Wenn wir für die aktuelle Datenmenge einen Cluster aus mehreren Maschinen einrichten müssten, dann reicht uns bei der Verwendung von ClickHouse eine Maschine mit geringem Stromverbrauch. Ja, natürlich verfügt ElasticSearch auch über Datenkomprimierungsmechanismen auf der Festplatte und andere Funktionen, die den Ressourcenverbrauch erheblich reduzieren können, aber im Vergleich zu ClickHouse ist dies teurer.

Ohne besondere Optimierungen unsererseits funktioniert das Laden der Daten und die Auswahl aus der Datenbank auf Grund der Standardeinstellungen mit erstaunlicher Geschwindigkeit. Wir haben noch nicht viele Daten (ungefähr 200 Millionen Datensätze), aber der Server selbst ist schwach. Wir können dieses Tool in Zukunft für andere Zwecke verwenden, die nicht mit der Speicherung von Protokollen zusammenhängen. Zum Beispiel für End-to-End-Analysen, im Bereich Sicherheit, maschinelles Lernen.

Zum Schluss noch ein wenig zu den Vor- und Nachteilen.

Cons

  1. Datensätze in großen Mengen laden. Dies ist einerseits ein Feature, dennoch müssen Sie für die Pufferung von Datensätzen zusätzliche Komponenten einsetzen. Diese Aufgabe ist nicht immer einfach, aber dennoch lösbar. Und ich möchte das Schema vereinfachen.
  2. Einige exotische Funktionen oder neue Features funktionieren in neuen Versionen oft nicht mehr. Dies gibt Anlass zur Sorge und verringert den Wunsch, auf eine neue Version zu aktualisieren. Beispielsweise ist die Kafka-Tabellen-Engine eine sehr nützliche Funktion, die es Ihnen ermöglicht, Ereignisse direkt aus Kafka zu lesen, ohne Consumer zu implementieren. Aber gemessen an der Anzahl der Probleme auf dem Github sind wir immer noch vorsichtig, diese Engine nicht in der Produktion zu verwenden. Wenn Sie jedoch keine plötzlichen Gesten zur Seite machen und die Hauptfunktionalität nutzen, funktioniert es stabil.

Pros

  1. Verlangsamt nicht.
  2. Niedrige Eintrittsschwelle.
  3. Open Source.
  4. Frei.
  5. Lässt sich gut skalieren (Sharding/Replikation sofort einsatzbereit)
  6. Im Verzeichnis der vom Kommunikationsministerium empfohlenen russischen Software enthalten.
  7. Das Vorhandensein offizieller Unterstützung von Yandex.

Source: habr.com

Kommentar hinzufügen