人類或外星技術的 ClickHouse 數據庫

MKB 信息技術局遠程服務渠道能力中心負責人 Aleksey Lizunov

人類或外星技術的 ClickHouse 數據庫

作為 ELK 堆棧(ElasticSearch、Logstash、Kibana)的替代方案,我們正在研究使用 ClickHouse 數據庫作為日誌的數據存儲。

在這篇文章中,我們想談談我們使用ClickHouse數據庫的經驗和試點運行的初步結果。 應該立即註意到結果令人印象深刻。


人類或外星技術的 ClickHouse 數據庫

接下來,我們將更詳細地描述我們的系統是如何配置的,以及它由哪些組件組成。 但現在我想從整體上談談這個數據庫,以及為什麼它值得關注。 ClickHouse 數據庫是來自 Yandex 的高性能分析型柱狀數據庫。 它用於 Yandex 服務,最初它是 Yandex.Metrica 的主要數據存儲。 開源系統,免費。 從開發人員的角度來看,我一直想知道他們是如何實現它的,因為有非常大的數據。 而且 Metrica 的用戶界面本身非常靈活和快速。 初識這個數據庫時,印像是:“嗯,終於! 為人民而生! 從安裝過程開始到發送請求結束。

該數據庫的入門門檻非常低。 即使是一般技能的開發人員也可以在幾分鐘內安裝此數據庫並開始使用它。 一切都很清楚。 即使是 Linux 新手也能很快搞定安裝並進行最簡單的操作。 如果早先,對於大數據、Hadoop、Google BigTable、HDFS 這些詞,一個普通的開發人員認為它是關於一些 TB、PB 的東西,一些超人參與了這些系統的設置和開發,那麼隨著 ClickHouse 的出現數據庫,我們得到了一個簡單易懂的工具,您可以使用它來解決以前無法完成的任務範圍。 安裝一台相當普通的機器只需五分鐘。 也就是說,我們得到了一個數據庫,例如 MySql,但只能存儲數十億條記錄! 某超級存檔用SQL語言。 這就像人們被交給了外星人的武器。

關於我們的日誌系統

為了收集信息,使用標準格式的Web應用程序的IIS日誌文件(我們目前也在解析應用程序日誌,但試點階段的主要目標是收集IIS日誌)。

由於各種原因,我們不能完全放棄 ELK 堆棧,我們繼續使用 LogStash 和 Filebeat 組件,它們已經證明自己很好並且工作非常可靠且可預測。

一般的日誌記錄方案如下圖所示:

人類或外星技術的 ClickHouse 數據庫

向ClickHouse數據庫寫入數據的一個特點是不頻繁(每秒一次)大批量插入記錄。 顯然,這是您第一次體驗使用 ClickHouse 數據庫時遇到的最“有問題”的部分:方案變得有點複雜。
直接向 ClickHouse 插入數據的 LogStash 插件在這裡幫了大忙。 該組件部署在與數據庫本身相同的服務器上。 所以,一般來說,不建議這樣做,但是從實用的角度來說,不要在部署在同一台服務器上的時候,產生單獨的服務器。 我們沒有觀察到任何故障或與數據庫的資源衝突。 另外需要注意的是,插件有出錯重試機制。 並且在出錯的情況下,插件將一批無法插入的數據寫入磁盤(文件格式很方便:編輯後,您可以使用 clickhouse-client 輕鬆插入更正的批次)。

該方案中使用的軟件的完整列表如下表所示:

使用的軟件列表

名稱

描述

分發鏈接

NGINX

反向代理限制端口訪問和組織授權

目前方案中未使用

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

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

檔案節拍

傳輸文件日誌。

https://www.elastic.co/downloads/beats/filebeat (適用於 Windows 64 位的分發包)。

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

日誌存儲

日誌收集器。

用於從 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 安裝 logstash-output-clickhouse

/usr/share/logstash/bin/logstash-plugin 安裝 logstash-filter-prune

/usr/share/logstash/bin/logstash-plugin 安裝 logstash-filter-multiline

點擊之家

日誌存儲 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 年 XNUMX 月開始,RHEL 的“正常”rpm 構建出現在 Yandex 存儲庫中,因此您可以嘗試使用它們。 在安裝時,我們使用的是 Altinity 構建的包。

格拉法納

日誌可視化。 設置儀表板

https://grafana.com/

https://grafana.com/grafana/download

Redhat & Centos(64 位) - 最新版本

Grafana 4.6+ 的 ClickHouse 數據源

帶有 ClickHouse 數據源的 Grafana 插件

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

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

日誌存儲

從 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 運行時(RabbitMQ 需要)

二郎運行時。 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(麥坡)

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;

我們使用默認分區(按月)和索引粒度。 所有字段實際上都對應於記錄 http 請求的 IIS 日誌條目。 另外,我們注意到有單獨的字段用於存儲 utm-tags(它們在從查詢字符串字段插入表的階段被解析)。

此外,幾個系統字段已添加到表中以存儲有關係統、組件、服務器的信息。 有關這些字段的說明,請參見下表。 在一張表中,我們存儲多個系統的日誌。

名稱

描述

例子

fld_應用程序名稱

應用程序/系統名稱
有效值:

  • site1.domain.com 外部站點 1
  • site2.domain.com 外部站點 2
  • internal-site1.domain.local 內部站點 1

site1.domain.com

fld_app_模塊

系統模塊
有效值:

  • 網站 - 網站
  • svc - 網站服務
  • intgr - 集成 Web 服務
  • bo - 管理員(後台)

捲筒紙

fld_網站名稱

IIS 中的站點名稱

一台服務器上可以部署多個系統,甚至可以是一個系統模塊的多個實例

網站主

fld_服務器_名稱

服務器名稱

web1.domain.com

fld_日誌_文件名

服務器上日誌文件的路徑

C:inetpublogs日誌文件
W3SVC1u_ex190711.log

這使您可以在 Grafana 中高效地構建圖形。 例如,查看來自特定係統前端的請求。 這類似於 Yandex.Metrica 中的站點計數器。

下面是兩個月數據庫使用情況的一些統計數據。

按系統及其組件細分的記錄數

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.

使用組件的描述

文件節拍。 傳輸文件日誌

該組件跟踪磁盤上日誌文件的更改並將信息傳遞給 LogStash。 安裝在所有寫入日誌文件的服務器上(通常是 IIS)。 在尾部模式下工作(即僅將添加的記錄傳輸到文件)。 但它可以單獨配置為傳輸整個文件。 當您需要下載前幾個月的數據時,這很有用。 只需將日誌文件放在一個文件夾中,它就會完整讀取它。

當服務停止時,數據不再進一步傳輸到存儲。

示例配置如下所示:

文件beat.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: ~

日誌存儲。 日誌收集器

該組件旨在從 FileBeat(或通過 RabbitMQ 隊列)接收日誌條目,解析批次並將其插入 ClickHouse 數據庫。

為了插入到 ClickHouse,使用了 Logstash-output-clickhouse 插件。 Logstash 插件有一個請求重試機制,但是定期關閉,最好停止服務本身。 停止時,消息會堆積在RabbitMQ隊列中,所以如果停止的時間較長,那麼最好停止服務器上的Filebeats。 在不使用 RabbitMQ 的方案中(在本地網絡上,Filebeat 直接將日誌發送到 Logstash),Filebeats 的工作是相當可接受且安全的,因此對於它們來說,輸出的不可用性不會產生任何後果。

示例配置如下所示:

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

管道.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"

點擊之家。 日誌存儲

所有系統的日誌都存儲在一個表中(請參閱本文開頭)。 它旨在存儲有關請求的信息:所有參數對於不同的格式都是相似的,例如 IIS 日誌、apache 和 nginx 日誌。 對於記錄了錯誤、信息消息、警告等的應用程序日誌,將提供一個具有適當結構的單獨表格(目前處於設計階段)。

在設計表時,確定主鍵(數據在存儲期間將按其排序)非常重要。 數據壓縮程度和查詢速度取決於此。 在我們的例子中,關鍵是
ORDER BY (fld_app_name, fld_app_module, logdatetime)
也就是說,通過系統的名稱、系統組件的名稱和事件的日期。 最初,事件的日期排在第一位。 將其移至最後一個位置後,查詢開始以大約兩倍的速度運行。 更改主鍵將需要重新創建表並重新加載數據,以便 ClickHouse 重新排序磁盤上的數據。 這是一項繁重的操作,因此最好仔細考慮排序鍵中應包含的內容。

還需要注意的是,LowCardinality數據類型在最近的版本中出現的比較多。 使用它時,對於那些具有低基數(很少選項)的字段,壓縮數據的大小會大大減少。

目前正在使用 19.6 版本,我們計劃嘗試更新到最新版本。 例如,它們具有自適應粒度、跳過索引和 DoubleDelta 編解碼器等出色功能。

默認情況下,在安裝期間,日誌記錄級別設置為跟踪。 日誌被輪換和歸檔,但同時它們擴展到 XNUMX GB。 如果沒有必要,那麼你可以設置警告級別,然後日誌的大小會大大減少。 日誌記錄設置在 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

日誌存儲。 從 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
    }
}

兔子MQ。 消息隊列

該組件用於緩衝 DMZ 中的日誌條目。 記錄是通過一堆 Filebeat → LogStash 完成的。 讀取是通過 LogStash 從 DMZ 外部完成的。 通過 RabboitMQ 操作時,每秒處理大約 4 條消息。

消息路由由系統名稱配置,即基於 FileBeat 配置數據。 所有消息都進入一個隊列。 如果由於某種原因隊列服務停止,那麼這不會導致消息丟失:FileBeats 會收到連接錯誤並暫時停止發送。 而從隊列中讀取的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 4.6+ 插件安裝 ClickHouse 數據源。 我們不得不對其進行一些調整,以提高在儀表板上處理 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上的Issues數量來看,我們還是小心翼翼的不在生產中使用這個引擎。 但是,如果您不突然向側面做手勢並使用主要功能,則它可以穩定運行。

優點

  1. 不會減速。
  2. 入門門檻低。
  3. 開源。
  4. 自由的。
  5. 擴展性好(開箱即用的分片/複製)
  6. 列入交通部推薦的俄羅斯軟件名錄。
  7. Yandex 官方支持的存在。

來源: www.habr.com

添加評論