BD ClickHouse за хората или Технологии на извънземните

Алексей Лизунов, ръководител на направление център за компетенции на дистанционни канали за обслужване на дирекцията по информационни технологии на МКБ

BD ClickHouse за хората или Технологии на извънземните

Като алтернатива на стека ELK (ElasticSearch, Logstash, Kibana), провеждаме изследователски работи по използването на базата данни ClickHouse като хранилище за данни за логовете.

В тази статия бихме искали да споделим нашия опит с базата данни ClickHouse и предварителните резултати след пилотното й използване. Следва да подчертаем, че резултатите са впечатляващи.


BD ClickHouse за хората или Технологии на извънземните

Нататък ще опишем по-подробно как е настроена нашата система и от какви компоненти се състои. Но сега искаме да споделим малко за тази база данни като цяло и защо си заслужава да се обърне внимание на нея. Базата данни ClickHouse е високопроизводителна аналитична колоночна база данни от Яндекс. Използва се в услугите на Яндекс и първоначално е основно хранилище за данни за Яндекс.Метрика. Системата е с отворен код и е безплатна. От гледна точка на разработчика, винаги съм се чудил как е реализирано всичко това, тъй като данните там са фантастично огромни. И потребителският интерфейс на Метрика е много гъвкав и работи бързо. При първото запознаване с тази база данни впечатлението е: "Най-накрая! Направено е "за хората"! Започвайки от процеса на инсталиране и завършвайки с изпращането на заявки."

Тази база данни има много нисък входен праг. Дори разработчик със средна квалификация може да инсталира тази база данни за няколко минути и да започне да я използва. Всичко работи точно. Дори хора, които не са добре запознати с Linux, бързо могат да се справят с инсталацията и да правят елементарни операции. Ако преди, при споменаване на Big Data, Hadoop, Google BigTable, HDFS, обикновеният разработчик си е представял, че става дума за терабайти, петабайти и настройките и разработката за тези системи се извършват от някакви суперчовеци, то с появата на базата данни ClickHouse получихме прост, разбираем инструмент, с помощта на който можем да решаваме досега недостижими задачи. Достатъчно е само една доста средна машина и пет минути за инсталация. Тоест получихме база данни като MySQL, но само за съхранение на милиарди записи! Нещо като суперархиватор с SQL език. Все едно хората получиха оръжие от извънземни.

През нашата система за събиране на логове

За събиране на информация се използват файлове с логове на IIS уеб приложения с стандартен формат (в момента също така работим и с парсване на логове на приложения, но основната цел през пилотната фаза е събирането на логове на IIS).

Напълно да се откажем от стек ELK не успяхме по различни причини, и продължаваме да използваме компонентите LogStash и Filebeat, които се показаха добре и работят надеждно и предсказуемо.

Общата схема на логиране е представена на изображението по-долу:

BD ClickHouse за хората или Технологии на извънземните

Характерна особеност при записването на данни в БД ClickHouse е рядкото (веднъж в секунда) вмъкване на записи на големи пакети. Това, изглежда, е най-проблематичната част, с която се сблъсквате при първото си взаимодействие с БД ClickHouse: схемата е малко по-сложна.
Тук много помогна плъгинът за LogStash, който директно вмъква данни в ClickHouse. Този компонент се разгръща на същия сървър, на който е и самата БД. Макар, че обикновено не се препоръчва да се прави това, от практическа гледна точка, за да не създаваме отделни сървъри, той все пак е разширен на същия сървър. Не наблюдавахме нито сривове, нито конфликти с ресурсите на БД. Освен това трябва да се отбележи, че плъгинът разполага с механизъм за повторно опитване в случай на грешки. При проблеми плъгинът запазва на диск пакет от данни, които не успя да вмъкне (форматът на файла е удобен: след корекция, можете лесно да вмъкнете поправен пакет с помощта на clickhouse-client).

Пълният списък на софтуера, използван в схемата, е представен в таблицата:

Списък на използвания софтуер

Име

Описание

Линк към дистрибутив

NGINX

Reverse-proxy за ограничаване на достъпа по портове и организиране на авторизация

В момента не се използва в схемата

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

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

FileBeat

Предаване на файлови логове.

https://www.elastic.co/downloads/beats/filebeat (дистрибутив за Windows 64bit).

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

LogStash

Събирач на логове.

Използва се за събиране на логове от FileBeat, както и за събиране на логове от опашката RabbitMQ (за сървъри, които се намират в DMZ).

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

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

Logstash-output-clickhouse

Плъгин Logstash за предаване на логове в БД ClickHouse на пакети

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

Хранилище на логовете 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

Забележка. От август 2018 г. в репозитория на Яндекс се появиха 'нормални' сборки rpm за RHEL, така че може да се опитате да ги използвате. По времето на инсталацията използвахме пакети, събрани от Altinity.

Grafana

Визуализация на логовете. Настройка на дашборди

https://grafana.com/

https://grafana.com/grafana/download

Redhat & Centos(64 Bit) – последната версия

ClickHouse datasource for Grafana 4.6+

Плъгин за Grafana с източник на данни ClickHouse

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

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

LogStash

Лог маршрутизатор от FileBeat в RabbitMQ опашка.

Забележка. За съжаление, FileBeat няма директен изход в RabbitMQ, затова е необходимо междинно звено под формата на Logstash

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

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

RabbitMQ

Опашка за съобщения. Това е буфер за записи на логове в 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 (Необходимо за RabbitMQ)

Erlang среда за изпълнение. Изисква се за работа на RabbitMQ

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

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

Конфигурацията на сървъра с ClickHouse БД е представена в следната таблица:

Име

Стойност

Забележка

Конфигурация

HDD: 40GB
RAM: 8GB
Процесор: Core 2 2Ghz

Необходимо е да се обърне внимание на съветите за работа с ClickHouse БД (https://clickhouse.yandex/docs/ru/operations/tips/)

Общи системни софтуер

ОС: Red Hat Enterprise Linux Server (Maipo)

JRE (Java 8)

 

Както е видно, това е обикновена работна станция.

Структурата на таблицата за съхранение на логовете изглежда по следния начин:

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;

Използваме стойностите по подразбиране за партициониране (по месеци) и грануларност на индекса. Всички полета практически съответстват на записите на IIS логове за HTTP заявки. Ос individually, се отбелязват отделни полета за съхранение на UTM етикети (те се парсват на етапа на вмъкване в таблицата от полето на заявката).

В таблицата са добавени и няколко системни полета за съхранение на информация за системите, компонентите, сървърите. Описанието на тези полета е посочено по-долу в таблицата. В една таблица съхраняваме логове за няколко системи.

Име

Описание

Пример

fld_app_name

Име на приложението/системата
Допустими стойности:

  • site1.domain.com Външен сайт 1
  • site2.domain.com Външен сайт 2
  • internal-site1.domain.local Вътрешен сайт 1

site1.domain.com

fld_app_module

Модул на системата
Допустими стойности:

  • web — Уебсайт
  • svc — Уебсервиз на сайта
  • intgr — Уебсервиз за интеграция
  • bo — Административен панел (BackOffice)

web

fld_website_name

Име на сайта в IIS

На един сървър може да бъдат разположени няколко системи или дори няколко екземпляра на един модул на системата

web-main

fld_server_name

Име на сървъра

web1.domain.com

fld_log_file_name

Път до лог файла на сървъра

C:inetpublogsLogFiles
W3SVC1u_ex190711.log

Това позволява ефективно изграждане на графики в Grafana. Например, да се преглеждат заявки от фронтенда на конкретна система. Това е подобно на брояча на сайта в Яндекс.Метрика.

Ето някои статистики за използването на базата данни през последните два месеца.

Брой записи, разделен по системи и техните компоненти

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

Обемът на данните на диска

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.

Степента на компресия на данните в колоните

ИЗБЕРЕТЕ
    имя,
    formatReadableSize(data_uncompressed_bytes) КАК uncompressed,
    formatReadableSize(data_compressed_bytes) КАК compressed,
    data_uncompressed_bytes / data_compressed_bytes КАК compress_ratio
ИЗ system.columns
ГДЕ table = 'log_web'
 
┌─имя─────────────────────┬─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 редове в наборе. Времена: 0.005 сек.

Описание на използваните компоненти

FileBeat. Изпращане на файлови логове

Този компонент следи промените в лог файловете на диска и предава информация на LogStash. Инсталира се на всички сървъри, където се записват файлове с логове (обикновено IIS). Работи в режим tail (т.е. предава само ново добавени записи в файл). Но може да бъде настроен и да предава целите файлове. Това е удобно, когато е необходимо да се заредят данни за предишни месеци. Просто поставете файла с лог в папката и той сам ще го прочете изцяло.

При спиране на услугата, данните спират да се предават нататък в хранилището.

Примерната конфигурация изглежда по следния начин:

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.bg"
    fld_app_name: "site1.domain.bg"
    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.bg"
    fld_app_name: "site2.domain.bg"
    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.bg.cer"
  ssl.key: "C:/filebeat/certs/site1.domain.bg.key"
 
#================================ Процесори =====================================
 
processors:
  - add_host_metadata: ~
  - add_cloud_metadata: ~

LogStash. Събирач на лога

Този компонент е предназначен за получаване на записи на логовете от FileBeat (или чрез опашка RabbitMQ), парсване и вмъкване на партиди в базата данни ClickHouse.

За вмъкване в ClickHouse се използва плъгинът Logstash-output-clickhouse. Плъгинът Logstash разполага с механизъм за повторно изпращане на заявки, но при стандартно спиране, по-добре е да се спре самата услуга. При спиране ще се натрупват съобщения в опашката RabbitMQ, затова ако спирането е за продължителен период от време, по-добре е да се спрат Filebeat-ите на сървърите. В схемата, където не се използва RabbitMQ (в локалната мрежа Filebeat изпраща логовете директно в Logstash), Filebeat-ите работят напълно приемливо и безопасно, затова недостъпността на output преминава без последици.

Примерната конфигурация изглежда по следния начин:

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. Хранилище на логов

Логов за всички системи се запазват в една таблица (вижте в началото на статията). Тя е предназначена за съхранение на информация за заявките: всички параметри са подобни за различни формати, например IIS логове, apache и nginx логове. За приложенията, в които се записват, например, грешки, информационни съобщения, предупреждения, ще бъде предвидена отделна таблица с подходяща структура (в момента на етапа на проектиране).

При проектирането на таблицата е много важно да се определи първичният ключ (по който данните ще се сортират при съхранение). От това зависи степента на компресия на данните и скоростта на заявките. В нашия пример ключът е
ORDER BY (fld_app_name, fld_app_module, logdatetime)
Т.е. по името на системата, името на компонента на системата и датата на събитието. Първоначално датата на събитието беше на първо място. След преместването й на последно място, заявките започнаха да работят почти два пъти по-бързо. Промяната на първичния ключ ще изисква създаване на таблицата отново и повторно зареждане на данните, за да може ClickHouse да пренареди данните на диска. Това е тежка операция, затова е желателно да се обмисли добре какво трябва да влезе в ключа за сортиране.

Също така е необходимо да се отбележи, че относително в последните версии се е появил тип данни LowCardinality. При неговото използване значително се намалява размерът на компресираните данни за полета с ниска кардиналност (малко варианти).

Сега се използва версия 19.6 и плануваме да опитаме да обновим версията до последната. В тях са добавени такива чудесни функции, като Adaptive Granularity, Skipping indices и кодек DoubleDelta, например.

По подразбиране при инсталация в конфигурацията е зададено ниво на логиране trace. Логовете се ротируют и архивират, но в същото време се разширяват до един гигабайт. Ако няма нужда, можете да зададете ниво warning, тогава размерът на логовете рязко намалява. Настройката на логиране се задава във файла config.xml:

<!-- Възможни нива: https://github.com/pocoproject/poco/blob/develop/Foundation/include/Poco/Logger.h#L105 -->
<level>warning<\/level>

Някои полезни команди

Тъй като оригиналните инсталационни пакети са събрани на базата на 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

Плъгинът ClickHouse за LogStash при грешка в една линия запазва целия пакет в файл /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. Лог маршрутизатор от FileBeat в опашка RabbitMQ

Този компонент се използва за маршрутизиране на логовете, постъпващи от FileBeat в опашка RabbitMQ. Има два момента:

  1. За съжаление, FileBeat няма плъгин за изход, който да записва директно в RabbitMQ. И такъв функционал, съдейки по проблема на тяхната GitHub страница, не се планира да бъде реализиран. Има плъгин за Kafka, но по определени причини не можем да го използваме при нас.
  2. Има изисквания за събиране на логове в DMZ. Въз основа на тях, логовете първо трябва да бъдат поставени в опашка и след това LogStash извън нея чете от опашката записи.

Ето защо именно за случая, когато сървърите са разположени в DMZ, се налага да се използва такава малко усложнена схема. Примерна конфигурация изглежда следния начин:

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. Опашка за съобщения

Този компонент се използва за буфериране на лог записи в DMZ. Записването се извършва чрез връзка Filebeat → LogStash. Четенето се осъществява извън DMZ чрез LogStash. При експлоатация през RabbitMQ се обработват около 4000 съобщения в секунда.

Рутифицирането на съобщенията е настроено на базата на името на системата, т.е. въз основа на данните от конфигурацията на FileBeat. Всички съобщения попада в една опашка. Ако по някакви причини услугата на опашките бъде спряна, това няма да доведе до загуба на съобщения: FileBeat ще получи грешки при свързване и временно ще спре изпращането. LogStash, който чете от опашката, също ще получава мрежови грешки и ще изчака, докато връзката се възстанови. Дадените, разбира се, ще спрат да се записват в БД.

Следните инструкции се използват за създаване и настройка на опашките:

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. Табла с инструменти

Този компонент се използва за визуализиране на данните за мониторинг. Важно е да се инсталира приставката ClickHouse datasource for Grafana 4.6+. Наложи се да я коригираме малко, за да повишим ефективността на обработка на SQL филтри на таблото.

Например, използваме променливи и ако не са зададени в полето за филтър, бихме искали да не генерира условие в WHERE вида ( uriStem = » AND uriStem != » ). В такъв случай ClickHouse ще прочете колоната uriStem. В крайна сметка опитахме различни варианти и накрая поправихме приставката (макрос $valueIfEmpty), за да връща 1 в случай на празна стойност, без споменаване на самата колона.

И сега можем да използваме такъв заявка за графика

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

който се преобразува в такъв SQL (обърнете внимание, че празните полета uriStem са преобразувани в просто 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

Заключение

Появата на базата данни ClickHouse беше знаково събитие на пазара. Трудно можеше да си представим, че напълно безплатно в един миг получихме мощен и практичен инструмент за работа с големи данни. Без съмнение, с увеличаване на нуждите (например, шардиране и репликация на няколко сървъра) схемата ще се усложнява. Но по първоначалните впечатления, работата с тази база данни е много приятна. Ясно е, че продуктът е направен "за хората".

В сравнение с ElasticSearch, разходите за съхранение и обработка на логовете предварително се оценяват, че намаляват от пет до десет пъти. С други думи, ако за текущия обем данни трябваше да конфигурираме клъстер от няколко машини, при използването на ClickHouse ни е достатъчна една маломощна машина. Да, разбира се, в ElasticSearch също има механизми за компресия на данни на диска и други функции, които позволяват значително да намалят потреблението на ресурси, но в сравнение с ClickHouse това ще изисква по-големи разходи.

Без никакви специални оптимизации от наша страна, при дефолтни настройки, зареждането на данни и извличания от БД работят с впечатляваща скорост. Данните все още не са много (около 200 млн записа), но самият сървър е слаб. Този инструмент в бъдеще можем да използваме и за други цели, несвързани със съхранение на логове. Например, за проследяваща аналитика, в областта на сигурността, машинно обучение.

В края на краищата малко за минусите и плюсовете.

Недостатъци

  1. Зареждането на записи на големи партиди. От една страна, това е функция, но все пак се налага да се използват допълнителни компоненти за буферизиране на записите. Тази задача не винаги е проста, но все пак е решима. И би било добре да се опрости схемата.
  2. Някои екзотични функционалности или нови функции често се повреждат в новите версии. Това предизвиква притеснения и намалява желанието за обновяване до нова версия. Например, механизмът на таблиците Kafka – много полезна функция, която позволява директно четене на събития от Kafka, без реализация на консумиращи програми. Но съдейки по броя на проблемите в GitHub, все още се въздържаме от използването на този механизъм в продукция. Въпреки това, ако не правим резки движения и използваме основната функционалност, той работи стабилно.

Плюсове

  1. Не забавя.
  2. Нисък праг за влизане.
  3. С отворен код.
  4. Безплатна.
  5. Добре скалира (шардинг/репликация «от кутията»)
  6. Включена в регистъра на руските софтуерни продукти, препоръчани от Министерството на комуникациите.
  7. Наличие на официална поддръжка от Яндекс.

Източник: habr.com

Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри 🔥 Купете надежден хостинг за сайтове със защита от DDoS, VPS и VDS сървъри | ProHoster