
Linux ir liels skaits rÄ«ku, lai debugÄtu kodolu un lietojumprogrammas. LielÄkÄ daļa no tiem negatÄ«vi ietekmÄ lietojumprogrammu veiktspÄju un tos nevar izmantot ražoÅ”anÄ.
Pirms dažiem gadiem tika izstrÄdÄts vÄl viens rÄ«ks Å obrÄ«d jau pastÄv daudzi lietojumprogrammu rÄ«ki, kas izmanto eBPF, un Å”ajÄ rakstÄ mÄs apskatÄ«sim, kÄ uzrakstÄ«t savu profila rÄ«ku, pamatojoties uz bibliotÄku
PythonBCC Ceph ir lÄns
CehÄ Ceph pievienots jauns hosts. PÄc datu daļas migrÄcijas uz to mÄs pamanÄ«jÄm, ka rakstīŔanas pieprasÄ«jumu apstrÄdes Ätrums ir ievÄrojami zemÄks nekÄ citos serveros.
AtŔķirÄ«bÄ no citÄm platformÄm, Å”ajÄ hostÄ tika izmantots bcache un jauns linux kodols 4.15. Å Ädas konfigurÄcijas hosts Å”eit tika izmantots pirmo reizi. Un brÄ«dÄ«, kad tas notika, bija skaidrs, ka problÄmu cÄlonis teorÄtiski varÄtu bÅ«t jebkas.

IzpÄtot hostu
SÄksim ar to, ka paskatÄ«simies, kas notiek ceph-osd procesÄ. Lai to izdarÄ«tu, mÄs izmantosim
perf un AttÄls norÄda, ka funkcija ):

tÄrÄja daudz laika pieprasÄ«juma nosÅ«tīŔanÄ funkcijÄ fdatasync() generic_make_request() . Tas nozÄ«mÄ, ka, visticamÄk, problÄmu cÄlonis atrodas Ärpus paÅ”a osd dema. Tas var bÅ«t gan kodols, gan diski. iostat izvade parÄdÄ«ja augstu bcache disku pieprasÄ«jumu apstrÄdes aizturi.PÄrbaudot hostu, mÄs atklÄjÄm, ka dema systemd-udevd patÄrÄ lielu CPU laiku ā apmÄram 20% vairÄkos kodolos. Å Ä« ir dÄ«vaina uzvedÄ«ba, tÄpÄc nepiecieÅ”ams noskaidrot tÄs iemeslu. TÄ kÄ Systemd-udevd darbojas ar uevent'iem, mÄs nolÄmÄm tos izvÄrtÄt, izmantojot
udevadm monitor . IzrÄdÄ«jÄs, ka sistÄmÄ tika Ä£enerÄts liels skaits change notikumu katram blokÄÅ”anas ierÄ«cei. Tas ir diezgan neparasti, tÄpÄc bÅ«s jÄskatÄs, kas Ä£enerÄ visus Å”os gadÄ«jumus.Izmantojot BCC rÄ«ku komplektu
KÄ jau mÄs noskaidrojÄm, kodols (un ceph dema sistÄmas izsaukumÄ) tÄrÄ daudz laika
. MÄÄ£inÄsim izmÄrÄ«t Ŕīs funkcijas darbÄ«bas Ätrumu. Jau . Tas nozÄ«mÄ, ka, visticamÄk, problÄmu cÄlonis atrodas Ärpus paÅ”a osd dema. Tas var bÅ«t gan kodols, gan diski. iostat izvade parÄdÄ«ja augstu bcache disku pieprasÄ«jumu apstrÄdes aizturi.pastÄv lielisks rÄ«ks ā funclatency funclatency. MÄs izsekosim demonam pÄc tÄ PID ar intervÄlu starp informÄcijas izvadÄm 1 sekunde un izvadÄ«sim rezultÄtu milisekundÄs.

Parasti Ŕī funkcija darbojas Ätri. Viss, ko tÄ dara ā pÄrsÅ«ta pieprasÄ«jumu uz ierÄ«ces draivera rindu.
Bcache ir kompleksa ierÄ«ce, kas faktiski sastÄv no trim diskdziÅiem:
- backing device (keÅ”Äjams disks), Å”ajÄ gadÄ«jumÄ tas ir lÄns HDD;
- caching device (keÅ”ÄjoÅ”ais disks), Å”eit tas ir viens NVMe ierÄ«ces nodalÄ«jums;
- virtuÄlo ierÄ«ci bcache, ar kuru darbojas lietotne.
MÄs zinÄm, ka pieprasÄ«juma pÄrsÅ«tīŔana aizkavÄjas, bet kura no Ŕīm ierÄ«cÄm? Par to runÄsim nedaudz vÄlÄk.
Å obrÄ«d mÄs zinÄm, ka uevent'i, iespÄjams, izraisa problÄmas. Atrast, kas tieÅ”i izraisa to Ä£enerÄciju, nav tik viegli. PieÅemam, ka tas ir kÄds softs, ko periodiski palaidz. ApskatÄ«sim, kurÅ” softs tiek palaidÄ«ts sistÄmÄ, izmantojot skriptu execsnoop no tÄ paÅ”a . PalaidÄ«sim to un novirzÄ«sim izvadi uz failu.
PiemÄram, Å”Ädi:
/usr/share/bcc/tools/execsnoop | tee ./execdump
NeizvirzÄ«sim Å”eit pilnu execsnoop izvadi, bet viena interesÄjoÅ”a rinda izskatÄ«jÄs Å”Ädi:
sh 1764905 5802 0 sudo arcconf getconfig 1 AD | grep Temperature | awk -F '[:\/]' '{print $2}' | sed 's\/^ ([0-9]*) C.*\/1\/'
TreÅ”Ä kolonna ir PPID (vecÄku PID) procesa. Process ar PID 5802 izrÄdÄ«jÄs viens no mÅ«su uzraudzÄ«bas sistÄmas pavedieniem. PÄrbaudot uzraudzÄ«bas sistÄmas konfigurÄciju, tika atrastas nepareizi iestatÄ«tas parametru vÄrtÄ«bas. HBA adaptera temperatÅ«ra tika nolasÄ«ta katras 30 sekundes, kas bija daudz biežÄk nekÄ nepiecieÅ”ams. PÄc pÄrbaudes intervÄla pagarinÄÅ”anas mÄs atklÄjÄm, ka pieprasÄ«jumu apstrÄdes aizture Å”ajÄ hostÄ vairs nav izteikta, salÄ«dzinot ar pÄrÄjiem hostiem.
Bet joprojÄm nav skaidrs, kÄpÄc bcache ierÄ«ce tik aizkavÄjÄs. MÄs izveidojÄm testÄÅ”anas platformu ar identisku konfigurÄciju un mÄÄ£inÄjÄm reproducÄt problÄmu, palaidzot fio uz bcache, periodiski palaidzot udevadm trigger Ä£enerÄcijai.
RakstÄ«t BCC bÄzÄtus rÄ«kus
MÄÄ£inÄsim uzrakstÄ«t vienkÄrÅ”u utilÄ«tu izsekoÅ”anai un ekrÄnÄ izvadīŔanai vislÄnÄkajiem zvaniem . Tas nozÄ«mÄ, ka, visticamÄk, problÄmu cÄlonis atrodas Ärpus paÅ”a osd dema. Tas var bÅ«t gan kodols, gan diski. iostat izvade parÄdÄ«ja augstu bcache disku pieprasÄ«jumu apstrÄdes aizturi.. MÅ«s arÄ« interesÄ diska nosaukums, kuram tika izsaukta Ŕī funkcija.
PlÄns ir vienkÄrÅ”s:
- ReÄ£istrÄjam kprobe uz . Tas nozÄ«mÄ, ka, visticamÄk, problÄmu cÄlonis atrodas Ärpus paÅ”a osd dema. Tas var bÅ«t gan kodols, gan diski. iostat izvade parÄdÄ«ja augstu bcache disku pieprasÄ«jumu apstrÄdes aizturi.:
- SaglabÄjam atmiÅÄ diska nosaukumu, kas pieejams caur funkcijas argumentu;
- SaglabÄjam laika zÄ«mogu.
- ReÄ£istrÄjam kretprobe uz atgrieÅ”anos no . Tas nozÄ«mÄ, ka, visticamÄk, problÄmu cÄlonis atrodas Ärpus paÅ”a osd dema. Tas var bÅ«t gan kodols, gan diski. iostat izvade parÄdÄ«ja augstu bcache disku pieprasÄ«jumu apstrÄdes aizturi.:
- SaÅemam paÅ”reizÄjo laika zÄ«mogu;
- MeklÄjam saglabÄto laika zÄ«mogu un salÄ«dzinÄm to ar paÅ”reizÄjo;
- Ja, ja rezultÄts ir lielÄks par noteikto, tad atrodam saglabÄto diska nosaukumu un izvadÄm to terminÄlÄ.
Kprobes un kretprobes izmanto punktu pÄrtraukÅ”anas mehÄnismu, lai dinamiski mainÄ«tu funkciju kodu. JÅ«s varat izlasÄ«t un rakstu par Å”o tÄmu. Ja aplÅ«ko dažÄdu utilÄ«tu kodu, tad var pamanÄ«t, ka tÄm ir identiska struktÅ«ra. TÄdÄļ Å”ajÄ rakstÄ mÄs izlaidÄ«sim skripta argumentu parsÄÅ”anu un pÄriesim pie paÅ”as BPF programmas. Teksts eBPF iekÅ” python skripta izskatÄs Å”Ädi:
bpf_text = """ # Šeit būs bpf programmas kods """
Lai apmainÄ«tos ar datiem starp funkcijÄm, eBPF programmas izmanto
hash-tabulas struct data_t { u64 pid; u64 ts; char comm[TASK_COMM_LEN]; u64 lat; char disk[DISK_NAME_LEN]; };BPF_HASH(p, u64, struct data_t); BPF_PERF_OUTPUT(events);
Å eit mÄs reÄ£istrÄjam hash-tabulu, kas saucas
, ar atslÄgas tipu pun vÄrtÄ«bu tipu u64 struct data_t . Tabula bÅ«s pieejama mÅ«su BPF programmas kontekstÄ. Makro BPF_PERF_OUTPUT reÄ£istrÄ citu tabulu, kas saucas, un tÄ tiek izmantota eventsdatenu pÄrsÅ«tīŔanai IzmÄrot aizturi starp funkcijas izsaukumu un tÄs atgrieÅ”anos, vai starp dažÄdu funkciju izsaukumiem, jÄÅem vÄrÄ, ka iegÅ«tajiem datiem jÄpieder vienam kontekstam. Citiem vÄrdiem sakot, jÄatceras par iespÄjamu funkciju paralÄlu izpildi. Mums ir iespÄja izmÄrÄ«t aizturi starp funkcijas izsaukumu viena procesa kontekstÄ un atgrieÅ”anos no Ŕīs funkcijas cita procesa kontekstÄ, bet tas, visticamÄk, bÅ«s bezjÄdzÄ«gi. Labi piemÄrs var bÅ«t
utilita biolatency struct request , kas atspoguļo vienu pieprasÄ«jumu uz disku.PÄc tam mums jÄuzraksta kods, kas tiks izpildÄ«ts, kad tiek izsaukta pÄtÄmÄ funkcija:
void start(struct pt_regs *ctx, struct bio *bio) { u64 pid = bpf_get_current_pid_tgid(); struct data_t data = {}; u64 ts = bpf_ktime_get_ns(); data.pid = pid; data.ts = ts; bpf_probe_read_str(&data.disk, sizeof(data.disk), (void*)bio->bi_disk->disk_name); p.update(&pid, &data); }
Å eit kÄ otrais arguments tiks aizvietots ar pirmo izsauktÄs funkcijas argumentu.
PÄc tam mÄs iegÅ«stam procesa PID, kurÄ darbojamies, un paÅ”reizÄjo laika zÄ«mogu nanosekundÄs. Viss tiek ierakstÄ«ts jaunizveidotajÄ struct data_t data struct data_t dataDisku nosaukumu iegÅ«stam no struktÅ«ras bio, kas tiek nodota izsaukuma laikÄ . Tas nozÄ«mÄ, ka, visticamÄk, problÄmu cÄlonis atrodas Ärpus paÅ”a osd dema. Tas var bÅ«t gan kodols, gan diski. iostat izvade parÄdÄ«ja augstu bcache disku pieprasÄ«jumu apstrÄdes aizturi., un saglabÄjam to tajÄ paÅ”Ä struktÅ«rÄ data. PÄdÄjais solis ir ieraksta pievienoÅ”ana iepriekÅ” minÄtajÄ hash tabulÄ.
NÄkamÄ funkcija tiks izsaukta atgriežoties no . Tas nozÄ«mÄ, ka, visticamÄk, problÄmu cÄlonis atrodas Ärpus paÅ”a osd dema. Tas var bÅ«t gan kodols, gan diski. iostat izvade parÄdÄ«ja augstu bcache disku pieprasÄ«jumu apstrÄdes aizturi.:
void stop(struct pt_regs *ctx) {
u64 pid = bpf_get_current_pid_tgid();
u64 ts = bpf_ktime_get_ns();
struct data_t* data = p.lookup(&pid);
if (data != 0 && data->ts > 0) {
bpf_get_current_comm(&data->comm, sizeof(data->comm));
data->lat = (ts - data->ts) / 1000;
if (data->lat > MIN_US) {
FACTOR
data->pid >>= 32;
events.perf_submit(ctx, data, sizeof(struct data_t));
}
p.delete(&pid);
}
}
Å Ä« funkcija ir lÄ«dzÄ«ga iepriekÅ”Äjai: mÄs uzzinÄm procesa PID un laika zÄ«mogu, bet neizveidojam jaunu data struktÅ«ru. TÄ vietÄ mÄs meklÄjam esoÅ”o struktÅ«ru hash tabulÄ, izmantojot atslÄgu == paÅ”reizÄjo PID. Ja struktÅ«ra tiek atrasta, mÄs uzzinÄm palaistÄ procesa nosaukumu un pievienojam to tai.
BinÄrais pÄrvietojums, ko mÄs Å”eit izmantojam, ir nepiecieÅ”ams, lai iegÅ«tu pavediena GID, t.i., PID galvenajam procesam, kas uzsÄka pavedienu, kurÄ mÄs strÄdÄjam. MÅ«su izsauktÄ funkcija atgriež gan pavediena GID, gan tÄ PID vienÄ 64-bit vÄrtÄ«bÄ.
Izvades terminÄlÄ« mÅ«s paÅ”laik neinteresÄ pavediena, bet interesÄ galvenais process. PÄc saÅemtÄs kavÄÅ”anÄs salÄ«dzinÄÅ”anas ar noteikto slieksni mÄs nododam savu struktÅ«ru data lietotÄja telpÄ caur tabulu events, pÄc tam dzÄÅ”am ierakstu no p.
Python skriptÄ, kas ielÄdÄs Å”o kodu, mums bÅ«s jÄaizvieto MIN_US un FACTOR ar kavÄÅ”anÄs sliekÅ”Åiem un laika vienÄ«bÄm, kuras mÄs nodosim caur argumentiem:
bpf_text = bpf_text.replace('MIN_US',str(min_usec))
if args.milliseconds:
bpf_text = bpf_text.replace('FACTOR','data->lat /= 1000;')
label = "msec"
else:
bpf_text = bpf_text.replace('FACTOR','')
label = "usec"
Tagad mums jÄpreparÄ BPF programma caur un jÄreÄ£istrÄ mÄri:
b = BPF(text=bpf_text)
b.attach_kprobe(event="generic_make_request",fn_name="start")
b.attach_kretprobe(event="generic_make_request",fn_name="stop")
Mums arÄ« bÅ«s jÄdefinÄ . Tabula bÅ«s pieejama mÅ«su BPF programmas kontekstÄ. Makro BPF_PERF_OUTPUT reÄ£istrÄ citu tabulu, kas saucas mÅ«su skriptÄ, pretÄjÄ gadÄ«jumÄ neko nevarÄsim izlasÄ«t:
TASK_COMM_LEN = 16 # linux/sched.h
DISK_NAME_LEN = 32 # linux/genhd.h
class Data(ct.Structure):
_fields_ = [("pid", ct.c_ulonglong),
("ts", ct.c_ulonglong),
("comm", ct.c_char * TASK_COMM_LEN),
("lat", ct.c_ulonglong),
("disk",ct.c_char * DISK_NAME_LEN)]
PÄdÄjais solis ā datu izvade uz terminÄla:
def print_event(cpu, data, size):
global start
event = ct.cast(data, ct.POINTER(Data)).contents
if start == 0:
start = event.ts
time_s = (float(event.ts - start)) / 1000000000
print("%-18.9f %-16s %-6d %-1s %s %s" % (time_s, event.comm, event.pid, event.lat, label, event.disk))
b["events"].open_perf_buffer(print_event)
# format output
start = 0
while 1:
try:
b.perf_buffer_poll()
except KeyboardInterrupt:
exit()
Scripts ir pieejams . MÄÄ£inÄsim to palaist izmÄÄ£inÄjuma platformÄ, kur darbojas fio, rakstot uz bcache, un izsaukt udevadm monitor:

Beidzot! Tagad mÄs redzam, ka tas, kas izskatÄ«jÄs kÄ bcache ierÄ«ces aizkave, patiesÄ«bÄ ir aizkave, kas notiek . Tas nozÄ«mÄ, ka, visticamÄk, problÄmu cÄlonis atrodas Ärpus paÅ”a osd dema. Tas var bÅ«t gan kodols, gan diski. iostat izvade parÄdÄ«ja augstu bcache disku pieprasÄ«jumu apstrÄdes aizturi. kÄ rezultÄtÄ no keÅ”otÄ diska.
Iedziļinieties Kernel
Kas tieÅ”i aizkavÄ pieprasÄ«juma nodoÅ”anu? MÄs redzam, ka aizkave rodas pat pirms pieprasÄ«juma kontÄ, t.i., konkrÄta pieprasÄ«juma uzskaitīŔana turpmÄkai statistikas izsniegÅ”anai (/,proc/,diskstats vai iostat) vÄl nav sÄkusies. To ir viegli pÄrbaudÄ«t, palaist iostat problÄmas reproducÄÅ”anas laikÄ, vai , kas balstÄs uz pieprasÄ«juma kontÄÅ”anas sÄkumu un beigÄm. Neviens no Å”iem rÄ«kiem neparÄdÄ«s problÄmas pieprasÄ«jumiem uz keÅ”oto disku.
Ja mÄs paskatÄmies uz funkciju . Tas nozÄ«mÄ, ka, visticamÄk, problÄmu cÄlonis atrodas Ärpus paÅ”a osd dema. Tas var bÅ«t gan kodols, gan diski. iostat izvade parÄdÄ«ja augstu bcache disku pieprasÄ«jumu apstrÄdes aizturi., mÄs redzam, ka pirms pieprasÄ«juma konta tiek izsauktas vÄl divas funkcijas. PirmÄ ā generic_make_request_checks(), veic pieprasÄ«juma likumÄ«bas pÄrbaudes attiecÄ«bÄ uz diska iestatÄ«jumiem. OtrÄ ā , kurÄ ir interesants izsaukums, :
ret = wait_event_interruptible(q->mq_freeze_wq,
(atomic_read(&q->mq_freeze_depth) == 0 &&
(preempt || !blk_queue_preempt_only(q))) ||
blk_queue_dying(q));
Å ajÄ funkcijÄ kodols gaida, kad queue tiek atkausÄta. MÄs izmÄrÄ«sim aizkavi blk_queue_enter():
~# /usr/share/bcc/tools/funclatency blk_queue_enter -i 1 -m
Izsekoju 1 funkciju "blk_queue_enter"... Nospiediet Ctrl-C, lai pabeigtu.
msecs : skaits distribūcija
0 -> 1 : 341 |****************************************|
msecs : skaits distribūcija
0 -> 1 : 316 |****************************************|
msecs : skaits distribūcija
0 -> 1 : 255 |****************************************|
2 -> 3 : 0 | |
4 -> 7 : 0 | |
8 -> 15 : 1 | |
IzskatÄs, ka mÄs esam tuvu atminÄjuma. Funkcijas, kas tiek izmantotas "iesaldÄÅ”anai/atkausÄÅ”anai" rindÄ ā tas ir un . TÄs tiek izmantotas, kad nepiecieÅ”ams mainÄ«t rindas pieprasÄ«jumu iestatÄ«jumus, kas ir potenciÄli bÄ«stami pieprasÄ«jumiem, kas jau atrodas Å”ajÄ rindÄ. Izsaucot blk_mq_freeze_queue() funkciju tiek palielinÄts skaitÄ«tÄjs q->mq_freeze_depth. PÄc tam kods gaida, lÄ«dz rinda ir iztukÅ”ota .
Å Ä«s rindas iztukÅ”oÅ”anas gaidīŔanas laiks ir ekvivalents diska kavÄÅ”anÄs laikam, jo āākods gaida, lÄ«dz tiek pabeigtas visas iepriekÅ” ieplÄnotÄs operÄcijas. TiklÄ«dz rinda ir tukÅ”a, tiek piemÄroti iestatÄ«jumi. PÄc tam tiek izsaukts , kas samazina skaitÄ«tÄju freeze_depth.
Tagad mÄs zinÄm pietiekami, lai novÄrstu situÄciju. Komanda udevadm trigger rezultÄtÄ noved pie iestatÄ«jumu piemÄroÅ”anas blokierÄ«cei. Å ie iestatÄ«jumi ir aprakstÄ«ti udev noteikumos. MÄs varam noskaidrot, kuri iestatÄ«jumi Ā«iesaldÄĀ» rindu, mÄÄ£inot tos mainÄ«t caur sysfs vai apskatot kodu. Varam arÄ« izmÄÄ£inÄt BCC , kas parÄdÄ«s terminÄlÄ« kodu steka pÄdas un lietotÄja telpu katram izsaukumam blk_freeze_queue, piemÄram:
~# /usr/share/bcc/tools/trace blk_freeze_queue -K -U
PID TID COMM FUNC
3809642 3809642 systemd-udevd blk_freeze_queue
blk_freeze_queue+0x1 [kernel]
elevator_switch+0x29 [kernel]
elv_iosched_store+0x197 [kernel]
queue_attr_store+0x5c [kernel]
sysfs_kf_write+0x3c [kernel]
kernfs_fop_write+0x125 [kernel]
__vfs_write+0x1b [kernel]
vfs_write+0xb8 [kernel]
sys_write+0x55 [kernel]
do_syscall_64+0x73 [kernel]
entry_SYSCALL_64_after_hwframe+0x3d [kernel]
__write_nocancel+0x7 [libc-2.23.so]
[unknown]
3809631 3809631 systemd-udevd blk_freeze_queue
blk_freeze_queue+0x1 [kernel]
queue_requests_store+0xb6 [kernel]
queue_attr_store+0x5c [kernel]
sysfs_kf_write+0x3c [kernel]
kernfs_fop_write+0x125 [kernel]
__vfs_write+0x1b [kernel]
vfs_write+0xb8 [kernel]
sys_write+0x55 [kernel]
do_syscall_64+0x73 [kernel]
entry_SYSCALL_64_after_hwframe+0x3d [kernel]
__write_nocancel+0x7 [libc-2.23.so]
[unknown]
Udev noteikumi mainÄs diezgan reti un parasti tas notiek kontrolÄti. TÄpÄc mÄs redzam, ka pat iepriekÅ” noteikto vÄrtÄ«bu izmantoÅ”ana izraisa pieprasÄ«juma sÅ«tīŔanas kavÄÅ”anÄs pie diska pieaugumu. Protams, Ä£enerÄt udev notikumus, kad nav izmaiÅu disku konfigurÄcijÄ (piemÄram, ierÄ«ce netiek pievienota/atslÄgta), nav Ä«paÅ”i laba prakse. TomÄr mÄs varam palÄ«dzÄt kodam neveikt nevajadzÄ«gu darbu un neĀ«iesaldÄtĀ» pieprasÄ«jumu rindu, ja tam nav vajadzÄ«bas. novÄrÅ” situÄciju.
SecinÄjums
eBPF ir ļoti elastÄ«gs un jaudÄ«gs rÄ«ks. Å ajÄ rakstÄ mÄs apskatÄ«jÄm vienu praktisku gadÄ«jumu un demonstrÄjÄm nelielu daļu no tÄ, ko ir iespÄjams izdarÄ«t. Ja jÅ«s interesÄ BCC rÄ«ku izstrÄde, noteikti ieskatieties , kas labi apraksta darba pamatus.
Ir arÄ« citi interesanti rÄ«ki atkļūdoÅ”anai un profilÄÅ”anai, kas balstÄ«ti uz eBPF. Viens no tiem ir , kas ļauj rakstÄ«t jaudÄ«gus vienrindas skriptus un mazas programmas uz awk lÄ«dzÄ«gas valodas. Cits ā , ļauj vÄcÄt augstas izŔķirtspÄjas zemu lÄ«meÅa metriku tieÅ”i uz jÅ«su prometheus serveri, ar iespÄju vÄlÄk iegÅ«t skaistu vizualizÄciju un pat brÄ«dinÄjumus.
Avots: habr.com
