Linux өзөктү жана тиркемелерди оңдоо үчүн көптөгөн куралдарга ээ. Алардын көбү тиркеменин иштешине терс таасирин тийгизет жана өндүрүштө колдонулушу мүмкүн эмес.
Бир-эки жыл мурун болгон
eBPF колдонгон көптөгөн тиркеме утилиталары бар жана бул макалада биз китепкананын негизинде өзүңүздүн профилиңизди кантип жазууну карап чыгабыз
Ceph жай
Ceph кластерине жаңы хост кошулду. Айрым маалыматтарды ага көчүргөндөн кийин, биз анын жазуу өтүнүчтөрүн иштетүү ылдамдыгы башка серверлерге караганда бир топ төмөн экенин байкадык.
Башка платформалардан айырмаланып, бул хост bcache жана жаңы Linux 4.15 ядросун колдонгон. Бул жерде бул конфигурациянын хосту биринчи жолу колдонулду. Жана ошол учурда маселенин түпкү тамыры теориялык жактан ар нерсе болушу мүмкүн экени айкын болду.
Хостту иликтөө
Келгиле, ceph-osd процессинин ичинде эмне болуп жатканын карап баштайлы. Бул үчүн биз колдонобуз
Сүрөт бул функцияны айтып берет fdatasync() функцияларга суроо-талап жөнөтүү үчүн көп убакыт коротту generic_make_request(). Бул, кыязы, көйгөйлөрдүн себеби osd демонунун өзүнөн башка жерде экенин билдирет. Бул ядро же дисктер болушу мүмкүн. Иостаттын чыгарылышы bcache дисктери тарабынан суроо-талаптарды иштетүүдө жогорку кечиктирүүнү көрсөттү.
Хостту текшерүүдө биз systemd-udevd демону процессордун көп убактысын - бир нече өзөктө 20% га жакынын сарптаарын таптык. Бул кызыктай жүрүм-турум, андыктан эмне үчүн экенин билишиңиз керек. Systemd-udevd окуялар менен иштегендиктен, биз аларды карап чыгууну чечтик udevadm монитор. Бул системанын ар бир блок түзмөк үчүн өзгөртүү окуялардын көп сандагы түзүлгөн экен. Бул адаттан тыш көрүнүш, ошондуктан биз бул окуялардын бардыгын эмне жаратып жатканын карап чыгышыбыз керек.
BCC Toolkit колдонуу
Биз буга чейин билгендей, ядро (жана системалык чалуудагы ceph демону) көп убакытты generic_make_request(). Келгиле, бул функциянын ылдамдыгын өлчөөгө аракет кылалы. IN
Бул өзгөчөлүк, адатта, тез иштейт. Мунун баары өтүнүчтү түзмөк драйверинин кезегине өткөрүп берүү.
Bcache чындыгында үч дисктен турган татаал түзүлүш болуп саналат:
- колдоо түзмөк (кэштелген диск), бул учурда ал жай HDD болуп саналат;
- кэштөөчү түзүлүш (кэштөө диски), бул жерде NVMe түзүлүшүнүн бир бөлүгү;
- колдонмо иштеген bcache виртуалдык аспабы.
Сурамдын берилиши жай экенин билебиз, бирок бул түзмөктөрдүн кайсынысы үчүн? Бул тууралуу бир аздан кийин чечебиз.
Биз азыр окуялар көйгөй жаратышы мүмкүн экенин билебиз. Алардын тукумуна эмне себеп болгонун табуу анчалык деле оңой эмес. Келгиле, бул мезгил-мезгили менен ишке киргизилген программалык камсыздоонун кандайдыр бир түрү деп коёлу. Келгиле, скрипт аркылуу системада кандай программалык камсыздоо иштей турганын карап көрөлү execsnoop ошол эле
Мисалы, бул сыяктуу:
/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 түзмөгү эмне үчүн мынчалык жай болгону азырынча белгисиз. Биз окшош конфигурациядагы сыноо платформасын даярдадык жана bcache'де fio иштетип, окуяларды түзүү үчүн мезгил-мезгили менен udevadm триггерди иштетип, көйгөйдү кайра чыгарууга аракет кылдык.
BCC негизиндеги куралдарды жазуу
Эң жай чалууларды көзөмөлдөө жана көрсөтүү үчүн жөнөкөй утилитаны жазууга аракет кылалы generic_make_request(). Бизди бул функция чакырылган дисктин аты да кызыктырат.
План жөнөкөй:
- Каттоо kprobe боюнча generic_make_request():
- Биз дисктин атын эстутумга сактайбыз, функция аргументи аркылуу жеткиликтүү;
- Биз убакыт белгисин сактайбыз.
- Каттоо kretprobe кайтуу үчүн generic_make_request():
- Биз учурдагы убакыт белгисин алабыз;
- Биз сакталган убакыт белгисин издеп, аны учурдагы менен салыштырабыз;
- Эгерде натыйжа көрсөтүлгөндөн чоңураак болсо, анда биз сакталган дисктин атын таап, аны терминалга көрсөтөбүз.
Kprobes и kretprobes Функциянын кодун тез арада өзгөртүү үчүн токтотуу чекитинин механизмин колдонуңуз. Сиз окуй аласыз
Python скриптинин ичиндеги eBPF тексти мындай көрүнөт:
bpf_text = “”” # Here will be the bpf program code “””
Функциялардын ортосунда маалымат алмашуу үчүн eBPF программалары колдонулат
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);
Бул жерде биз хэш таблицасын каттайбыз 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():
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 жана убакыт белгисин табабыз, бирок жаңы маалымат структурасы үчүн эстутумду бөлбөйбүз. Анын ордуна, == учурдагы PID ачкычын колдонуп, хэш таблицасында мурунтан эле бар структураны издейбиз. Эгерде структура табылса, анда биз иштеп жаткан процесстин атын таап, ага кошобуз.
Бул жерде биз колдонгон бинардык жылдыруу жип GID алуу үчүн керек. ошол. Биз иштеп жаткан контекстте жипти баштаган негизги процесстин PID. Биз чакырган функция
Терминалга чыгарууда, биз учурда жипке кызыкпайбыз, бирок биз негизги процесске кызыгабыз. Алынган кечиктирүүнү берилген босого менен салыштыргандан кийин, биз структурабыздан өтөбүз маалымат стол аркылуу колдонуучу мейкиндигине окуялар, андан кийин биз жазууну өчүрөбүз 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 программасын даярдоо керек
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()
Сценарийдин өзү даректе жеткиликтүү
Акыры! Эми биз токтоп калган bcache түзмөгү сыяктуу көрүнгөн нерсе чындыгында токтоп калган чалуу экенин көрүп жатабыз generic_make_request() кэштелген диск үчүн.
Ядрону казыңыз
Сурам жөнөтүү учурунда так эмне жайлатып жатат? Биз кечиктирүү суроо-талапты эсепке алуу башталганга чейин пайда болгонун көрөбүз, б.а. ал боюнча статистиканы андан ары чыгаруу үчүн атайын суроо-талапты эсепке алуу (/proc/diskstats же iostat) баштала элек. Бул көйгөйдү кайра чыгарууда iostat иштетүү менен оңой текшерилиши мүмкүн, же
Функцияны карасак generic_make_request(), анда биз суроо-талап бухгалтердик эсепке алуу башталганга чейин дагы эки функциянын чакырылышын көрөбүз. Алгачкы - generic_make_request_checks(), диск орнотууларына байланыштуу суроо-талаптын мыйзамдуулугун текшерет. Экинчи -
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 | |
Чечимге жакындап калдык окшойт. Кезекти тоңдуруу/тоңдуруу үчүн колдонулган функциялар
Бул кезекти тазалоо үчүн талап кылынган убакыт дисктин кечигүү убактысына барабар, анткени ядро кезектеги бардык операциялардын аягына чыгышын күтөт. Кезек бош болгондон кийин, орнотуулардагы өзгөртүүлөр колдонулат. Андан кийин ал деп аталат
Азыр биз кырдаалды оңдоо үчүн жетиштүү билебиз. Udevadm триггер буйругу блок түзмөгүнүн орнотууларын колдонууга алып келет. Бул орнотуулар udev эрежелеринде сүрөттөлгөн. Кайсы орнотуулар кезекти тоңдуруп жатканын sysfs аркылуу өзгөртүүгө аракет кылып же ядронун баштапкы кодун карап таба алабыз. Биз ошондой эле BCC утилитасын сынап көрөбүз
~# /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 негизинде башка кызыктуу мүчүлүштүктөрдү оңдоо жана профилдөө куралдары бар. Алардын бирөөсү -
Source: www.habr.com