O High Ceph Latency i Kernel Patch gan ddefnyddio eBPF/BCC

O High Ceph Latency i Kernel Patch gan ddefnyddio eBPF/BCC

Mae gan Linux nifer fawr o offer ar gyfer dadfygio'r cnewyllyn a'r cymwysiadau. Mae'r rhan fwyaf ohonynt yn cael effaith negyddol ar berfformiad y cais ac ni ellir eu defnyddio wrth gynhyrchu.

Ychydig flynyddoedd yn Γ΄l roedd yna mae offeryn arall wedi'i ddatblygu - eBPF. Mae'n ei gwneud hi'n bosibl olrhain y cnewyllyn a chymwysiadau defnyddwyr Γ’ gorbenion isel a heb yr angen i ailadeiladu rhaglenni a llwytho modiwlau trydydd parti i'r cnewyllyn.

Mae yna lawer o gyfleustodau cymhwysiad eisoes sy'n defnyddio eBPF, ac yn yr erthygl hon byddwn yn edrych ar sut i ysgrifennu eich cyfleustodau proffilio eich hun yn seiliedig ar y llyfrgell PythonBCC. Mae'r erthygl yn seiliedig ar ddigwyddiadau go iawn. Byddwn yn mynd o broblem i drwsio i ddangos sut y gellir defnyddio cyfleustodau presennol mewn sefyllfaoedd penodol.

Ceph Yn Araf

Mae gwesteiwr newydd wedi'i ychwanegu at glwstwr Ceph. Ar Γ΄l mudo peth o'r data iddo, gwnaethom sylwi bod cyflymder prosesu ceisiadau ysgrifennu ganddo yn llawer is nag ar weinyddion eraill.

O High Ceph Latency i Kernel Patch gan ddefnyddio eBPF/BCC
Yn wahanol i lwyfannau eraill, defnyddiodd y gwesteiwr hwn bcache a'r cnewyllyn linux 4.15 newydd. Dyma'r tro cyntaf i lu o'r cyfluniad hwn gael ei ddefnyddio yma. Ac ar y foment honno roedd yn amlwg y gallai gwraidd y broblem fod yn unrhyw beth yn ddamcaniaethol.

Ymchwilio i'r Gwesteiwr

Gadewch i ni ddechrau trwy edrych ar yr hyn sy'n digwydd y tu mewn i'r broses ceph-osd. Ar gyfer hyn byddwn yn defnyddio perff ΠΈ fflamsgop (mwy y gallwch chi ddarllen amdano yma):

O High Ceph Latency i Kernel Patch gan ddefnyddio eBPF/BCC
Mae'r llun yn dweud wrthym fod y swyddogaeth fdatasync() treulio llawer o amser yn anfon cais i swyddogaethau cais_gwneud_generig(). Mae hyn yn golygu mai rhywle y tu allan i'r ellyll osd ei hun sydd fwyaf tebygol o achosi'r problemau. Gall hyn fod naill ai'r cnewyllyn neu ddisgiau. Roedd allbwn yr iostat yn dangos llawer o hwyrni wrth brosesu ceisiadau gan ddisgiau bcache.

Wrth wirio'r gwesteiwr, canfuom fod yr daemon systemd-udevd yn defnyddio llawer iawn o amser CPU - tua 20% ar sawl craidd. Mae hwn yn ymddygiad rhyfedd, felly mae angen i chi ddarganfod pam. Gan fod Systemd-udevd yn gweithio gyda uevents, penderfynasom edrych arnynt drwodd monitor udevadm. Mae'n ymddangos bod nifer fawr o ddigwyddiadau newid wedi'u cynhyrchu ar gyfer pob dyfais bloc yn y system. Mae hyn yn eithaf anarferol, felly bydd yn rhaid i ni edrych ar yr hyn sy'n cynhyrchu'r holl ddigwyddiadau hyn.

Defnyddio Pecyn Cymorth BCC

Fel yr ydym eisoes wedi darganfod, mae'r cnewyllyn (a'r ellyll ceph yn yr alwad system) yn treulio llawer o amser yn cais_gwneud_generig(). Gadewch i ni geisio mesur cyflymder y swyddogaeth hon. YN BCC Mae yna ddefnyddioldeb gwych eisoes - funclatency. Byddwn yn olrhain yr ellyll yn Γ΄l ei PID gydag egwyl o 1 eiliad rhwng allbynnau ac allbwn y canlyniad mewn milieiliadau.

O High Ceph Latency i Kernel Patch gan ddefnyddio eBPF/BCC
Mae'r nodwedd hon fel arfer yn gweithio'n gyflym. Y cyfan y mae'n ei wneud yw trosglwyddo'r cais i'r ciw gyrrwr dyfais.

Bcache yn ddyfais gymhleth sydd mewn gwirionedd yn cynnwys tair disg:

  • dyfais gefn (disg wedi'i storio), yn yr achos hwn mae'n HDD araf;
  • dyfais caching (disg caching), dyma un rhaniad o'r ddyfais NVMe;
  • y ddyfais rithwir bcache y mae'r rhaglen yn rhedeg ag ef.

Gwyddom fod trosglwyddo ceisiadau yn araf, ond ar gyfer pa rai o'r dyfeisiau hyn? Byddwn yn delio Γ’ hyn ychydig yn ddiweddarach.

Gwyddom yn awr fod digwyddiadau yn debygol o achosi problemau. Nid yw dod o hyd i beth yn union sy'n achosi eu cenhedlaeth mor hawdd. Gadewch i ni dybio bod hwn yn rhyw fath o feddalwedd sy'n cael ei lansio o bryd i'w gilydd. Gawn ni weld pa fath o feddalwedd sy'n rhedeg ar y system gan ddefnyddio sgript execsnoop o'r un Pecyn cyfleustodau BCC. Gadewch i ni ei redeg ac anfon yr allbwn i ffeil.

Er enghraifft fel hyn:

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

Ni fyddwn yn dangos allbwn llawn execsnoop yma, ond roedd un llinell o ddiddordeb i ni yn edrych fel hyn:

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

Y drydedd golofn yw PPID (pID rhiant) y broses. Trodd y broses gyda PID 5802 yn un o edafedd ein system fonitro. Wrth wirio cyfluniad y system fonitro, canfuwyd paramedrau gwallus. Cymerwyd tymheredd yr addasydd HBA bob 30 eiliad, sy'n llawer amlach na'r angen. Ar Γ΄l newid yr egwyl siec i un hirach, canfuom nad oedd yr hwyrni prosesu ceisiadau ar y gwesteiwr hwn bellach yn sefyll allan o'i gymharu Γ’ gwesteiwyr eraill.

Ond mae'n dal yn aneglur pam roedd y ddyfais bcache mor araf. Fe wnaethom baratoi platfform prawf gyda chyfluniad union yr un fath a cheisio atgynhyrchu'r broblem trwy redeg fio on bcache, gan redeg sbardun udevadm o bryd i'w gilydd i gynhyrchu uevents.

Ysgrifennu Offer yn Seiliedig ar BCC

Gadewch i ni geisio ysgrifennu cyfleustodau syml i olrhain ac arddangos y galwadau arafaf cais_gwneud_generig(). Mae gennym ddiddordeb hefyd yn enw'r gyriant y galwyd y swyddogaeth hon ar ei gyfer.

Mae'r cynllun yn syml:

  • Cofrestrwch kprobe ar cais_gwneud_generig():
    • Rydym yn cadw enw'r ddisg yn y cof, sy'n hygyrch trwy'r ddadl swyddogaeth;
    • Rydym yn arbed y stamp amser.

  • Cofrestrwch kretprobe am ddychwelyd o cais_gwneud_generig():
    • Cawn y stamp amser presennol;
    • Edrychwn am y stamp amser a arbedwyd a'i gymharu Γ’'r un gyfredol;
    • Os yw'r canlyniad yn fwy na'r un penodedig, yna rydym yn dod o hyd i enw'r ddisg sydd wedi'i gadw a'i arddangos ar y derfynell.

Kprobes ΠΈ kretprobes defnyddio mecanwaith torbwynt i newid cod swyddogaeth ar y hedfan. Gallwch ddarllen dogfennaeth ΠΈ da erthygl ar y pwnc hwn. Os edrychwch ar y cod o wahanol gyfleustodau yn BCC, yna gallwch weld bod ganddynt strwythur union yr un fath. Felly yn yr erthygl hon byddwn yn hepgor dosrannu dadleuon sgriptiau ac yn symud ymlaen i'r rhaglen BPF ei hun.

Mae'r testun eBPF y tu mewn i'r sgript python yn edrych fel hyn:

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

I gyfnewid data rhwng swyddogaethau, mae rhaglenni eBPF yn defnyddio byrddau stwnsh. Byddwn yn gwneud yr un peth. Byddwn yn defnyddio'r broses PID fel yr allwedd, ac yn diffinio'r strwythur fel y gwerth:

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

Yma rydym yn cofrestru tabl hash o'r enw p, gyda math allweddol u64 a gwerth o fath strwythuro data_t. Bydd y tabl ar gael yng nghyd-destun ein rhaglen BPF. Mae'r macro BPF_PERF_OUTPUT yn cofrestru tabl arall o'r enw digwyddiadau, a ddefnyddir ar gyfer trosglwyddo data i mewn i ofod defnyddwyr.

Wrth fesur oedi rhwng galw swyddogaeth a dychwelyd ohoni, neu rhwng galwadau i wahanol swyddogaethau, mae angen i chi ystyried bod yn rhaid i'r data a dderbynnir berthyn i'r un cyd-destun. Mewn geiriau eraill, mae angen i chi gofio am lansiad cyfochrog posibl o swyddogaethau. Mae gennym y gallu i fesur yr hwyrni rhwng galw swyddogaeth yng nghyd-destun un broses a dychwelyd o’r swyddogaeth honno yng nghyd-destun proses arall, ond mae hyn yn debygol o fod yn ddiwerth. Enghraifft dda yma fyddai cyfleustodau biolatency, lle mae'r allwedd tabl hash wedi'i osod i bwyntydd i cais am strwythur, sy'n adlewyrchu un cais disg.

Nesaf, mae angen i ni ysgrifennu'r cod a fydd yn rhedeg pan fydd y swyddogaeth dan sylw yn cael ei alw:

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

Yma bydd dadl gyntaf y ffwythiant a elwir yn cael ei disodli fel yr ail ddadl cais_gwneud_generig(). Ar Γ΄l hyn, rydym yn cael PID y broses yr ydym yn gweithio yn ei chyd-destun, a'r stamp amser presennol mewn nanoseconds. Ysgrifennwn y cyfan i lawr mewn detholiad ffres strwythuro data_t data. Rydyn ni'n cael enw'r ddisg o'r strwythur bio, a drosglwyddir wrth alw cais_gwneud_generig(), a'i gadw yn yr un strwythur data. Y cam olaf yw ychwanegu cofnod at y tabl hash a grybwyllwyd yn gynharach.

Bydd y swyddogaeth ganlynol yn cael ei galw wrth ddychwelyd o cais_gwneud_generig():

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

Mae'r swyddogaeth hon yn debyg i'r un blaenorol: rydym yn darganfod PID y broses a'r stamp amser, ond nid ydym yn dyrannu cof ar gyfer y strwythur data newydd. Yn lle hynny, rydym yn chwilio'r tabl hash am strwythur sydd eisoes yn bodoli gan ddefnyddio'r allwedd == PID cyfredol. Os canfyddir y strwythur, yna byddwn yn darganfod enw'r broses redeg ac yn ei ychwanegu ato.

Mae angen y shifft deuaidd rydyn ni'n ei ddefnyddio yma i gael yr edefyn GID. y rhai. PID o'r brif broses a ddechreuodd yr edefyn yn y cyd-destun yr ydym yn gweithio arno. Y swyddogaeth rydyn ni'n ei galw bpf_get_current_pid_tgid() yn dychwelyd GID yr edefyn a'i PID mewn un gwerth 64-did.

Wrth allbynnu i'r derfynell, nid oes gennym ddiddordeb yn yr edefyn ar hyn o bryd, ond mae gennym ddiddordeb yn y brif broses. Ar Γ΄l cymharu'r oedi canlyniadol Γ’ throthwy penodol, rydym yn pasio ein strwythur data i mewn i ofod defnyddiwr trwy fwrdd digwyddiadau, ac ar Γ΄l hynny rydym yn dileu'r cofnod o p.

Yn y sgript python a fydd yn llwytho'r cod hwn, mae angen i ni ddisodli MIN_US a FACTOR gyda'r trothwyon oedi a'r unedau amser, y byddwn yn mynd trwy'r dadleuon:

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"

Nawr mae angen i ni baratoi'r rhaglen BPF trwy macro BPF a chofrestru samplau:

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

Bydd yn rhaid inni benderfynu hefyd strwythuro data_t yn ein sgript, fel arall ni fyddwn yn gallu darllen unrhyw beth:

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

Y cam olaf yw allbynnu data i'r derfynell:

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

Mae'r sgript ei hun ar gael yn GitHub. Gadewch i ni geisio ei redeg ar lwyfan prawf lle mae fio yn rhedeg, ysgrifennu at bcache, a galw monitor udevadm:

O High Ceph Latency i Kernel Patch gan ddefnyddio eBPF/BCC
O'r diwedd! Nawr rydym yn gweld bod yr hyn sy'n edrych fel dyfais bcache arafu mewn gwirionedd yn alwad stopio cais_gwneud_generig() ar gyfer disg wedi'i storio.

Cloddio i mewn i'r Cnewyllyn

Beth yn union sy'n arafu wrth drosglwyddo cais? Gwelwn fod yr oedi yn digwydd hyd yn oed cyn dechrau cyfrifo ceisiadau, h.y. nid yw cyfrifo cais penodol am allbwn pellach o ystadegau arno (/proc/diskstats neu iostat) wedi dechrau eto. Gellir gwirio hyn yn hawdd trwy redeg iostat wrth atgynhyrchu'r broblem, neu Biolatency sgript BCC, sy'n seiliedig ar ddechrau a diwedd cyfrifo cais. Ni fydd unrhyw un o'r cyfleustodau hyn yn dangos problemau ar gyfer ceisiadau i'r ddisg sydd wedi'i storio.

Os edrychwn ar y swyddogaeth cais_gwneud_generig(), yna byddwn yn gweld, cyn i'r cais ddechrau cyfrifo, gelwir dwy swyddogaeth arall. Yn gyntaf - gwiriadau_gwneud_cais_generig(), yn perfformio gwiriadau ar gyfreithlondeb y cais o ran gosodiadau'r ddisg. Ail - blk_ciw_enter(), sydd Γ’ her ddiddorol aros_digwyddiad_ torri ar draws ():

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

Ynddo, mae'r cnewyllyn yn aros i'r ciw ddadrewi. Gadewch i ni fesur yr oedi blk_ciw_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    	|                                    	|

Mae'n edrych fel ein bod ni'n agos at ateb. Y swyddogaethau a ddefnyddir i rewi/dadrewi ciw yw blk_mq_rhewi_ciw ΠΈ blk_mq_unfreeze_ciw. Fe'u defnyddir pan fo angen newid gosodiadau'r ciw cais, a allai fod yn beryglus ar gyfer ceisiadau yn y ciw hwn. Wrth alw blk_mq_rhewi_ciw() swyddogaeth blk_rhewi_ciw_cychwyn() cynyddir y cownter q-> mq_rhewi_dyfnder. Ar Γ΄l hyn, mae'r cnewyllyn yn aros i'r ciw wagio i mewn blk_mq_rewi_ciw_aros().

Mae'r amser mae'n ei gymryd i glirio'r ciw hwn yn cyfateb i hwyrni disg gan fod y cnewyllyn yn aros i'r holl weithrediadau ciwio gael eu cwblhau. Unwaith y bydd y ciw yn wag, caiff y newidiadau gosodiadau eu cymhwyso. Wedi hynny fe'i gelwir blk_mq_dadrewi_ciw(), gostwng y cownter rhewi_dyfnder.

Nawr rydyn ni'n gwybod digon i unioni'r sefyllfa. Mae'r gorchymyn sbarduno udevadm yn achosi'r gosodiadau ar gyfer y ddyfais bloc i'w gymhwyso. Disgrifir y gosodiadau hyn yn y rheolau udev. Gallwn ddarganfod pa osodiadau sy'n rhewi'r ciw trwy geisio eu newid trwy sysfs neu trwy edrych ar y cod ffynhonnell cnewyllyn. Gallwn hefyd roi cynnig ar y cyfleustodau BCC olrhain, a fydd yn allbynnu olion pentwr cnewyllyn a gofod defnyddiwr ar gyfer pob galwad i'r derfynell blk_rhewi_ciw, er enghraifft:

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

Anaml iawn y mae rheolau Udev yn newid ac fel arfer mae hyn yn digwydd mewn modd rheoledig. Felly gwelwn fod hyd yn oed cymhwyso'r gwerthoedd a osodwyd eisoes yn achosi cynnydd sydyn yn yr oedi wrth drosglwyddo'r cais o'r cais i'r ddisg. Wrth gwrs, nid yw cynhyrchu digwyddiadau udev pan nad oes unrhyw newidiadau yng nghyfluniad y ddisg (er enghraifft, nid yw'r ddyfais wedi'i gosod / datgysylltu) yn arfer da. Fodd bynnag, gallwn helpu'r cnewyllyn i beidio Γ’ gwneud gwaith diangen a rhewi'r ciw cais os nad oes angen. Tri bach ymrwymo cywiro'r sefyllfa.

Casgliad

Mae eBPF yn offeryn hyblyg a phwerus iawn. Yn yr erthygl fe wnaethom edrych ar un achos ymarferol a dangos rhan fach o'r hyn y gellir ei wneud. Os oes gennych ddiddordeb mewn datblygu cyfleustodau BCC, mae'n werth edrych arno tiwtorial swyddogol, sy'n disgrifio'r pethau sylfaenol yn dda.

Mae yna offer dadfygio a phroffilio diddorol eraill yn seiliedig ar eBPF. Un o nhw - bpftrace, sy'n eich galluogi i ysgrifennu un-leiners pwerus a rhaglenni bach yn yr iaith awk-debyg. Arall - ebpf_allforiwr, yn eich galluogi i gasglu metrigau lefel isel, cydraniad uchel yn uniongyrchol i'ch gweinydd prometheus, gyda'r gallu i gael delweddiadau hardd a hyd yn oed rhybuddion yn ddiweddarach.

Ffynhonnell: hab.com

Ychwanegu sylw