БД ClickHouse за хора, или технологии от извънземни

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

БД ClickHouse за хора, или технологии от извънземни

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

В тази статия искаме да споделим с вас нашия опит с БД ClickHouse и предварителните резултати от пилотната експлоатация. Трябва да отбележим веднага, че резултатите са впечатляващи.


БД ClickHouse за хора, или технологии от извънземни

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

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

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

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

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

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

БД ClickHouse за хора, или технологии от извънземни

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

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

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

Име

Описание

Връзка към разпространението

NGINX

Обратен прокси за ограничения на достъпа по портове и организиране на удостоверяване

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

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

Плъгин Loagstash за предаване на логове в базата данни 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 източник на данни за 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-запитвания. Отделно ще отбележим, че има отделни полета за съхранение на 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

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

С: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.

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

ИЗБЕРИ
    име,
    форматирай четлив размер(несжатие_байтов) КАТО несжат размер,
    форматирай четлив размер(сжатие_байтов) КАТО сжат размер,
    несжатие_байтов / сжатие_байтов КАТО отношение на сжатие
ОТ система.колони
КЪДЕ таблица = 'log_web'
 
┌─име────────────────────┬─несжато───────┬─сжато───────┬─────отношение_на_сжатие────┐
│ 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.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. Събирач на лога

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

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

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

log_web__filebeat_clickhouse.conf

вход {
 
    бийтове {
        порт => 5044
        тип => '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"
        порт => 5671
        потребител => "q-reader"
        парола => "парола"
        опашка => "web_log"
        heartbeat => 30
        durable => true
        ssl => true
        #ssl_certificate_path => "/etc/logstash/certs/server.p12"
        #ssl_certificate_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]}"
        }
    }
 
}
 
филтър { 
 
      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" => "," }
        }
 
}
 
изход { 
  #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:


warnign

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

Тъй като оригиналните инсталационни пакети са събрани по 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 няма output плъгин за запис директно в RabbitMQ. И такъв функционал, съдейки по съобщението на техния GitHub, не е планиран за реализиране. Има плъгин за Kafka, но по определени причини не можем да го използваме при нас.
  2. Има изисквания за събиране на логове в DMZ. Въз основа на тях, логовете първо трябва да се съхраняват в опашка и след това LogStash извънредно да чете записа от опашката.

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

iis_w3c_logs__filebeat_rabbitmq.conf

вход {
 
    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"
    }
 
}
 
изход { 
  #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 => "парола"
        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