Аз баландии латентии Ceph то Ядро бо истифода аз eBPF/BCC

Аз баландии латентии Ceph то Ядро бо истифода аз eBPF/BCC

Linux дорои миқдори зиёди асбобҳо барои ислоҳи ядро ​​ва барномаҳо мебошад. Аксарияти онхо ба ичрои корхо таъсири манфй мерасонанд ва дар истехсолот истифода бурда намешаванд.

Якчанд сол пеш буд асбоби дигар тартиб дода шудааст - eBPF. Он имкон медиҳад, ки ядро ​​ва барномаҳои корбарро бо хароҷоти кам ва бидуни эҳтиёҷ ба барқарорсозии барномаҳо ва бор кардани модулҳои тарафи сеюм ба ядро ​​пайгирӣ кунед.

Аллакай бисёр утилитаҳои барномавӣ мавҷуданд, ки eBPF-ро истифода мебаранд ва дар ин мақола мо дида мебароем, ки чӣ гуна утилитаи профили худро дар асоси китобхона нависед PythonBCC. Мақола ба воқеаҳои воқеӣ асос ёфтааст. Мо аз мушкилот ба ислоҳ мегузарем, то нишон диҳем, ки чӣ гуна хидматҳои мавҷуда дар ҳолатҳои мушаххас истифода мешаванд.

Ceph суст аст

Хости нав ба кластери Ceph илова карда шуд. Пас аз интиқоли баъзе маълумот ба он, мо мушоҳида кардем, ки суръати коркарди дархостҳои навиштан аз ҷониби он нисбат ба серверҳои дигар хеле пасттар аст.

Аз баландии латентии Ceph то Ядро бо истифода аз eBPF/BCC
Баръакси дигар платформаҳо, ин мизбон bcache ва ядрои нави Linux 4.15-ро истифода бурд. Ин бори аввал буд, ки мизбони ин конфигуратсия дар ин ҷо истифода мешуд. Ва дар он лахза маълум буд, ки решаи масъала аз чихати назариявй хар чиз буда метавонад.

Тафтиши мизбон

Биёед аз бубинем, ки дар дохили раванди ceph-osd чӣ рӯй медиҳад. Барои ин мо истифода хоҳем кард комил и алангагирӣ (бештар дар бораи он шумо метавонед хонед дар ин ҷо):

Аз баландии латентии Ceph то Ядро бо истифода аз eBPF/BCC
Тасвир ба мо мегӯяд, ки функсия fdatasync() барои фиристодани дархост ба вазифаҳо вақти зиёд сарф кард generic_make_request(). Ин маънои онро дорад, ки эҳтимолан сабаби мушкилот дар ҷое берун аз худи демони osd аст. Ин метавонад ядро ​​ё дискҳо бошад. Натиҷаи iostat дар коркарди дархостҳо аз ҷониби дискҳои bcache таъхири баландро нишон дод.

Ҳангоми тафтиши ҳост, мо дарёфтем, ки демони systemd-udevd миқдори зиёди вақти CPU-ро сарф мекунад - тақрибан 20% дар якчанд ядро. Ин рафтори аҷиб аст, бинобар ин шумо бояд фаҳмед, ки чаро. Азбаски Systemd-udevd бо рӯйдодҳо кор мекунад, мо тасмим гирифтем, ки онҳоро аз назар гузаронем монитор udevadm. Маълум мешавад, ки барои ҳар як дастгоҳи блок дар система миқдори зиёди рӯйдодҳои тағирёбанда тавлид шудаанд. Ин хеле ғайриоддӣ аст, аз ин рӯ мо бояд бубинем, ки ҳамаи ин рӯйдодҳоро чӣ тавлид мекунанд.

Истифодаи асбобҳои BCC

Тавре ки мо аллакай фаҳмидем, ядро ​​​​(ва демони ceph дар занги система) вақти зиёдро дар generic_make_request(). Биёед кӯшиш кунем, ки суръати ин функсияро чен кунем. ДАР МФБ Аллакай як хидмати олиҷаноб мавҷуд аст - функлатнокӣ. Мо демонро аз рӯи PID-и он бо фосилаи 1 сония байни натиҷаҳо пайгирӣ мекунем ва натиҷаро дар миллисонияҳо мебарорем.

Аз баландии латентии Ceph то Ядро бо истифода аз eBPF/BCC
Ин хусусият одатан зуд кор мекунад. Танҳо ин аст, ки дархостро ба навбати драйвери дастгоҳ интиқол диҳед.

Bcache дастгоҳи мураккабест, ки воқеан аз се диск иборат аст:

  • дастгоҳи пуштибонӣ (диски кэш), дар ин ҳолат он HDD суст аст;
  • дастгоҳи кэшкунӣ (диски кэш), дар ин ҷо ин як қисми дастгоҳи NVMe аст;
  • дастгоҳи виртуалии bcache, ки барнома бо он кор мекунад.

Мо медонем, ки интиқоли дархост суст аст, аммо барои кадоме аз ин дастгоҳҳо? Мо бо ин каме дертар сарукор хоҳем кард.

Мо ҳоло медонем, ки ҳодисаҳо эҳтимолан мушкилотро ба вуҷуд меоранд. Ҷустуҷӯи он, ки ба насли онҳо чӣ сабаб мешавад, он қадар осон нест. Фарз мекунем, ки ин як навъ нармафзорест, ки давра ба давра оғоз мешавад. Биёед бубинем, ки чӣ гуна нармафзор дар система бо истифода аз скрипт кор мекунад execsnoop аз ҳамон Маҷмӯи хидматрасонии BCC. Биёед онро иҷро кунем ва натиҷаро ба файл фиристем.

Масалан, чунин:

/usr/share/bcc/tools/execsnoop  | tee ./execdump

Мо натиҷаи пурраи execsnoop-ро дар ин ҷо нишон намедиҳем, аммо як хати таваҷҷӯҳи мо чунин буд:

sh 1764905 5802 0 sudo arcconf getconfig 1 AD | grep Temperature | awk -F '[:/]' '{print $2}' | sed 's/^ ([0-9]*) C.*/1/'

Сутуни сеюм PPID (PID волидайн) -и раванд мебошад. Раванди PID 5802 яке аз риштаҳои системаи мониторинги мо гардид. Ҳангоми тафтиши конфигуратсияи системаи мониторинг параметрҳои хато ошкор карда шуданд. Ҳарорати адаптери HBA дар ҳар 30 сония гирифта мешуд, ки ин аз зарурат бештар аст. Пас аз тағир додани фосилаи санҷиш ба фосилаи дарозтар, мо фаҳмидем, ки таъхири коркарди дархост дар ин ҳост дигар дар муқоиса бо дигар ҳостҳо фарқ намекунад.

Аммо то ҳол маълум нест, ки чаро дастгоҳи bcache ин қадар суст буд. Мо як платформаи санҷиширо бо конфигуратсияи якхела омода кардем ва кӯшиш кардем, ки мушкилотро тавассути иҷро кардани fio дар bcache, давра ба давра триггери udevadm барои тавлиди рӯйдодҳо дубора тавлид кунем.

Навиштани асбобҳои дар асоси BCC

Биёед кӯшиш кунем, ки як утилитаи оддиро барои пайгирӣ ва намоиши зангҳои сусттарин нависем generic_make_request(). Мо инчунин ба номи диске таваҷҷӯҳ дорем, ки барои он ин функсия номида шудааст.

Нақша оддӣ аст:

  • Бақайдгирӣ кпроб ба generic_make_request():
    • Мо номи дискро дар хотира нигоҳ медорем, ки тавассути аргументи функсия дастрас аст;
    • Мо тамғаи вақтро захира мекунем.

  • Бақайдгирӣ кретпроб барои бозгашт аз generic_make_request():
    • Мо тамғаи вақтро мегирем;
    • Мо тамғаи вақти захирашударо меҷӯем ва онро бо вақти ҷорӣ муқоиса мекунем;
    • Агар натиҷа аз нишондодашуда зиёдтар бошад, мо номи диски захирашударо пайдо мекунем ва онро дар терминал нишон медиҳем.

Кпробхо и кретпробхо барои тағир додани рамзи функсионалӣ дар парвоз механизми нуқтаро истифода баред. Шумо метавонед хонед ҳуҷҷатҳо и хуб мақола дар ин мавзӯъ. Агар шумо ба рамзи хидматҳои гуногун нигаред МФБ, пас шумо мебинед, ки онҳо сохтори якхела доранд. Ҳамин тавр, дар ин мақола мо далелҳои таҳлили скриптро аз даст медиҳем ва ба худи барномаи BPF мегузарем.

Матни eBPF дар дохили скрипти python чунин менамояд:

bpf_text = “”” # Here will be the bpf program code “””

Барои мубодилаи маълумот байни функсияҳо, барномаҳои eBPF истифода мебаранд мизҳои ҳаш. Мо хам хамин тавр мекунем. Мо раванди PID-ро ҳамчун калид истифода мебарем ва сохторро ҳамчун арзиш муайян мекунем:

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);

Дар ин ҷо мо ҷадвали hash номро қайд мекунем p, бо навъи калид u64 ва арзиши намуд сохтори data_t. Ҷадвал дар доираи барномаи BPF мо дастрас хоҳад буд. Макроси BPF_PERF_OUTPUT ҷадвали дигареро, ки ном дорад, сабт мекунад чорабиниҳо, ки барои он истифода мешавад интиқоли маълумот ба фазои корбар.

Ҳангоми чен кардани таъхирҳо байни занги функсия ва баргаштан аз он ё байни зангҳо ба вазифаҳои гуногун, шумо бояд ба назар гиред, ки маълумоти гирифташуда бояд ба як контекст тааллуқ дошта бошанд. Ба ибораи дигар, ба шумо лозим аст, ки дар бораи имконпазирии имконпазири параллелии функсияҳо дар хотир доред. Мо қобилияти чен кардани таъхирро байни даъвати функсия дар заминаи як раванд ва баргаштан аз он функсия дар заминаи раванди дигар дорем, аммо ин эҳтимол бефоида аст. Намунаи хубе дар ин ҷо хоҳад буд воситаи биологӣ, ки дар он калиди ҷадвали ҳаш ба нишоннамо гузошта шудааст дархости сохторӣ, ки як дархости дискро инъикос мекунад.

Минбаъд, мо бояд кодеро нависем, ки ҳангоми даъват кардани функсияи мавриди омӯзиш кор хоҳад кард:

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);
}

Дар ин ҷо аргументи якуми функсияи даъватшуда ҳамчун далели дуюм иваз карда мешавад generic_make_request(). Пас аз ин, мо PID-и равандро дар контекст, ки мо кор карда истодаем ва тамғаи вақтро дар наносонияҳо мегирем. Мо ҳама чизро дар як нав интихобшуда менависем сохтори data_t маълумот. Мо номи дискро аз сохтор мегирем биологии, ки хангоми занг задан мегузарад generic_make_request(), ва онро дар ҳамон сохтор захира кунед маълумот. Қадами охирин ин илова кардани вуруд ба ҷадвали ҳаш мебошад, ки қаблан зикр шуда буд.

Функсияи зерин ҳангоми бозгашт аз он даъват карда мешавад generic_make_request():

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);
    }
}

Ин функсия ба функсияи қаблӣ монанд аст: мо PID-и раванд ва тамғаи вақтро мефаҳмем, аммо хотираро барои сохтори нави додаҳо ҷудо намекунем. Ба ҷои ин, мо дар ҷадвали hash сохтори аллакай мавҷудбударо бо истифода аз калиди == PID ҷорӣ ҷустуҷӯ мекунем. Агар сохтор пайдо шавад, мо номи раванди иҷрошавандаро пайдо мекунем ва онро ба он илова мекунем.

Сменаи дуӣ, ки мо дар ин ҷо истифода мебарем, барои ба даст овардани риштаи GID лозим аст. ки. PID-и раванди асосӣ, ки риштаро дар контекст, ки мо кор карда истодаем, оғоз кард. Функсияе, ки мо даъват мекунем bpf_get_current_pid_tgid() ҳам GID-и ришта ва ҳам PID-и онро бо арзиши ягонаи 64-бит бармегардонад.

Ҳангоми баромад ба терминал, мо ҳоло ба ришта таваҷҷӯҳ зоҳир намекунем, аммо мо ба раванди асосӣ таваҷҷӯҳ дорем. Пас аз муқоисаи таъхири натиҷавӣ бо ҳадди додашуда, мо сохтори худро мегузарем маълумот ба фазои корбар тавассути ҷадвал чорабиниҳо, пас аз он мо вурудро аз p.

Дар скрипти python, ки ин кодро бор мекунад, мо бояд MIN_US ва FACTOR-ро бо ҳадди таъхир ва воҳидҳои вақт иваз кунем, ки мо онҳоро аз аргументҳо мегузарем:

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"

Акнун мо бояд барномаи BPF-ро тавассути Макроси BPF ва бақайдгирии намунаҳо:

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

Мо низ бояд муайян кунем сохтори data_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)]

Қадами охирин ин баровардани маълумот ба терминал аст:

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()

Худи скрипт дар он дастрас аст GItHub. Биёед кӯшиш кунем, ки онро дар платформаи санҷишӣ иҷро кунем, ки дар он fio кор мекунад, ба bcache нависед ва udevadm мониторро даъват кунед:

Аз баландии латентии Ceph то Ядро бо истифода аз eBPF/BCC
Ниҳоят! Ҳоло мо мебинем, ки он чизе, ки ба дастгоҳи қатъии bcache монанд буд, воқеан занги қатъист generic_make_request() барои диски кэшшуда.

Ба ядро ​​​​кобед

Ҳангоми интиқоли дархост маҳз чӣ суст мешавад? Мо мебинем, ки таъхир ҳатто пеш аз оғози баҳисобгирии дархост рух медиҳад, яъне. баҳисобгирии дархости мушаххас барои баровардани маълумоти минбаъдаи омор оид ба он (/proc/diskstats ё iostat) ҳанӯз оғоз нашудааст. Инро тавассути иҷро кардани iostat ҳангоми дубора тавлид кардани мушкилот ба осонӣ тафтиш кардан мумкин аст ё Биолатентии скрипти BCC, ки ба оғоз ва анҷоми баҳисобгирии дархост асос ёфтааст. Ҳеҷ яке аз ин утилитаҳо мушкилотро барои дархостҳо ба диски кэш нишон намедиҳад.

Агар мо ба функсия назар кунем generic_make_request(), пас мо мебинем, ки пеш аз оғози дархост баҳисобгирӣ боз ду функсия даъват карда мешавад. Аввал - generic_make_request_checks(), қонунӣ будани дархостро оид ба танзимоти диск тафтиш мекунад. Дуюм - blk_queue_enter(), ки як даъвати ҷолиб дорад wait_event_interruptible():

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));

Дар он ядро ​​интизор аст, ки навбат яхбандӣ шавад. Биёед таъхирро чен кунем blk_queue_enter():

~# /usr/share/bcc/tools/funclatency  blk_queue_enter -i 1 -m               	 
Tracing 1 functions for "blk_queue_enter"... Hit Ctrl-C to end.

 	msecs           	: count 	distribution
     	0 -> 1      	: 341  	|****************************************|

 	msecs           	: count 	distribution
     	0 -> 1      	: 316  	|****************************************|

 	msecs           	: count 	distribution
     	0 -> 1      	: 255  	|****************************************|
     	2 -> 3      	: 0    	|                                    	|
     	4 -> 7      	: 0    	|                                    	|
     	8 -> 15     	: 1    	|                                    	|

Чунин ба назар мерасад, ки мо ба ҳалли мушкилот наздикем. Функсияҳое, ки барои ях кардан/ кушодани навбат истифода мешаванд, инҳоянд blk_mq_freeze_queue и blk_mq_unfreeze_queue. Онҳо ҳангоми зарурати тағир додани танзимоти навбати дархост истифода мешаванд, ки барои дархостҳо дар ин навбат эҳтимолан хатарноканд. Ҳангоми занг задан blk_mq_freeze_queue() функсия blk_freeze_queue_start() ҳисобкунак зиёд мешавад q->умқи_мқ_ях. Пас аз ин, ядро ​​интизор аст, ки навбат холӣ шавад blk_mq_freeze_queue_wait().

Вақт барои тоза кардани ин навбат ба таъхири диск баробар аст, зеро ядро ​​интизори анҷоми ҳама амалҳои навбатдор аст. Пас аз холӣ шудани навбат, тағироти танзимот татбиқ карда мешаванд. Пас аз он даъват карда мешавад blk_mq_unfreeze_queue(), кам кардани ҳисобкунак умқи яхкунӣ.

Ҳоло мо барои ислоҳи вазъият кофӣ медонем. Фармони триггери udevadm боиси татбиқи танзимоти дастгоҳи блок мегардад. Ин танзимот дар қоидаҳои udev тасвир шудаанд. Мо метавонем фаҳмем, ки кадом танзимот навбатро ях мекунад, тавассути кӯшиши тағир додани онҳо тавассути sysfs ё тавассути дидани рамзи сарчашмаи ядро. Мо инчунин метавонем утилитаи BCC-ро санҷем пайгирӣ, ки барои ҳар як занг ба терминал пайгирии ядро ​​ва фазои корбаронро мебарорад blk_freeze_queue, масалан:

~# /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 хеле кам тағир меёбанд ва одатан ин ба таври назоратшаванда рӯй медиҳад. Ҳамин тавр, мо мебинем, ки ҳатто татбиқи арзишҳои аллакай муқарраршуда боиси афзоиши таъхири интиқоли дархост аз барнома ба диск мегардад. Албатта, тавлиди рӯйдодҳои udev, вақте ки дар конфигуратсияи диск ягон тағирот вуҷуд надорад (масалан, дастгоҳ васл нашудааст / ҷудо карда нашудааст) таҷрибаи хуб нест. Аммо, мо метавонем ба ядро ​​​​ёрӣ расонем, ки кори нолозимро иҷро накунад ва дар ҳолати зарурӣ навбати дархостро боздорад. Сеюм хурд ӯҳдадор вазъиятро ислох.

хулоса

eBPF як воситаи хеле фасеҳ ва пурқувват аст. Дар мақола мо як мисоли амалиро дида баромадем ва як қисми ками он чизеро, ки метавон анҷом дод, нишон дод. Агар шумо ба таҳияи хидматҳои BCC таваҷҷӯҳ дошта бошед, ба он назар кардан лозим аст дастури расмӣ, ки асосхоро нагз тасвир мекунад.

Дигар асбобҳои ҷолиби ислоҳ ва профилсозӣ дар асоси eBPF мавҷуданд. Яке аз онҳо - bpftrace, ки ба шумо имкон медиҳад, ки бо забони ба awk шабоҳати пурқуввати яксавор ва барномаҳои хурд нависед. Дигар - ebpf_exporter, ба шумо имкон медиҳад, ки ченакҳои сатҳи паст ва баландсифатро мустақиман дар сервери prometheus-и худ бо қобилияти баъдтар ба даст овардани визуализатсияи зебо ва ҳатто огоҳӣ ҷамъ кунед.

Манбаъ: will.com

Илова Эзоҳ