Аналіз TSDB у Prometheus 2

Аналіз TSDB у Prometheus 2

База дадзеных часовых шэрагаў (TSDB, time series database) у Prometheus 2 – гэта выдатны прыклад інжынернага рашэння, якое прапануе сур'ёзныя паляпшэнні ў параўнанні са сховішчам v2 у Prometheus 1 у плане хуткасці назапашвання дадзеных і выкананні запытаў, эфектыўнасці выкарыстання рэсурсаў. Мы ўкаранялі Prometheus 2 у Percona Monitoring and Management (PMM), і ў мяне была магчымасць разабрацца з прадукцыйнасцю Prometheus 2 TSDB. У гэтым артыкуле я раскажу аб выніках гэтых назіранняў.

Сярэдняя працоўная нагрузка Prometheus

Для тых, хто абвык мець справу з базамі дадзеных асноўнага прызначэння, звычайная працоўная нагрузка Prometheus даволі цікаўная. Хуткасць назапашвання дадзеных імкнецца да стабільнай велічыні: звычайна сэрвісы, якія вы маніторыце, пасылаюць прыкладна аднолькавую колькасць метрык, і інфраструктура мяняецца адносна павольна.
Запыты інфармацыі могуць прыходзіць з розных крыніц. Некаторыя з іх, напрыклад алерты, таксама імкнуцца да стабільнай і прадказальнай велічыні. Іншыя, такія як прыстасаваныя запыты, могуць выклікаць ўсплёскі, хоць, гэта не характэрна для большай часткі нагрузкі.

Тэст нагрузкі

У ходзе тэсціравання я сканцэнтраваўся на здольнасці назапашваць дадзеныя. Я разгарнуў Prometheus 2.3.2, скампіляваны з дапамогай Go 1.10.1 (як частка PMM 1.14) на сэрвісе Linode, выкарыстаючы гэты скрыпт: StackScript. Для максімальна рэалістычнага генеравання нагрузкі, з дапамогай гэтага StackScript я запусціў некалькі MySQL-нод з рэальнай нагрузкай (Sysbench TPC-C Test), кожная з якіх эмулявала 10 нод Linux/MySQL.
Усе наступныя тэсты праводзіліся на серверы Linode з васьмю віртуальнымі ядрамі і 32 Гбайт памяці, на якім запушчаны 20 нагрузачных сімуляцый маніторынгу двухсот інстансаў MySQL. Або, у тэрмінах Prometheus, 800 таргетаў (targets), 440 збораў (scrapes) у секунду, 380 тысяч запісаў (samples) у секунду і 1,7 млн ​​актыўных часавых шэрагаў.

Дызайн

Звычайны падыход традыцыйных баз дадзеных, у тым ліку той, што выкарыстоўваў Prometheus 1.x, заключаецца ў ліміце памяці. Калі яго недастаткова, каб вытрымаць нагрузку, вы сутыкнецеся з вялікімі затрымкамі, і нейкія запыты не будуць выкананы. Выкарыстанне памяці ў Prometheus 2 канфігуруецца праз ключ storage.tsdb.min-block-duration, які вызначае, як доўга запісы будуць захоўвацца ў памяці перад скідам на дыск (па змаўчанні гэта 2 гадзіны). Колькасць неабходнай памяці будзе залежаць ад колькасці часавых шэрагаў, цэтлікаў (labels) і інтэнсіўнасці збору дадзеных (scrapes) у суме з чыстым уваходным струменем. У плане дыскавай прасторы Prometheus імкнецца выкарыстоўваць па 3 байта на запіс (sample). З іншага боку, патрабаванні да памяці куды вышэйшыя.

Нягледзячы на ​​тое, што ёсць магчымасць канфігураваць памер блока, не рэкамендуецца наладжваць яго ўручную, таму вы пастаўлены перад неабходнасцю даць Prometheus столькі памяці, колькі ён папросіць для вашай нагрузкі.
Калі памяці будзе нядосыць, каб падтрымліваць уваходны струмень метрык, Prometheus зваліцца з out of memory або да яго дабярэцца OOM killer.
Дадаць swap, каб адцягнуць момант падзення, калі ў Prometheus заканчваецца памяць, не асабліва дапамагае, таму што выкарыстанне гэтай функцыі выклікае выбуховае спажыванне памяці. Я думаю, што справа ў Go, яго garbage collector і ў тым, як ен працуе са swap.
Іншым цікавым падыходам выглядае настройка скіду head block на дыск у пэўны час, замест таго каб адлічваць яго з часу старту працэсу.

Аналіз TSDB у Prometheus 2

Як вы можаце бачыць з графіка, скіды на дыск адбываюцца кожныя дзве гадзіны. Калі вы памяняеце параметр min-block-duration на адну гадзіну, то гэтыя скіды будуць адбывацца кожную гадзіну, пачынаючы праз паўгадзіны.
Калі вы хочаце выкарыстоўваць гэты і іншыя графікі ў вашай інсталяцыі Prometheus, можаце выкарыстоўваць гэты дашборд. Ён быў распрацаваны для PMM, але, з невялікімі зменамі, падыходзіць да любой усталёўкі Prometheus.
У нас ёсць актыўны блок, званы head block, які захоўваецца ў памяці; блокі ж з больш старымі дадзенымі даступныя праз mmap(). Гэта прыбірае неабходнасць канфігураваць кэш асобна, але таксама азначае, што вам трэба пакідаць дастаткова месцы для кэша аперацыйнай сістэмы, калі вы хочаце рабіць запыты да дадзеных старэйшых за тыя, якія месціць head block.
А яшчэ гэта значыць, што спажыванне Prometheus віртуальнай памяці будзе выглядаць дастаткова высокім, пра што не варта турбавацца.

Аналіз TSDB у Prometheus 2

Яшчэ адзін цікавы момант дызайну - выкарыстанне WAL (write ahead log). Як відаць з дакументацыі па сховішчы, Prometheus выкарыстоўвае WAL для пазбягання страт пры падзеннях. Канкрэтныя механізмы гарантыі жывучасці дадзеных, на жаль, недастаткова дакументаваны. Версія Prometheus 2.3.2 скідае WAL на дыск кожныя 10 секунд, і гэты параметр не канфігуруецца карыстачом.

Ушчыльненні (Compactions)

Prometheus TSDB спраектавана па выяве LSM-сховішчы (Log Structured merge – часопісна-структураванае дрэва са зліццём): head block скідаецца перыядычна на дыск, у той жа час механізм ушчыльнення аб'ядноўвае некалькі блокаў разам для пазбягання сканавання занадта вялікай колькасці блокаў пры запытах. Тут бачна колькасць блокаў, якія я назіраў на тэставай сістэме пасля сутак нагрузкі.

Аналіз TSDB у Prometheus 2

Калі вы хочаце даведацца больш пра сховішча, вы можаце вывучыць файл meta.json, у якім ёсць інфармацыя аб наяўных блоках і аб тым, як яны з'явіліся.

{
       "ulid": "01CPZDPD1D9R019JS87TPV5MPE",
       "minTime": 1536472800000,
       "maxTime": 1536494400000,
       "stats": {
               "numSamples": 8292128378,
               "numSeries": 1673622,
               "numChunks": 69528220
       },
       "compaction": {
               "level": 2,
               "sources": [
                       "01CPYRY9MS465Y5ETM3SXFBV7X",
                       "01CPYZT0WRJ1JB1P0DP80VY5KJ",
                       "01CPZ6NR4Q3PDP3E57HEH760XS"
               ],
               "parents": [
                       {
                               "ulid": "01CPYRY9MS465Y5ETM3SXFBV7X",
                               "minTime": 1536472800000,
                               "maxTime": 1536480000000
                       },
                       {
                               "ulid": "01CPYZT0WRJ1JB1P0DP80VY5KJ",
                               "minTime": 1536480000000,
                               "maxTime": 1536487200000
                       },
                       {
                               "ulid": "01CPZ6NR4Q3PDP3E57HEH760XS",
                               "minTime": 1536487200000,
                               "maxTime": 1536494400000
                       }
               ]
       },
       "version": 1
}

Ушчыльненні ў Prometheus прывязаныя да часу скіду head block на дыск. У гэты момант можа праводзіцца некалькі такіх аперацыяў.

Аналіз TSDB у Prometheus 2

Судзячы па ўсім, ушчыльненні ніяк не абмежаваныя і могуць выклікаць вялікія скокі дыскавага I/O падчас выканання.

Аналіз TSDB у Prometheus 2

Скокі загрузкі CPU

Аналіз TSDB у Prometheus 2

Зразумела, гэта даволі негатыўна ўплывае на хуткасць працы сістэмы, а таксама з'яўляецца сур'ёзным выклікам для LSM-сховішчаў: як рабіць ушчыльненні для падтрымкі высокай хуткасці запытаў і пры гэтым не выклікаць занадта моцнага звышеда?
Выкарыстанне памяці падчас ушчыльненняў таксама выглядае даволі цікава.

Аналіз TSDB у Prometheus 2

Мы можам бачыць, як пасля ўшчыльнення вялікая частка памяці змяняе стан з Cached на Free: значыць, патэнцыйна каштоўная інфармацыя была адтуль прыбраная. Цікаўна, ці выкарыстоўваецца тут fadvice() ці нейкая яшчэ тэхніка мінімізацыі, ці гэта выклікана тым, што кэш быў вызвалены ад блокаў, знішчаных пры ўшчыльненні?

Аднаўленне пасля збою

Аднаўленне пасля збояў займае час, і гэта абгрунтавана. Для ўваходнага струменя ў мільён запісаў у секунду мне прыйшлося чакаць каля 25 хвілін, пакуль рабілася аднаўленне з улікам SSD-дыска.

level=info ts=2018-09-13T13:38:14.09650965Z caller=main.go:222 msg="Starting Prometheus" version="(version=2.3.2, branch=v2.3.2, revision=71af5e29e815795e9dd14742ee7725682fa14b7b)"
level=info ts=2018-09-13T13:38:14.096599879Z caller=main.go:223 build_context="(go=go1.10.1, user=Jenkins, date=20180725-08:58:13OURCE)"
level=info ts=2018-09-13T13:38:14.096624109Z caller=main.go:224 host_details="(Linux 4.15.0-32-generic #35-Ubuntu SMP Fri Aug 10 17:58:07 UTC 2018 x86_64 1bee9e9b78cf (none))"
level=info ts=2018-09-13T13:38:14.096641396Z caller=main.go:225 fd_limits="(soft=1048576, hard=1048576)"
level=info ts=2018-09-13T13:38:14.097715256Z caller=web.go:415 component=web msg="Start listening for connections" address=:9090
level=info ts=2018-09-13T13:38:14.097400393Z caller=main.go:533 msg="Starting TSDB ..."
level=info ts=2018-09-13T13:38:14.098718401Z caller=repair.go:39 component=tsdb msg="found healthy block" mint=1536530400000 maxt=1536537600000 ulid=01CQ0FW3ME8Q5W2AN5F9CB7R0R
level=info ts=2018-09-13T13:38:14.100315658Z caller=web.go:467 component=web msg="router prefix" prefix=/prometheus
level=info ts=2018-09-13T13:38:14.101793727Z caller=repair.go:39 component=tsdb msg="found healthy block" mint=1536732000000 maxt=1536753600000 ulid=01CQ78486TNX5QZTBF049PQHSM
level=info ts=2018-09-13T13:38:14.102267346Z caller=repair.go:39 component=tsdb msg="found healthy block" mint=1536537600000 maxt=1536732000000 ulid=01CQ78DE7HSQK0C0F5AZ46YGF0
level=info ts=2018-09-13T13:38:14.102660295Z caller=repair.go:39 component=tsdb msg="found healthy block" mint=1536775200000 maxt=1536782400000 ulid=01CQ7SAT4RM21Y0PT5GNSS146Q
level=info ts=2018-09-13T13:38:14.103075885Z caller=repair.go:39 component=tsdb msg="found healthy block" mint=1536753600000 maxt=1536775200000 ulid=01CQ7SV8WJ3C2W5S3RTAHC2GHB
level=error ts=2018-09-13T14:05:18.208469169Z caller=wal.go:275 component=tsdb msg="WAL corruption detected; truncating" err="unexpected CRC32 checksum d0465484, want 0" file=/opt/prometheus/data/.prom2-data/wal/007357 pos=15504363
level=info ts=2018-09-13T14:05:19.471459777Z caller=main.go:543 msg="TSDB started"
level=info ts=2018-09-13T14:05:19.471604598Z caller=main.go:603 msg="Loading configuration file" filename=/etc/prometheus.yml
level=info ts=2018-09-13T14:05:19.499156711Z caller=main.go:629 msg="Completed loading of configuration file" filename=/etc/prometheus.yml
level=info ts=2018-09-13T14:05:19.499228186Z caller=main.go:502 msg="Server is ready to receive web requests."

Асноўная праблема працэсу аднаўлення - высокае спажыванне памяці. Нягледзячы на ​​тое, што ў звычайнай сітуацыі сервер можа стабільна працаваць з такім жа аб'ёмам памяці, пры падзенні ён можа не падняцца з-за OOM. Адзінае рашэнне, якое я знайшоў, гэта адключыць збор дадзеных, падняць сервер, дазволіць яму аднавіцца і перазагрузіць ужо са ўключаным зборам.

разагрэў

Яшчэ адно паводзіны, аб якім варта памятаць падчас разагравання - суадносіны нізкай прадукцыйнасці і высокага спажывання рэсурсаў прама пасля старту. Падчас некаторых, але не ўсіх стартаў я назіраў сур'ёзную нагрузку па CPU і памяці.

Аналіз TSDB у Prometheus 2

Аналіз TSDB у Prometheus 2

Правалы ў выкарыстанні памяці кажуць аб тым, што Prometheus не можа са старту сканфігураваць усе зборы, і нейкая інфармацыя аказваецца страчаная.
Я не высветліў дакладныя прычыны высокай нагрузкі на працэсар і памяць. Падазраю што гэта злучана са стварэннем новых часавых шэрагаў у head block з высокай частатой.

Скокі нагрузкі на CPU

Апроч ушчыльненняў, якія ствараюць даволі высокую нагрузку па I/O, я заўважыў сур'ёзныя скокі нагрузкі на працэсар кожныя дзве хвіліны. Усплёскі даўжэй пры высокім уваходным струмені і падобна, што яны выкліканыя зборшчыкам смецця Go, па меншай меры, некаторыя ядра цалкам загружаны.

Аналіз TSDB у Prometheus 2

Аналіз TSDB у Prometheus 2

Гэтыя скокі не так ужо і неістотныя. Падобна, што калі яны ўзнікаюць, унутраная кропка ўваходу і метрыкі Prometheus становяцца недаступныя, што выклікае правалы ў дадзеных у гэтыя ж прамежкі часу.

Аналіз TSDB у Prometheus 2

Таксама можна заўважыць, што экспарцёр Prometheus затыкаецца на адну секунду.

Аналіз TSDB у Prometheus 2

Мы можам заўважыць карэляцыі з уборкай смецця (GC).

Аналіз TSDB у Prometheus 2

Заключэнне

TSDB у Prometheus 2 дзейнічае хутка, здольная спраўляцца з мільёнамі часавых шэрагаў і ў той жа час з тысячамі запісаў, якія здзяйсняюцца ў секунду, выкарыстоўваючы даволі сціплае жалеза. Утылізацыя CPU і дыскавага I/O таксама ўражвае. Мой прыклад паказваў да 200 000 метрык у секунду на адно скарыстанае ядро.

Для планавання пашырэння трэба памятаць аб дастатковых аб'ёмах памяці, і гэта павінна быць рэальная памяць. Аб'ём выкарыстоўванай памяці, які я назіраў, складаў каля 5 Гбайт на 100 000 запісаў у секунду ўваходнага струменя, што давала ў суме з кэшам аперацыйнай сістэмы каля 8 Гбайт занятай памяці.

Зразумела, яшчэ трэба будзе нямала працы па утаймаванне усплёскаў CPU і дыскавага I/O, і гэта нядзіўна, улічваючы, наколькі яшчэ маладая TSDB Prometheus 2 у параўнанні з InnoDB, TokuDB, RocksDB, WiredTiger, але ўсе яны мелі падобныя праблемы ў пачатку жыццёвага цыклу.

Крыніца: habr.com

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