Ji Derengiya Bilind a Ceph bigire heya Kernel Patch bi karanîna eBPF / BCC

Ji Derengiya Bilind a Ceph bigire heya Kernel Patch bi karanîna eBPF / BCC

Linux ji bo debuggkirina kernel û sepanan gelek amûr hene. Piraniya wan bandorek neyînî li ser performansa serîlêdanê dikin û nekarin di hilberînê de werin bikar anîn.

Çend sal berê hebû amûreke din hatiye pêşxistin - eBPF. Ew gengaz dike ku meriv kernel û serîlêdanên bikarhêner bi sermaya kêm û bêyî hewcedariya ji nû ve avakirina bernameyan bişopîne û modulên partiya sêyemîn di kernelê de bar bike.

Jixwe gelek karûbarên serîlêdanê hene ku eBPF bikar tînin, û di vê gotarê de em ê binihêrin ka meriv çawa li ser bingeha pirtûkxaneyê karûbarê profîla xwe binivîsîne. PythonBCC. Gotar li ser bûyerên rastîn e. Em ê ji pirsgirêkê ber bi rastkirinê ve biçin da ku destnîşan bikin ka karûbarên heyî çawa dikarin di rewşên taybetî de werin bikar anîn.

Ceph Hêdî ye

Mêvandarek nû li koma Ceph hate zêdekirin. Piştî koçkirina hin daneyan li ser wê, me dît ku leza hilanîna daxwazên nivîsandinê ji hêla wê ve ji serverên din pir kêmtir bû.

Ji Derengiya Bilind a Ceph bigire heya Kernel Patch bi karanîna eBPF / BCC
Berevajî platformên din, vê mêvandarê bcache û kernelê nû linux 4.15 bikar anî. Ev cara yekem bû ku mêvandarek vê veavakirinê li vir hate bikar anîn. Û di wê gavê de diyar bû ku koka pirsgirêkê bi teorîk dikare her tişt be.

Lêkolîna Host

Ka em bi nihêrîna ka di hundurê pêvajoya ceph-osd de çi diqewime dest pê bikin. Ji bo vê em ê bikar bînin lhevderketî и flamescope (bêtir li ser ku hûn dikarin bixwînin vir):

Ji Derengiya Bilind a Ceph bigire heya Kernel Patch bi karanîna eBPF / BCC
Wêne ji me re dibêje ku fonksiyonê fdatasync() gelek dem derbas kir ku daxwazek ji fonksiyonan re bişîne generic_make_request(). Ev tê vê wateyê ku bi îhtîmalek mezin sedema pirsgirêkan li derûdora osd daemon bixwe ye. Ev dikare bibe kernel an dîsk. Derketina iostat di pêvajoyên daxwazên ji hêla dîskên bcache de derengiyek bilind nîşan da.

Dema ku mêvandar kontrol kirin, me dît ku daemon systemd-udevd gelek wextê CPU-ê dixwe - li ser çend naverokan% 20. Ev tevgerek xerîb e, ji ber vê yekê hûn hewce ne ku hûn bizanin ka çima. Ji ber ku Systemd-udevd bi uevents re dixebite, me biryar da ku em li wan binihêrin çavdêriya udevadm. Derket holê ku ji bo her amûrek blokê di pergalê de hejmareke mezin ji bûyerên guherînê hatine çêkirin. Ev pir ne asayî ye, ji ber vê yekê em ê neçar bin ku li van bûyeran çi diafirîne binihêrin.

Bikaranîna BCC Toolkit

Wekî ku me berê jî fêr kir, kernel (û ceph daemon di banga pergalê de) gelek wext tê de derbas dike generic_make_request(). Ka em hewl bidin ku leza vê fonksiyonê bipîvin. LI BCC Jixwe amûrek ecêb heye - funclatency. Em ê daemonê bi PID-a wê bi navberek 1 çirkeyê ya di navbera derkan de bişopînin û encamê di milî çirkeyan de derxînin.

Ji Derengiya Bilind a Ceph bigire heya Kernel Patch bi karanîna eBPF / BCC
Ev taybetmendî bi gelemperî zû dixebite. Tiştê ku dike ev e ku daxwazê ​​ji rêza ajokera cîhazê re derbas bike.

Bcache amûrek tevlihev e ku bi rastî ji sê dîskan pêk tê:

  • cîhaza piştgiriyê (dîska cached), di vê rewşê de ew HDD hêdî ye;
  • cîhaza caching (dîska caching), li vir ev yek dabeşkirina cîhaza NVMe ye;
  • cîhaza virtual bcache ya ku serîlêdan pê dimeşe.

Em dizanin ku veguheztina daxwazê ​​hêdî ye, lê ji bo kîjan ji van amûran? Em ê hinekî paşê bi vê yekê re mijûl bibin.

Naha em dizanin ku bûyer dibe sedema pirsgirêkan. Dîtina ku tam dibe sedema nifşa wan ne ew çend hêsan e. Ka em bifikirin ku ev celebek nermalava ku bi periyodîk tê destpêkirin e. Ka em bibînin ka çi celeb nermalava li ser pergalê bi karanîna skrîptê dimeşîne execsnoop ji heman Kîta karûbarê BCC. Ka em wê bimeşînin û encam bişînin pelek.

Mînak bi vî rengî:

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

Em ê li vir hilberîna tevahî ya execsnoop nîşan nedin, lê yek rêzek balkêş ji me re wiha xuya bû:

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

Stûna sêyemîn PPID (PID dêûbav) ya pêvajoyê ye. Pêvajoya bi PID 5802 ve derket ku yek ji mijarên pergala çavdêriya me ye. Dema ku veavakirina pergala çavdêriyê kontrol kirin, pîvanên xelet hatin dîtin. Germahiya adapterê HBA her 30 saniyeyan carekê hate girtin, ku pir caran ji hewcedariyê pirtir e. Piştî guheztina navbera kontrolê ya dirêjtir, me dît ku derengiya pêvajoya daxwaznameyê li ser vê mêvandarê nema li gorî mêvandarên din xuya dike.

Lê hîn jî ne diyar e çima cîhaza bcache ew qas hêdî bû. Me platformek ceribandinê ya bi veavakirinek wekhev amade kir û hewl da ku pirsgirêkê bi xebitandina fio li ser bcache-ê, bi periyodîk vekêşana udevadm-ê bixebitînin da ku uevents biafirînin.

Nivîsandina Amûrên Bingeha BCC

Ka em hewl bidin ku amûrek hêsan binivîsin da ku bangên herî hêdî bişopînin û nîşan bidin generic_make_request(). Di heman demê de em bi navê ajokera ku ji bo vê fonksiyonê hatî gazî kirin jî eleqedar dibin.

Plana hêsan e:

  • Fêhrist kprobe li ser generic_make_request():
    • Em navê dîskê di bîranînê de hilînin, ku bi argumana fonksiyonê ve tê gihîştin;
    • Em demjimêrê xilas dikin.

  • Fêhrist kretprobe ji bo vegerê ji generic_make_request():
    • Em demjimêra heyî distînin;
    • Em li zemaneya tomarkirî digerin û wê bi ya niha re berhev dikin;
    • Ger encam ji ya diyarkirî mezintir be, wê hingê em navê dîska tomarkirî dibînin û li ser termînalê nîşan didin.

Kprobes и kretprobes mekanîzmayek xala veqetînê bikar bînin da ku koda fonksiyonê li ser firînê biguhezînin. Hûn dikarin bixwînin belgekirin и baş gotara li ser vê mijarê. Ger hûn li koda karûbarên cûrbecûr binêrin BCC, wê hingê hûn dikarin bibînin ku ew xwediyê avahiyek wekhev in. Ji ber vê yekê di vê gotarê de em ê dev ji argumanên skrîptê yên parskirinê berdin û biçin bernameya BPF bixwe.

Nivîsara eBPF di hundurê nivîsara python de wiha xuya dike:

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

Ji bo danûstandina daneyan di navbera fonksiyonan de, bernameyên eBPF bikar tînin maseyên hash. Em ê jî heman tiştî bikin. Em ê pêvajoya PID-ê wekî mifteyê bikar bînin, û strukturê wekî nirx diyar bikin:

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

Li vir em tabloyek hash a bi navê tomar dikin p, bi cureyê key u64 û nirxek celeb struct data_t. Tablo dê di çarçoveya bernameya meya BPF de peyda bibe. BPF_PERF_OUTPUT makro tabloyek din a ku jê re tê gotin tomar dike bûyerên, ku ji bo tê bikaranîn veguheztina daneyan nav cîhê bikarhêner.

Dema ku dereng di navbera bangkirina fonksiyonek û vegerandina jê, an jî di navbera bangên fonksiyonên cihêreng de pîvandin, hûn hewce ne ku zanibin ku daneyên wergirtî divê di heman çarçoveyê de bin. Bi gotinek din, hûn hewce ne ku di derbarê destpêkirina paralel a gengaz a fonksiyonan de bîr bînin. Hêza me heye ku em derengiya di navbera bangkirina fonksiyonek di çarçoveyek pêvajoyek de û vegerandina ji wê fonksiyonê di çarçoweya pêvajoyek din de bipîvin, lê ev îhtîmal bêkêr e. Li vir mînakek baş dê bibe bikaranîna biolatency, cihê ku mifteya tabloya hash li ser nîşanderek tê danîn daxwaza struct, ku yek daxwazek dîskê nîşan dide.

Dûv re, pêdivî ye ku em kodê binivîsin ku dê dema ku fonksiyona di bin lêkolînê de tê gotin were xebitandin:

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

Li vir argûmana yekem a fonksiyona tê gotin dê wekî argumana duyemîn were veguheztin generic_make_request(). Piştî vê yekê, em PID-a pêvajoyê ya ku di çarçoweya ku em lê dixebitin, û dema niha di nanoseconds de digirin. Em her tiştî di nav bijarteyek nû de dinivîsin struct data_t data. Em navê dîskê ji strukturê digirin bio, ya ku di dema bangê de derbas dibe generic_make_request(), û wê di heman avahiyê de hilînin jimare. Pêngava paşîn ev e ku meriv têketinek li tabloya hashê ya ku berê hatî behs kirin zêde bike.

Fonksiyona jêrîn dê di vegerê de were gazî kirin 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);
    }
}

Ev fonksiyon mîna ya berê ye: em PID-a pêvajoyê û nîşana demê fêr dibin, lê ji bo avahiya daneya nû bîranîn venaqetînin. Di şûna wê de, em bi karanîna mifteya == PID-a heyî li tabloya haş li avahiyek berê ya heyî digerin. Ger avahî were dîtin, wê hingê em navê pêvajoya xebitandinê fêr dibin û lê zêde dikin.

Veguheztina binaryê ku em li vir bikar tînin, ji bo bidestxistina GID-a mijarê hewce ye. ewan. PID ya pêvajoya sereke ya ku di çarçoveya ku em dixebitin de dest pê kir. Fonksiyona ku em jê re dibêjin bpf_get_current_pid_tgid() hem GID-ya têxê û hem jî PID-a wê di nirxek yek 64-bit de vedigerîne.

Dema ku em li termînalê derdixin, em niha eleqedar nabin mijarê, lê em bi pêvajoya sereke re eleqedar in. Piştî ku derengiya encam bi bendek diyarkirî re berhev bikin, em avahiya xwe derbas dikin jimare nav cîhê bikarhêner bi riya tabloyê bûyerên, piştî ku em têketinê jêbirin p.

Di skrîpta python a ku dê vê kodê bar bike, pêdivî ye ku em MIN_US û FACTOR bi tixûbên derengmayînê û yekîneyên demê re biguhezînin, ku em ê di nav argumanan re derbas bikin:

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"

Naha divê em bernameya BPF bi rê ve amade bikin BPF makro û nimûne tomar bikin:

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

Em ê jî diyar bikin struct data_t di nivîsara me de, wekî din em ê nikaribin tiştek bixwînin:

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êngava paşîn ev e ku meriv daneya li termînalê derxe:

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

Skrîpt bi xwe li ser heye GItHub. Ka em hewl bidin ku wê li ser platformek ceribandinê ya ku fio lê dimeşîne, li bcache-ê dinivîse, bimeşîne û jê re çavdêriya udevadm bang bike:

Ji Derengiya Bilind a Ceph bigire heya Kernel Patch bi karanîna eBPF / BCC
Paşan! Naha em dibînin ku ya ku dişibihe amûrek bcache ya rawestandî bi rastî bangek sekinî ye generic_make_request() ji bo dîskek cached.

Di Kernelê de bikolin

Di dema veguheztina daxwazê ​​de bi rastî çi hêdî dibe? Em dibînin ku dereng berî destpêkirina hesabkirina daxwazê ​​jî çêdibe, yanî. hesabkirina daxwazek taybetî ya ji bo hilberîna bêtir statîstîkên li ser wê (/proc/diskstats an iostat) hîn dest pê nekiriye. Ev dikare bi hêsanî verastkirina iostat dema ku pirsgirêk ji nû ve hilberandin, an BCC script biolatency, ku li ser destpêk û dawiya hesabê daxwazê ​​ye. Yek ji van amûran dê pirsgirêkên ji bo daxwaznameyên dîskê cached nîşan bide.

Ger em li fonksiyonê binêrin generic_make_request(), wê hingê em ê bibînin ku berî ku daxwaz dest bi hesabkirinê bike, du fonksiyonên din têne gazî kirin. Yekem - generic_make_request_checks(), di derheqê mîhengên dîskê de li ser rewabûna daxwazê ​​kontrol dike. Duyem - blk_queue_enter(), ku dijwariyek balkêş heye 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));

Di wê de, kernel li bendê ye ku rêz bê cemidandin. Ka em derengiyê bipîvin 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    	|                                    	|

Wusa dixuye ku em nêzî çareseriyê ne. Fonksiyonên ku ji bo cemidandin/necemidandina dorê têne bikar anîn ev in blk_mq_freeze_queue и blk_mq_unfreeze_queue. Ew têne bikar anîn dema ku pêdivî ye ku mîhengên rêza daxwaznameyê biguhezînin, ku ji bo daxwazên di vê dorê de potansiyel xeternak in. Dema ku bang dikin blk_mq_freeze_queue() karî blk_freeze_queue_start() jimarvan zêde dibe q-> mq_freeze_depth. Piştî vê yekê, kernel li benda valakirina dorê ye blk_mq_freeze_queue_wait().

Wextê ku ji bo paqijkirina vê rêzê digire bi derengiya dîskê re wekhev e ji ber ku kernel li bendê ye ku hemî operasyonên rêzê biqede. Dema ku rêz vala bibe, guheztinên mîhengan têne sepandin. Piştî ku tê gotin blk_mq_unfreeze_queue(), kêmkirina hejmarê cemidî_kûrahî.

Niha em têra xwe dizanin ku rewşê rast bikin. Fermana tetikê ya udevadm dibe sedem ku mîhengên ji bo cîhaza blokê were sepandin. Van mîhengan di qaîdeyên udev de têne diyar kirin. Em dikarin bibînin ka kîjan mîhengan rêzê dicemidîne bi hewldana guhertina wan bi navgîniya sysfs an jî bi dîtina koda çavkaniya kernelê. Em dikarin karûbarê BCC jî biceribînin kişandin, ku dê ji bo her banga termînalê şopên kernel û cîhê bikarhêner derxe blk_freeze_queue, wek nimûne:

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

Qaîdeyên Udev pir kêm kêm diguhezin û bi gelemperî ev bi rengek kontrolkirî diqewime. Ji ber vê yekê em dibînin ku tewra sepandina nirxên jixwe hatî destnîşankirî jî dibe sedema derengiya veguhestina daxwazê ​​ji serîlêdanê li dîskê. Bê guman, çêkirina bûyerên udev dema ku di veavakirina dîskê de guheztin çênebin (mînak, cîhaz ne siwar kirin/veqetandin) ne pratîkek baş e. Lêbelê, em dikarin alîkariya kernel bikin ku karên nehewce neke û rêza daxwaznameyê bicemidîne ger ne hewce be. biçûk bikaranîn rewşê rast bike.

Xelasî

eBPF amûrek pir maqûl û bi hêz e. Di gotarê de me li dozek pratîkî nihêrî û beşek piçûk a ku dikare were kirin destnîşan kir. Ger hûn bala xwe didin pêşdebirina karûbarên BCC, hêja ye ku meriv lê binêre tutorial fermî, ku bingehîn baş diyar dike.

Li ser bingeha eBPF-ê amûrên debugkirin û profîlên din ên balkêş hene. Yek ji wan - bpftrace, ku destûrê dide te ku hûn bi zimanê awk-wek yek rêzikên hêzdar û bernameyên piçûk binivîsin. Din - ebpf_exporter, destûrê dide te ku meriv metrîkên nizm, rezîliya bilind rasterast di servera xweya prometheus de berhev bike, bi jêhatîbûna ku hûn paşê dîmenên xweşik û hetta hişyariyan bistînin.

Source: www.habr.com

Add a comment