מסד הנתונים של ClickHouse לבני אדם, או טכנולוגיות חייזרים

אלכסי ליזונוב, ראש מרכז הכשירות לערוצי שירות מרוחקים של מינהלת טכנולוגיות המידע של ה-MKB

מסד הנתונים של ClickHouse לבני אדם, או טכנולוגיות חייזרים

כחלופה למחסנית ELK (ElasticSearch, Logstash, Kibana), אנו עורכים מחקר על השימוש במסד הנתונים של ClickHouse כמאגר נתונים עבור יומנים.

במאמר זה נרצה לדבר על הניסיון שלנו בשימוש במסד הנתונים של ClickHouse ועל התוצאות הראשוניות של פעולת הפיילוט. יש לציין מיד שהתוצאות היו מרשימות.


מסד הנתונים של ClickHouse לבני אדם, או טכנולוגיות חייזרים

לאחר מכן, נתאר ביתר פירוט כיצד המערכת שלנו מוגדרת ומאילו רכיבים היא מורכבת. אבל עכשיו אני רוצה לדבר קצת על מסד הנתונים הזה בכללותו, ולמה כדאי לשים לב אליו. מסד הנתונים של ClickHouse הוא מסד נתונים עמודות אנליטי בעל ביצועים גבוהים מבית Yandex. הוא משמש בשירותי Yandex, בתחילה זהו אחסון הנתונים העיקרי עבור Yandex.Metrica. מערכת קוד פתוח, בחינם. מנקודת מבט של מפתח, תמיד תהיתי איך הם יישמו את זה, כי יש נתונים גדולים להפליא. וממשק המשתמש של Metrica עצמו מאוד גמיש ומהיר. בהיכרות ראשונה עם מאגר זה, הרושם הוא: "טוב, סוף סוף! נוצר עבור האנשים! החל מתהליך ההתקנה וכלה בשליחת בקשות.

למסד הנתונים הזה יש מחסום כניסה נמוך מאוד. אפילו מפתח עם מיומנות ממוצעת יכול להתקין אותו ולהתחיל להשתמש בו תוך מספר דקות. הכל עובד בצורה חלקה. אפילו אנשים חדשים בו. 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 64 ביט).

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 התקנת 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, בניית סל"ד "רגילה" עבור RHEL הופיעו במאגר Yandex, כך שתוכל לנסות להשתמש בהם. בזמן ההתקנה, השתמשנו בחבילות שנבנו על ידי Altinity.

גרפנה

הדמיית יומן. הגדרת לוחות מחוונים

https://grafana.com/

https://grafana.com/grafana/download

רדהאט ו Centos(64 ביט) – הגרסה האחרונה

מקור נתונים של 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 (נדרש עבור 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 מוצגת בטבלה הבאה:

שם

ערך

שים לב

תְצוּרָה

דיסק קשיח: 40GB
זיכרון RAM: 8GB
מעבד: Core 2 2Ghz

יש לשים לב לעצות להפעלת מסד הנתונים של ClickHouse (https://clickhouse.yandex/docs/ru/operations/tips/)

תוכנת מערכת כללית

מערכת הפעלה: רד האט אנטרפרייז Linux שרת (מאיפו)

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

מודול מערכת
ערכים חוקיים:

  • אינטרנט - אתר אינטרנט
  • svc - שירות אתר אינטרנט
  • intgr - Integration Web Service
  • bo - Admin (BackOffice)

אינטרנט

fld_website_name

שם האתר ב-IIS

ניתן לפרוס מספר מערכות בשרת אחד, או אפילו מספר מופעים של מודול מערכת אחד

ראשי אינטרנט

fld_server_name

שם שרת

web1.domain.com

fld_log_file_name

נתיב לקובץ היומן בשרת

C:inetpublogsLogFiles
W3SVC1u_ex190711.log

זה מאפשר לך לבנות ביעילות גרפים בגרפאנה. לדוגמה, הצג בקשות מהחזית של מערכת מסוימת. זה דומה למונה האתרים ב-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.

תיאור הרכיבים בשימוש

FileBeat. העברת יומני קבצים

רכיב זה עוקב אחר שינויים בקבצי יומן בדיסק ומעביר את המידע ל-LogStash. מותקן בכל השרתים שבהם נכתבים קבצי יומן (בדרך כלל IIS). עובד במצב זנב (כלומר מעביר רק את הרשומות שנוספו לקובץ). אבל בנפרד זה יכול להיות מוגדר להעביר קבצים שלמים. זה שימושי כאשר אתה צריך להוריד נתונים מחודשים קודמים. פשוט שים את קובץ היומן בתיקייה והוא יקרא אותו בשלמותו.

כאשר השירות מופסק, הנתונים אינם מועברים עוד לאחסון.

תצורה לדוגמה נראית כך:

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, כך שאם העצירה היא לזמן ארוך אז עדיף להפסיק את 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"
        }
    }
 
}

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"

בית קליק. אחסון יומנים

היומנים של כל המערכות מאוחסנים בטבלה אחת (ראה בתחילת המאמר). הוא נועד לאחסן מידע על בקשות: כל הפרמטרים דומים עבור פורמטים שונים, כגון יומני IIS, apache ו-nginx. עבור יומני יישומים, בהם נרשמות, למשל, שגיאות, הודעות מידע, אזהרות, תינתן טבלה נפרדת עם המבנה המתאים (כרגע בשלב התכנון).

בעת עיצוב טבלה, חשוב מאוד להחליט על המפתח הראשי (על פיו ימוינו הנתונים במהלך האחסון). מידת דחיסת הנתונים ומהירות השאילתה תלויים בכך. בדוגמה שלנו, המפתח הוא
ORDER BY (fld_app_name, fld_app_module, logdatetime)
כלומר לפי שם המערכת, שם רכיב המערכת ותאריך האירוע. בתחילה, תאריך האירוע הגיע ראשון. לאחר העברתו למקום האחרון, השאילתות החלו לעבוד בערך פי שניים מהר יותר. שינוי המפתח הראשי ידרוש יצירה מחדש של הטבלה וטעינה מחדש של הנתונים כך ש-ClickHouse ימיין מחדש את הנתונים בדיסק. מדובר בפעולה כבדה ולכן כדאי לחשוב הרבה מה צריך לכלול במפתח המיון.

יש לציין גם שסוג הנתונים LowCardinality הופיע יחסית בגרסאות האחרונות. בעת השימוש בו, גודל הנתונים הדחוסים מצטמצם באופן דרסטי עבור אותם שדות בעלי קרדינליות נמוכה (אפשרויות מעטות).

גרסה 19.6 נמצאת כעת בשימוש ואנו מתכננים לנסות לעדכן לגרסה העדכנית ביותר. יש להם תכונות נפלאות כמו Adaptive Granularity, דילוג על מדדי ו-DoubleDelta, למשל.

כברירת מחדל, במהלך ההתקנה, רמת הרישום מוגדרת למעקב. היומנים מסובבים ומאוחסנים בארכיון, אך במקביל הם מתרחבים עד לגיגה-בייט. אם אין צורך, אז אתה יכול להגדיר את רמת האזהרה, ואז גודל היומן מצטמצם באופן דרסטי. הגדרת הרישום מוגדרת בקובץ 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 אין תוסף פלט לכתיבה ישירות ל-RabbitMQ. ופונקציונליות כזו, אם לשפוט לפי הנושא ב-github שלהם, לא מתוכננת ליישום. יש תוסף לקפקא, אבל משום מה אנחנו לא יכולים להשתמש בו בבית.
  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. כל ההודעות עוברות לתור אחד. אם מסיבה כלשהי שירות התורים הופסק, אז זה לא יוביל לאובדן הודעות: 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"

גרפאנה. לוחות מחוונים

רכיב זה משמש להמחשת נתוני ניטור. במקרה זה, עליך להתקין את מקור הנתונים של ClickHouse עבור 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. פונקציונליות אקזוטית מסוימת או תכונות חדשות נשברות לעתים קרובות בגרסאות חדשות. זה מעורר דאגה, ומפחית את הרצון לשדרג לגרסה חדשה. לדוגמה, מנוע השולחן של קפקא הוא תכונה שימושית מאוד המאפשרת לקרוא אירועים מקפקא ישירות, מבלי ליישם צרכנים. אבל אם לשפוט לפי מספר הבעיות ב-github, אנחנו עדיין נזהרים לא להשתמש במנוע הזה בייצור. עם זאת, אם אתה לא עושה מחוות פתאומיות לצד ומשתמש בפונקציונליות העיקרית, אז זה עובד ביציבות.

Pros

  1. לא מאט.
  2. סף כניסה נמוך.
  3. קוד פתוח
  4. חינם.
  5. קנה מידה טוב (ריסוק/שכפול מחוץ לקופסה)
  6. כלול בפנקס התוכנות הרוסיות המומלצות על ידי משרד התקשורת.
  7. נוכחות של תמיכה רשמית מ- Yandex.

מקור: www.habr.com

קנה אירוח אמין לאתרים עם הגנת DDoS, שרתי VPS VDS 🔥 קנה אחסון אתרים אמין עם הגנת DDoS, שרתי VPS VDS | ProHoster