БД 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

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

Убудова 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.

Графана

Візуалізацыя логаў. Настройка дашбордаў

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 няма output напроста ў 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 прадстаўлена ў наступнай табліцы:

Назва

Значэнне

Заўвага

Канфігурацыя

Жорсткі дыск: 40GB
Аператыўная памяць: 8GB
Processor: 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.

Ступень сціску дадзеных у слупках

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.

Апісанне выкарыстоўваных кампанентаў

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

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/XNUMX, і мы плануем паспрабаваць абнавіць версію да апошняй. У іх з'явіліся такія выдатныя фічы як Adaptive Granularity, Skipping indices і кодэк DoubleDelta, напрыклад.

Па змаўчанні пры ўсталёўцы ў канфігурацыі ўсталяваны ўзровень лагавання trace. Логі раціруюцца і архівуюцца, але пры гэтым пашыраюцца да гігабайта. Калі няма неабходнасці, то можна паставіць узровень warning, тады памер лога рэзка памяншаецца. Налада лагіравання задаецца ў файле config.xml:

<!-- Possible levels: 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
 
Плагин кликлауза для логстеш в случае ошибки в одной строке сохраняет всю пачку в файл /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. І такі функцыянал, мяркуючы па жыццю на іх гітхабе, не плануецца да рэалізацыі. Ёсць плягін для Кафкі, але па пэўных прычынах мы не можам яе выкарыстоўваць у сябе.
  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. Пры эксплуатацыі праз RabboitMQ апрацоўваецца каля 4 тысяч паведамленняў у секунду.

Раутынг паведамленняў настроены па назве сістэмы, т. е. на аснове дадзеных канфігурацыі 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 - вельмі карысная фіча, якая дазваляе напрамую чытаць падзеі з кафкі, без рэалізацыі кансьюмераў. Але мяркуючы па колькасці Issue на гітхабе, мы пакуль сцеражымся выкарыстоўваць гэты рухавічок у прадакшэне. Зрэшты, калі не рабіць рэзкіх рухаў цела ў бок і выкарыстоўваць асноўны функцыянал, то ён працуе стабільна.

Плюсы

  1. Не тармозіць.
  2. Нізкі парог уваходу.
  3. З адкрытым зыходным кодам.
  4. Бясплатная.
  5. Добра маштабуецца (шардзіраванне/рэплікацыя «са скрынкі»)
  6. Уваходзіць у рэестр расійскага ПЗ, рэкамендаванага МинКомСвязи.
  7. Наяўнасць афіцыйнай падтрымкі ад Яндэкс.

Крыніца: habr.com

Дадаць каментар