Base de données ClickHouse pour les humains ou technologies extraterrestres

Aleksey Lizunov, responsable du centre de compétences pour les canaux de service à distance de la direction des technologies de l'information du MKB

Base de données ClickHouse pour les humains ou technologies extraterrestres

Comme alternative à la pile ELK (ElasticSearch, Logstash, Kibana), nous effectuons des recherches sur l'utilisation de la base de données ClickHouse comme magasin de données pour les journaux.

Dans cet article, nous aimerions parler de notre expérience d'utilisation de la base de données ClickHouse et des résultats préliminaires de l'opération pilote. Il convient de noter tout de suite que les résultats ont été impressionnants.


Base de données ClickHouse pour les humains ou technologies extraterrestres

Ensuite, nous décrirons plus en détail comment notre système est configuré et de quels composants il se compose. Mais maintenant, je voudrais parler un peu de cette base de données dans son ensemble, et pourquoi il vaut la peine d'y prêter attention. La base de données ClickHouse est une base de données en colonnes analytique haute performance de Yandex. Il est utilisé dans les services Yandex, initialement c'est le principal stockage de données pour Yandex.Metrica. Système open source, gratuit. Du point de vue d'un développeur, je me suis toujours demandé comment ils l'ont implémenté, car il y a des données incroyablement volumineuses. Et l'interface utilisateur de Metrica elle-même est très flexible et rapide. A la première prise de connaissance de cette base de données, l'impression est : « Eh bien, enfin ! Fait pour le peuple ! À partir du processus d'installation et se terminant par l'envoi de demandes.

Cette base de données a un seuil d'entrée très bas. Même un développeur moyennement qualifié peut installer cette base de données en quelques minutes et commencer à l'utiliser. Tout fonctionne clairement. Même les personnes qui découvrent Linux peuvent gérer rapidement l'installation et effectuer les opérations les plus simples. Si auparavant, avec les mots Big Data, Hadoop, Google BigTable, HDFS, un développeur ordinaire avait des idées qu'il s'agissait de quelques téraoctets, pétaoctets, que certains surhumains sont impliqués dans les paramètres et le développement de ces systèmes, alors avec l'avènement du ClickHouse base de données, nous avons un outil simple et compréhensible avec lequel vous pouvez résoudre une gamme de tâches auparavant inaccessibles. Il ne faut qu'une machine assez moyenne et cinq minutes à installer. C'est-à-dire que nous avons une base de données telle que, par exemple, MySql, mais uniquement pour stocker des milliards d'enregistrements ! Un certain super-archiveur avec le langage SQL. C'est comme si les gens recevaient les armes des extraterrestres.

À propos de notre système de journalisation

Pour collecter des informations, les fichiers journaux IIS des applications Web au format standard sont utilisés (nous analysons également actuellement les journaux des applications, mais l'objectif principal à l'étape pilote est de collecter les journaux IIS).

Pour diverses raisons, nous ne pouvions pas complètement abandonner la pile ELK, et nous continuons à utiliser les composants LogStash et Filebeat, qui ont fait leurs preuves et fonctionnent de manière assez fiable et prévisible.

Le schéma général de journalisation est illustré dans la figure ci-dessous :

Base de données ClickHouse pour les humains ou technologies extraterrestres

Une caractéristique de l'écriture de données dans la base de données ClickHouse est l'insertion peu fréquente (une fois par seconde) d'enregistrements par lots volumineux. Ceci, apparemment, est la partie la plus "problématique" que vous rencontrez lorsque vous travaillez pour la première fois avec la base de données ClickHouse : le schéma devient un peu plus compliqué.
Le plugin pour LogStash, qui insère directement des données dans ClickHouse, a beaucoup aidé ici. Ce composant est déployé sur le même serveur que la base de données elle-même. Donc, d'une manière générale, il n'est pas recommandé de le faire, mais d'un point de vue pratique, afin de ne pas produire de serveurs séparés alors qu'il est déployé sur le même serveur. Nous n'avons observé aucune panne ou conflit de ressources avec la base de données. De plus, il convient de noter que le plugin dispose d'un mécanisme de nouvelle tentative en cas d'erreur. Et en cas d'erreurs, le plugin écrit sur disque un lot de données qui n'ont pas pu être insérées (le format de fichier est pratique : après édition, vous pouvez facilement insérer le lot corrigé à l'aide de clickhouse-client).

Une liste complète des logiciels utilisés dans le schéma est présentée dans le tableau :

Liste des logiciels utilisés

Nom

description

Lien de distribution

Nginx

Reverse-proxy pour restreindre l'accès par les ports et organiser l'autorisation

Actuellement non utilisé dans le schéma

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

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

FichierBeat

Transfert des journaux de fichiers.

https://www.elastic.co/downloads/beats/filebeat (kit de distribution pour Windows 64 bits).

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

logstash

Collecteur de journaux.

Utilisé pour collecter les journaux de FileBeat, ainsi que pour collecter les journaux de la file d'attente RabbitMQ (pour les serveurs qui se trouvent dans la DMZ.)

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

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

Logstash-sortie-clickhouse

Plugin Loagstash pour transférer les journaux vers la base de données ClickHouse par lots

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

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

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

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

Cliquez Maison

Stockage des journaux 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

Note. À partir d'août 2018, des versions de RPM "normales" pour RHEL sont apparues dans le référentiel Yandex, vous pouvez donc essayer de les utiliser. Au moment de l'installation, nous utilisions des packages construits par Altinity.

grafana

Visualisation du journal. Mise en place de tableaux de bord

https://grafana.com/

https://grafana.com/grafana/download

Redhat & Centos (64 bits) - dernière version

Source de données ClickHouse pour Grafana 4.6+

Plugin pour Grafana avec source de données ClickHouse

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

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

logstash

Connectez le routeur de FileBeat à la file d'attente RabbitMQ.

Note. Malheureusement, FileBeat ne sort pas directement vers RabbitMQ, donc un lien intermédiaire sous la forme de Logstash est requis

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

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

RabbitMQ

file d'attente de messages. Ceci est le tampon de journal dans la 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

Runtime Erlang (requis pour RabbitMQ)

Exécution Erlang. Requis pour que RabbitMQ fonctionne

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

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

La configuration du serveur avec la base de données ClickHouse est présentée dans le tableau suivant :

Nom

Valeur

Noter

Configuration

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

Il faut faire attention aux conseils d'utilisation de la base de données ClickHouse (https://clickhouse.yandex/docs/ru/operations/tips/)

Logiciel système général

Système d'exploitation : serveur Red Hat Enterprise Linux (Maipo)

JRE (Java 8)

 

Comme vous pouvez le voir, il s'agit d'un poste de travail ordinaire.

La structure de la table de stockage des logs est la suivante :

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;

Nous utilisons le partitionnement par défaut (par mois) et la granularité de l'index. Tous les champs correspondent pratiquement aux entrées du journal IIS pour la journalisation des requêtes http. Séparément, nous notons qu'il existe des champs séparés pour stocker les balises utm (elles sont analysées au stade de l'insertion dans la table à partir du champ de chaîne de requête).

De plus, plusieurs champs système ont été ajoutés à la table pour stocker des informations sur les systèmes, les composants et les serveurs. Voir le tableau ci-dessous pour une description de ces champs. Dans une table, nous stockons les journaux de plusieurs systèmes.

Nom

description

Exemple

fld_app_name

Nom de l'application/du système
Valeurs valides :

  • site1.domaine.com Site externe 1
  • site2.domaine.com Site externe 2
  • site-interne1.domaine.local Site interne 1

site1.domaine.com

fld_app_module

Module système
Valeurs valides :

  • Web - Site Web
  • svc - Service de site Web
  • intégr - Service Web d'intégration
  • bo - Administrateur (BackOffice)

web

fld_site_name

Nom du site dans IIS

Plusieurs systèmes peuvent être déployés sur un serveur, voire plusieurs instances d'un module système

Web principal

fld_server_name

Nom du serveur

web1.domaine.com

fld_log_file_name

Chemin d'accès au fichier journal sur le serveur

C:inetpublogsLogFiles
W3SVC1u_ex190711.log

Cela vous permet de construire efficacement des graphiques dans Grafana. Par exemple, affichez les demandes provenant de l'interface d'un système particulier. Ceci est similaire au compteur de site dans Yandex.Metrica.

Voici quelques statistiques sur l'utilisation de la base de données pendant deux mois.

Nombre d'enregistrements ventilés par systèmes et leurs composants

SELECT
    fld_app_name,
    fld_app_module,
    count(fld_app_name) AS rows_count
FROM log_web
GROUP BY
    fld_app_name,
    fld_app_module
    WITH TOTALS
ORDER BY
    fld_app_name ASC,
    rows_count DESC
 
┌─fld_app_name─────┬─fld_app_module─┬─rows_count─┐
│ site1.domain.ru  │ web            │     131441 │
│ site2.domain.ru  │ web            │    1751081 │
│ site3.domain.ru  │ web            │  106887543 │
│ site3.domain.ru  │ svc            │   44908603 │
│ site3.domain.ru  │ intgr          │    9813911 │
│ site4.domain.ru  │ web            │     772095 │
│ site5.domain.ru  │ web            │   17037221 │
│ site5.domain.ru  │ intgr          │     838559 │
│ site5.domain.ru  │ bo             │       7404 │
│ site6.domain.ru  │ web            │     595877 │
│ site7.domain.ru  │ web            │   27778858 │
└──────────────────┴────────────────┴────────────┘
 
Totals:
┌─fld_app_name─┬─fld_app_module─┬─rows_count─┐
│              │                │  210522593 │
└──────────────┴────────────────┴────────────┘
 
11 rows in set. Elapsed: 4.874 sec. Processed 210.52 million rows, 421.67 MB (43.19 million rows/s., 86.51 MB/s.)

La quantité de données sur le disque

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.

Degré de compression des données dans les colonnes

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.

Description des composants utilisés

battement de fichier. Transfert des journaux de fichiers

Ce composant suit les modifications apportées aux fichiers journaux sur le disque et transmet les informations à LogStash. Installé sur tous les serveurs où les fichiers journaux sont écrits (généralement IIS). Fonctionne en mode queue (c'est-à-dire ne transfère que les enregistrements ajoutés au fichier). Mais séparément, il peut être configuré pour transférer des fichiers entiers. Ceci est utile lorsque vous devez télécharger les données des mois précédents. Placez simplement le fichier journal dans un dossier et il le lira dans son intégralité.

Lorsque le service est arrêté, les données ne sont plus transférées vers le stockage.

Un exemple de configuration ressemble à ceci :

fichierbeat.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. Collecteur de journaux

Ce composant est conçu pour recevoir des entrées de journal de FileBeat (ou via la file d'attente RabbitMQ), en analysant et en insérant des lots dans la base de données ClickHouse.

Pour l'insertion dans ClickHouse, le plugin Logstash-output-clickhouse est utilisé. Le plugin Logstash a un mécanisme de nouvelle tentative de demande, mais avec un arrêt régulier, il est préférable d'arrêter le service lui-même. Lorsqu'il est arrêté, les messages s'accumulent dans la file d'attente RabbitMQ, donc si l'arrêt dure longtemps, il est préférable d'arrêter Filebeats sur les serveurs. Dans un schéma où RabbitMQ n'est pas utilisé (sur le réseau local, Filebeat envoie directement les logs à Logstash), Filebeats fonctionne de manière tout à fait acceptable et sécurisée, donc pour eux l'indisponibilité de la sortie passe sans conséquence.

Un exemple de configuration ressemble à ceci :

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. Stockage des journaux

Les journaux de tous les systèmes sont stockés dans une table (voir au début de l'article). Il est destiné à stocker des informations sur les requêtes : tous les paramètres sont similaires pour différents formats, tels que les journaux IIS, apache et nginx. Pour les journaux d'application, dans lesquels, par exemple, des erreurs, des messages d'information, des avertissements sont enregistrés, un tableau séparé sera fourni avec la structure appropriée (actuellement au stade de la conception).

Lors de la conception d'une table, il est très important de décider de la clé primaire (par laquelle les données seront triées lors du stockage). Le degré de compression des données et la vitesse des requêtes en dépendent. Dans notre exemple, la clé est
ORDER BY (fld_app_name, fld_app_module, logdatetime)
C'est-à-dire par le nom du système, le nom du composant système et la date de l'événement. Initialement, la date de l'événement venait en premier. Après l'avoir déplacé à la dernière place, les requêtes ont commencé à fonctionner environ deux fois plus vite. La modification de la clé primaire nécessitera de recréer la table et de recharger les données afin que ClickHouse trie à nouveau les données sur le disque. Il s'agit d'une opération lourde, c'est donc une bonne idée de bien réfléchir à ce qui doit être inclus dans la clé de tri.

Il convient également de noter que le type de données LowCardinality est apparu dans des versions relativement récentes. Lors de son utilisation, la taille des données compressées est considérablement réduite pour les champs à faible cardinalité (peu d'options).

La version 19.6 est actuellement utilisée et nous prévoyons d'essayer de mettre à jour vers la dernière version. Ils ont des fonctionnalités aussi merveilleuses que la granularité adaptative, les indices de saut et le codec DoubleDelta, par exemple.

Par défaut, lors de l'installation, le niveau de journalisation est défini sur trace. Les journaux sont tournés et archivés, mais en même temps, ils s'étendent jusqu'à un gigaoctet. Si ce n'est pas nécessaire, vous pouvez définir le niveau d'avertissement, puis la taille du journal est considérablement réduite. Le paramètre de journalisation est défini dans le fichier config.xml :

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

Quelques commandes utiles

Поскольку оригинальные пакеты установки собираются по 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. Journaliser le routeur de FileBeat vers la file d'attente RabbitMQ

Ce composant est utilisé pour acheminer les journaux provenant de FileBeat vers la file d'attente RabbitMQ. Il y a deux points ici:

  1. Malheureusement, FileBeat n'a pas de plugin de sortie pour écrire directement sur RabbitMQ. Et une telle fonctionnalité, à en juger par le problème sur leur github, n'est pas prévue pour la mise en œuvre. Il existe un plugin pour Kafka, mais pour une raison quelconque, nous ne pouvons pas l'utiliser à la maison.
  2. Il existe des exigences pour la collecte des journaux dans la DMZ. Sur cette base, les journaux doivent d'abord être ajoutés à la file d'attente, puis LogStash lit les entrées de la file d'attente depuis l'extérieur.

Par conséquent, c'est pour le cas où les serveurs sont situés dans la DMZ qu'il faut utiliser un schéma aussi légèrement compliqué. Un exemple de configuration ressemble à ceci :

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

LapinMQ. file d'attente de messages

Ce composant est utilisé pour mettre en mémoire tampon les entrées de journal dans la DMZ. L'enregistrement se fait via un tas de Filebeat → LogStash. La lecture se fait depuis l'extérieur de la DMZ via LogStash. Lors de l'utilisation de RabboitMQ, environ 4 XNUMX messages par seconde sont traités.

Le routage des messages est configuré par nom de système, c'est-à-dire basé sur les données de configuration FileBeat. Tous les messages vont dans une file d'attente. Si, pour une raison quelconque, le service de mise en file d'attente est arrêté, cela n'entraînera pas la perte de messages : FileBeats recevra des erreurs de connexion et suspendra temporairement l'envoi. Et LogStash qui lit à partir de la file d'attente recevra également des erreurs de réseau et attendra que la connexion soit restaurée. Dans ce cas, les données ne seront bien sûr plus écrites dans la base de données.

Les instructions suivantes sont utilisées pour créer et configurer des files d'attente :

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

Ce composant est utilisé pour visualiser les données de surveillance. Dans ce cas, vous devez installer la source de données ClickHouse pour le plug-in Grafana 4.6+. Nous avons dû le modifier un peu pour améliorer l'efficacité du traitement des filtres SQL sur le tableau de bord.

Par exemple, nous utilisons des variables, et si elles ne sont pas définies dans le champ du filtre, nous aimerions qu'il ne génère pas de condition dans le WHERE du formulaire ( uriStem = » AND uriStem != » ). Dans ce cas, ClickHouse lira la colonne uriStem. En général, nous avons essayé différentes options et avons finalement corrigé le plugin (la macro $valueIfEmpty) pour que dans le cas d'une valeur vide, il renvoie 1, sans mentionner la colonne elle-même.

Et maintenant, vous pouvez utiliser cette requête pour le graphique

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

qui se traduit par ce SQL (notez que les champs uriStem vides ont été convertis en seulement 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

Conclusion

L'apparition de la base de données ClickHouse est devenue un événement marquant sur le marché. Il était difficile d'imaginer que, totalement gratuit, nous disposions en un instant d'un outil puissant et pratique pour travailler avec le big data. Bien sûr, avec l'augmentation des besoins (par exemple, le sharding et la réplication sur plusieurs serveurs), le schéma deviendra plus compliqué. Mais à première vue, travailler avec cette base de données est très agréable. On peut voir que le produit est fait "pour les gens".

Par rapport à ElasticSearch, le coût de stockage et de traitement des journaux est estimé être réduit de cinq à dix fois. En d'autres termes, si pour la quantité actuelle de données, nous devions configurer un cluster de plusieurs machines, alors lors de l'utilisation de ClickHouse, une machine à faible consommation nous suffit. Oui, bien sûr, ElasticSearch dispose également de mécanismes de compression de données sur disque et d'autres fonctionnalités qui peuvent réduire considérablement la consommation de ressources, mais par rapport à ClickHouse, cela coûtera plus cher.

Sans aucune optimisation spéciale de notre part, sur les paramètres par défaut, le chargement des données et la sélection dans la base de données fonctionnent à une vitesse incroyable. Nous n'avons pas encore beaucoup de données (environ 200 millions d'enregistrements), mais le serveur lui-même est faible. Nous pourrons utiliser cet outil à l'avenir à d'autres fins non liées au stockage des journaux. Par exemple, pour l'analyse de bout en bout, dans le domaine de la sécurité, l'apprentissage automatique.

À la fin, un peu sur les avantages et les inconvénients.

Moins

  1. Chargement d'enregistrements par lots volumineux. D'une part, il s'agit d'une fonctionnalité, mais vous devez toujours utiliser des composants supplémentaires pour mettre en mémoire tampon les enregistrements. Cette tâche n'est pas toujours facile, mais toujours résoluble. Et je voudrais simplifier le schéma.
  2. Certaines fonctionnalités exotiques ou de nouvelles fonctionnalités se cassent souvent dans les nouvelles versions. Cela suscite des inquiétudes, réduisant le désir de passer à une nouvelle version. Par exemple, le moteur de table Kafka est une fonctionnalité très utile qui vous permet de lire directement les événements de Kafka, sans implémenter de consommateurs. Mais à en juger par le nombre de Issues sur le github, nous faisons tout de même attention à ne pas utiliser ce moteur en production. Cependant, si vous ne faites pas de gestes brusques sur le côté et n'utilisez pas la fonctionnalité principale, cela fonctionne de manière stable.

Avantages

  1. Ne ralentit pas.
  2. Seuil d'entrée bas.
  3. Open source
  4. Gratuit.
  5. Évolue bien (sharding/réplication prête à l'emploi)
  6. Inclus dans le registre des logiciels russes recommandés par le ministère des Communications.
  7. La présence du soutien officiel de Yandex.

Source: habr.com

Ajouter un commentaire