Saka High Ceph Latency menyang Kernel Patch nggunakake eBPF / BCC

Saka High Ceph Latency menyang Kernel Patch nggunakake eBPF / BCC

Linux duwe akeh alat kanggo debugging kernel lan aplikasi. Umume duwe pengaruh negatif ing kinerja aplikasi lan ora bisa digunakake ing produksi.

Sawetara taun kepungkur ana alat liyane wis dikembangakΓ© -eBPF. Iku ndadekake iku bisa kanggo nglacak kernel lan aplikasi pangguna karo overhead kurang lan tanpa perlu kanggo mbangun maneh program lan mbukak modul pihak katelu menyang kernel.

Wis akeh utilitas aplikasi sing nggunakake eBPF, lan ing artikel iki kita bakal weruh carane nulis sarana profil dhewe adhedhasar perpustakaan. PythonBCC. Artikel kasebut adhedhasar kedadeyan nyata. Kita bakal pindhah saka masalah kanggo ndandani kanggo nuduhake carane utilitas sing ana bisa digunakake ing kahanan tartamtu.

Ceph Slow

Tuan rumah anyar wis ditambahake ing kluster Ceph. Sawise migrasi sawetara data menyang, kita weruh sing kacepetan Processing nulis panjalukan dening iku luwih murah tinimbang ing server liyane.

Saka High Ceph Latency menyang Kernel Patch nggunakake eBPF / BCC
Ora kaya platform liyane, host iki nggunakake bcache lan kernel linux 4.15 anyar. Iki minangka sepisanan host konfigurasi iki digunakake ing kene. Lan ing wayahe iku cetha yen oyod saka masalah bisa teoritis apa wae.

Investigating Host

Ayo diwiwiti kanthi ndeleng apa sing kedadeyan ing proses ceph-osd. Kanggo iki kita bakal nggunakake parfum ΠΈ flamescope (luwih akeh babagan sing bisa diwaca kene):

Saka High Ceph Latency menyang Kernel Patch nggunakake eBPF / BCC
Gambar ngandhani yen fungsi kasebut fdatasync() ngginakaken akèh wektu ngirim panjalukan kanggo fungsi generic_make_request(). Iki tegese paling kamungkinan sabab saka masalah nang endi wae njaba daemon osd dhewe. Iki bisa dadi kernel utawa disk. Output iostat nuduhake latensi dhuwur ing pangolahan panjalukan dening disk bcache.

Nalika mriksa host, kita nemokake manawa daemon systemd-udevd nggunakake akeh wektu CPU - udakara 20% ing sawetara inti. Iki minangka prilaku sing aneh, mula sampeyan kudu ngerti sebabe. Wiwit Systemd-udevd dianggo karo uevents, kita mutusakΓ© kanggo ndeleng wong liwat monitor udevadm. Pranyata metu sing nomer akeh acara owah-owahan padha kui kanggo saben piranti pamblokiran ing sistem. Iki cukup ora biasa, mula kita kudu ndeleng apa sing nggawe kabeh acara kasebut.

Nggunakake BCC Toolkit

Kaya sing wis dingerteni, kernel (lan daemon ceph ing panggilan sistem) mbuwang akeh wektu ing generic_make_request(). Ayo nyoba ngukur kacepetan fungsi iki. ING Bcc Wis ana sarana sing apik banget - fungsi. Kita bakal nglacak daemon kanthi PID kanthi interval 1 detik antarane output lan ngasilake asil ing milliseconds.

Saka High Ceph Latency menyang Kernel Patch nggunakake eBPF / BCC
Fitur iki biasane dianggo kanthi cepet. Kabeh sing ditindakake yaiku ngirim panjalukan menyang antrian driver piranti.

Bcache iku piranti Komplek sing bener kasusun saka telung disk:

  • piranti gawe (disk cache), ing kasus iki iku HDD alon;
  • piranti caching (caching disk), kene iki salah siji partisi saka piranti NVMe;
  • piranti virtual bcache karo kang aplikasi mlaku.

Kita ngerti manawa transmisi panyuwunan alon, nanging kanggo piranti kasebut? Kita bakal ngrampungake iki mengko.

Saiki kita ngerti manawa acara bisa nyebabake masalah. Nggoleki apa sing nyebabake generasi kasebut ora gampang. Ayo nganggep manawa iki minangka piranti lunak sing diluncurake sacara periodik. Ayo ndeleng piranti lunak apa sing mlaku ing sistem nggunakake skrip eksekutor saka padha Paket utilitas BCC. Ayo mbukak lan ngirim output menyang file.

Contone kaya iki:

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

Kita ora bakal nuduhake output lengkap execsnoop ing kene, nanging siji baris sing menarik kanggo kita katon kaya iki:

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

Kolom katelu yaiku PPID (parent PID) saka proses kasebut. Proses karo PID 5802 dadi salah sawijining utas sistem pemantauan kita. Nalika mriksa konfigurasi sistem ngawasi, paramèter sing salah ditemokake. Suhu adaptor HBA dijupuk saben 30 detik, sing luwih asring tinimbang sing dibutuhake. Sawise ngganti interval mriksa kanggo maneh, kita nemokake yen latensi pangolahan panjalukan ing host iki ora ana maneh dibandhingake karo host liyane.

Nanging isih ora jelas kenapa piranti bcache dadi alon. We nyiapake platform test karo konfigurasi podho rupo lan nyoba kanggo ngasilaken masalah dening mlaku fio ing bcache, periodik mlaku udevadm pemicu kanggo generate uevents.

Nulis BCC-Based Tools

Ayo nyoba nulis sarana sing gampang kanggo nglacak lan nampilake telpon sing paling alon generic_make_request(). Kita uga kasengsem ing jeneng drive sing fungsi iki diarani.

Rencana kasebut prasaja:

  • Ndaftar kprobe ing generic_make_request():
    • Kita nyimpen jeneng disk menyang memori, bisa diakses liwat argumen fungsi;
    • Kita nyimpen stempel wektu.

  • Ndaftar kretprobe kanggo bali saka generic_make_request():
    • We njaluk timestamp saiki;
    • Kita nggoleki cap wektu sing disimpen lan mbandhingake karo sing saiki;
    • Yen asil luwih gedhe tinimbang sing ditemtokake, banjur kita nemokake jeneng disk sing disimpen lan ditampilake ing terminal.

Kprobes ΠΈ kretprobes nggunakake mekanisme breakpoint kanggo ngganti kode fungsi ing fly. Sampeyan bisa maca dokumentasi ΠΈ apik artikel babagan topik iki. Yen katon ing kode saka macem-macem keperluan ing Bcc, banjur sampeyan bisa ndeleng manawa padha duwe struktur sing padha. Dadi ing artikel iki kita bakal ngliwati argumen skrip parsing lan pindhah menyang program BPF dhewe.

Teks eBPF ing skrip python katon kaya iki:

bpf_text = β€œβ€β€ # Here will be the bpf program code β€œβ€β€

Kanggo ngganti data antarane fungsi, program eBPF digunakake tabel hash. Kita bakal nindakake sing padha. Kita bakal nggunakake proses PID minangka kunci, lan nemtokake struktur minangka nilai:

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

Ing kene kita ndhaptar tabel hash sing diarani p, kanthi jinis kunci u64 lan nilai saka jinis struct data_t. Tabel kasebut bakal kasedhiya ing konteks program BPF kita. Macro BPF_PERF_OUTPUT ndhaptar tabel liyane sing diarani acara, kang digunakake kanggo transmisi data menyang ruang panganggo.

Nalika ngukur wektu tundha antarane nelpon fungsi lan bali saka iku, utawa antarane telpon kanggo fungsi beda, sampeyan kudu njupuk menyang akun sing data ditampa kudu kagungane konteks padha. Ing tembung liya, sampeyan kudu ngelingi babagan kemungkinan peluncuran fungsi paralel. Kita duwe kemampuan kanggo ngukur latensi antarane nelpon fungsi ing konteks siji proses lan bali saka fungsi kasebut ing konteks proses liyane, nanging iki ora ana gunane. Conto apik ing kene sarana biolatensi, ing ngendi tombol tabel hash disetel menyang pointer menyang request struktur, sing nggambarake siji panjalukan disk.

Sabanjure, kita kudu nulis kode sing bakal ditindakake nalika fungsi sing diteliti diarani:

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

Ing kene argumen pisanan saka fungsi sing diarani bakal diganti minangka argumen kapindho generic_make_request(). Sawise iki, kita entuk PID proses ing konteks sing digunakake, lan cap wektu saiki ing nanodetik. We nulis kabeh mudhun ing anyar dipilih struct data_t data. Kita entuk jeneng disk saka struktur kasebut bio, sing liwati nalika nelpon generic_make_request(), lan simpen ing struktur sing padha data. Langkah pungkasan yaiku nambah entri menyang tabel hash sing kasebut sadurunge.

Fungsi ing ngisor iki bakal diarani nalika bali saka 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);
    }
}

Fungsi iki padha karo sing sadurunge: kita ngerteni PID proses lan cap wektu, nanging ora menehi memori kanggo struktur data anyar. Nanging, kita nelusuri tabel hash kanggo struktur sing wis ana nggunakake tombol == PID saiki. Yen struktur ditemokake, banjur kita nemokake jeneng proses sing mlaku lan ditambahake.

Pergeseran binar sing digunakake ing kene dibutuhake kanggo entuk GID benang. sing. PID saka proses utama sing miwiti utas ing konteks sing digunakake. Fungsi sing kita sebut bpf_get_current_pid_tgid() ngasilake GID thread lan PID ing nilai 64-bit siji.

Nalika outputting menyang terminal, kita saiki ora kasengsem ing thread, nanging kita kasengsem ing proses utama. Sawise mbandhingake wektu tundha sing diasilake karo ambang sing diwenehake, kita ngliwati struktur kita data menyang ruang pangguna liwat tabel acara, sawise kita mbusak entri saka p.

Ing skrip python sing bakal mbukak kode iki, kita kudu ngganti MIN_US lan FACTOR kanthi ambang wektu tundha lan unit wektu, sing bakal kita lewati argumen:

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"

Saiki kita kudu nyiapake program BPF liwat BPF makro lan ndhaptar conto:

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

Kita uga kudu nemtokake struct data_t ing naskah kita, yen ora, kita ora bakal bisa maca apa-apa:

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

Langkah pungkasan yaiku output data menyang terminal:

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

Skrip dhewe kasedhiya ing GItHub. Ayo nyoba mbukak ing platform test ing ngendi fio mlaku, nulis menyang bcache, lan nelpon monitor udevadm:

Saka High Ceph Latency menyang Kernel Patch nggunakake eBPF / BCC
Akhire! Saiki kita ndeleng manawa piranti bcache sing macet yaiku telpon sing mandheg generic_make_request() kanggo disk cache.

Dig menyang Kernel

Apa persis sing kalem sajrone transmisi panyuwunan? Kita waca sing wektu tundha ana malah sadurunge wiwitan accounting request, i.e. accounting request tartamtu kanggo output luwih saka statistik ing (/ proc / diskstats utawa iostat) durung diwiwiti. Iki bisa gampang diverifikasi kanthi mbukak iostat nalika ngasilake masalah, utawa Biolatensi skrip BCC, sing adhedhasar wiwitan lan pungkasan akuntansi panyuwunan. Ora ana utilitas kasebut bakal nuduhake masalah kanggo panjaluk menyang disk sing di-cache.

Yen kita katon ing fungsi generic_make_request(), banjur kita bakal weruh yen sadurunge akuntansi panyuwunan diwiwiti, rong fungsi liyane diarani. pisanan- generic_make_request_checks(), nindakake mriksa legitimasi panyuwunan babagan setelan disk. Kapindho - blk_queue_enter(), sing nduweni tantangan sing menarik 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));

Ing kono, kernel ngenteni antrian unfreeze. Ayo ngukur wektu tundha 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    	|                                    	|

Kayane kita wis cedhak karo solusi. Fungsi sing digunakake kanggo beku / unfreeze antrian yaiku blk_mq_freeze_queue ΠΈ blk_mq_unfreeze_queue. Lagi digunakake nalika iku perlu kanggo ngganti setelan antrian request, kang duweni potensi mbebayani kanggo panjalukan ing antrian iki. Nalika nelpon blk_mq_freeze_queue() fungsi blk_freeze_queue_start() counter wis incremented q->mq_freeze_depth. Sawise iki, kernel ngenteni antrian kosong blk_mq_freeze_queue_wait().

Wektu sing dibutuhake kanggo mbusak antrian iki padha karo latensi disk amarga kernel ngenteni kabeh operasi sing antri rampung. Sawise antrian kosong, pangowahan setelan ditrapake. Sawise iku diarani blk_mq_unfreeze_queue(), nyuda counter freeze_depth.

Saiki kita ngerti cukup kanggo mbenerake kahanan kasebut. Printah pemicu udevadm nyebabake setelan kanggo piranti pemblokiran ditrapake. Setelan iki diterangake ing aturan udev. Kita bisa nemokake setelan sing beku antrian kanthi nyoba ngganti liwat sysfs utawa kanthi ndeleng kode sumber kernel. Kita uga bisa nyoba sarana BCC jejere, sing bakal ngasilake jejak tumpukan kernel lan ruang pangguna kanggo saben telpon menyang terminal blk_freeze_queue, contone:

~# /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]

Aturan Udev arang banget diganti lan biasane kedadeyan kanthi cara sing dikontrol. Dadi, kita bisa ndeleng manawa malah ngetrapake nilai sing wis disetel nyebabake tundha wektu nransfer panjalukan saka aplikasi menyang disk. Mesthi, ngasilake acara udev nalika ora ana owah-owahan ing konfigurasi disk (Contone, piranti ora dipasang / pedhot) ora laku apik. Nanging, kita bisa nulungi kernel supaya ora nindakake pakaryan sing ora perlu lan beku antrian panyuwunan yen ora perlu. Telung cilik prasetya mbenerake kahanan.

kesimpulan

eBPF minangka alat sing fleksibel lan kuat. Ing artikel kasebut, kita ndeleng siji kasus praktis lan nuduhake bagean cilik saka apa sing bisa ditindakake. Yen sampeyan kasengsem ing ngembangaken keperluan BCC, iku worth njupuk dipikir ing tutorial resmi, sing nggambarake dhasar kanthi apik.

Ana alat debugging lan profiling liyane sing menarik adhedhasar eBPF. Salah sijine - bpftrace, sing ngijini sampeyan kanggo nulis kuat siji-liners lan program cilik ing basa awk-kaya. Liyane - ebpf_exporter, ngidini sampeyan ngumpulake metrik tingkat rendah lan resolusi dhuwur langsung menyang server prometheus sampeyan, kanthi kemampuan kanggo entuk visualisasi sing apik lan malah tandha.

Source: www.habr.com

Add a comment