Steal: хто краде у віртуалок процесорний час

Steal: хто краде у віртуалок процесорний час

Вітання! Хочу розповісти простою мовою про механіку виникнення steal усередині віртуальних машин і про деякі неочевидні артефакти, які нам вдалося з'ясувати при його дослідженні, в яке мені довелося поринути як техдиру хмарної платформи. Mail.ru Cloud Solutions. Платформа працює на KVM.

CPU steal time це час, протягом якого віртуальна машина не отримує ресурси процесора для свого виконання. Цей час вважається лише у гостьових операційних системах у середовищах віртуалізації. Причини, куди подіються ці виділені ресурси, як і в житті, дуже туманні. Але ми вирішили розібратися, навіть поставили цілу низку експериментів. Не те, щоб ми тепер все знаємо про steal, але дещо цікаве зараз розповімо.

1. Що таке steal

Отже, steal – це метрика, що вказує на нестачу процесорного часу для процесів усередині віртуальної машини. Як описано у патчі ядра KVM, Steal - це час, протягом якого гіпервізор виконує інші процеси на хостовій ОС, хоча він поставив процес віртуальної машини в чергу на виконання. Тобто, steal вважається як різниця між часом, коли процес готовий виконатися, і часом, коли процесу виділено процесорний час.

Метрику steal ядро ​​віртуальної машини отримує від гіпервізора. При цьому гіпервізор не уточнює, які саме інші процеси він виконує просто «поки зайнятий, тобі часу приділити не можу». На KVM підтримка підрахунку steal додана в патчі. Ключових моментів тут два:

  • Віртуальна машина дізнається про steal від гіпервізора. Тобто, з точки зору втрат, для процесів на самій віртуалці це непрямий вимір, який може бути схильний до різних спотворень.
  • Гіпервізор не ділиться з віртуалкою інформацією про те, чим іншим він зайнятий — головне, що він не приділяє їй часу. Через це сама віртуалка не може виявити спотворення у показнику steal, які можна було б оцінити за характером конкуруючих процесів.

2. Що впливає на steal

2.1. Обчислення steal

По суті, steal вважається приблизно так, як і звичайний час утилізації процесора. Інформації про те, як вважається утилізація, небагато. Напевно тому, що більшість вважає це питання очевидним. Але тут теж буває підводне каміння. Для ознайомлення з цим процесом можна прочитати статтю Brendann Gregg: ви дізнаєтеся про купу нюансів при розрахунку утилізації та про ситуації, коли цей підрахунок буде помилковим з наступних причин:

  • Перегрів процесора, у якому пропускаються такти.
  • Увімкнення/вимкнення турбобусту, внаслідок якого змінюється тактова частота процесора.
  • Зміна тривалості кванта часу, що відбувається під час використання технологій енергозбереження процесора, наприклад SpeedStep.
  • Проблема підрахунку середнього: оцінка утилізації протягом однієї хвилини на рівні 80% може заховати короткочасний бурст у 100%.
  • Циклічне блокування (spin lock) призводить до того, що процесор утилізований, але користувальницький процес не бачить просування за своїм виконанням. У результаті розрахункова утилізація процесора процесом буде стовідсотковою, хоча фізично процесорний час споживати не буде.

Статті, що описує подібний підрахунок для steal, я не знайшов (якщо знаєте - поділіться у коментарях). Але, судячи з вихідних засобів, механізм розрахунку такий самий, як і для утилізації. Просто в ядрі додається ще один лічильник безпосередньо для процесу KVM (процесу віртуальної машини), який вважає тривалість перебування процесу KVM в стані очікування процесорного часу. Лічильник бере інформацію про процесор з його специфікації і дивиться, чи його тики утилізовані процесом віртуалки. Якщо все, то вважаємо, що процесор займався лише процесом віртуальної машини. Інакше ми інформуємо, що процесор займався чимось, з'явився steal.

Процес підрахунку steal схильний до тих самих проблем, що й звичайний підрахунок утилізації. Не сказати, що такі проблеми з'являються часто, але виглядають бентежно.

2.2. Типи віртуалізації на KVM

Взагалі кажучи, є три типи віртуалізації і всі вони підтримуються KVM. Від типу віртуалізації може залежати механізм виникнення steal.

трансляція. В цьому випадку робота операційної системи віртуальної машини з фізичними пристроями гіпервізора відбувається приблизно так:

  1. Гостьова операційна система посилає своєму гостьовому устрою команду.
  2. Драйвер гостьового пристрою приймає команду, формує запит для BIOS пристрою та відправляє її в гіпервізор.
  3. Процес гіпервізора здійснює трансляцію команди в команду для фізичного пристрою, роблячи її, зокрема, безпечнішою.
  4. Драйвер фізичного пристрою приймає модифіковану команду і відправляє її вже в фізичний пристрій.
  5. Результати виконання команд йдуть назад тим самим шляхом.

Перевага трансляції в тому, що вона дозволяє емулювати будь-який пристрій і не вимагає спеціальної підготовки ядра операційної системи. Але за це доводиться розплачуватися передусім швидкодією.

Апаратна віртуалізація. У цьому випадку пристрій на апаратному рівні розуміє команди операційної системи. Це найшвидший і найкращий спосіб. Але, на жаль, він підтримується далеко не всіма фізичними пристроями, гіпервізорами та гостьовими операційними системами. На даний момент основні пристрої, які підтримують апаратну віртуалізацію, це процесори.

Паравіртуалізація (paravirtualization). Найпоширеніший варіант віртуалізації пристроїв на KVM і найпоширеніший режим віртуалізації для гостьових операційних систем. Особливість його в тому, що робота з деякими підсистемами гіпервізора (наприклад, мережевим або дисковим стеком) або виділення сторінок пам'яті відбувається з використанням API гіпервізора без трансляції низькорівневих команд. Недоліком цього способу віртуалізації є необхідність модифікації ядра гостьової операційної системи, щоб воно могло взаємодіяти з гіпервізором за допомогою цього API. Але зазвичай це вирішується за рахунок встановлення спеціальних драйверів на гостьову операційну систему. У KVM це API називається virtio API.

При паравіртуалізації, порівняно з трансляцією, шлях до фізичного пристрою значно скорочується рахунок відправлення команд безпосередньо з віртуальної машини в процес гіпервізора на хості. Це дозволяє прискорити виконання всіх інструкцій усередині віртуальної машини. У KVM за це відповідає virtio API, який працює тільки для певних пристроїв, як мережевий або дисковий адаптер. Саме тому усередину віртуальних машин ставляться virtio-драйвери.

Зворотний бік такого прискорення — не всі процеси, які виконуються всередині віртуалки, залишаються в ній. Це створює деякі спецефекти, які можуть призвести до появи на стелі. Детальне вивчення цього питання рекомендую почати з An API for virtual I/O: virtio.

2.3. «Справедливий» шедулінг

Віртуалка на гіпервізорі є фактично звичайним процесом, який підпорядковується законам шедулінгу (розподілу ресурсів між процесами) в ядрі Linux, тому розглянемо його докладніше.

У Linux використовується так званий CFS, Completely Fair Scheduler, починаючи з ядра 2.6.23, що став диспетчером за замовчуванням. Щоб розібратися з цим алгоритмом, можна прочитати Linux Kernel Architecture або вихідні коди. Суть CFS полягає у розподілі процесорного часу між процесами залежно від тривалості їх виконання. Чим більше процесорного часу вимагає процес, тим менше він отримує. Це гарантує «чесне» виконання всіх процесів — щоб один процес не займав усі процесори постійно, інші процеси теж могли виконуватися.

Іноді така парадигма призводить до цікавих артефактів. Давні користувачі Linux, напевно, пам'ятають завмирання звичайного текстового редактора на десктопі під час запуску ресурсомістких додатків типу компілятора. Так виходило, тому що нересурсоємні завдання десктопних додатків конкурували із завданнями, які активно споживають ресурси, такими як компілятор. CFS вважає, що це нечесно, тому періодично зупиняє текстовий редактор і дає процесору обробити завдання компілятора. Це поправили за допомогою механізму sched_autogroupАле залишилися багато інших особливостей розподілу процесорного часу між завданнями. Власне, це розповідь не про те, як все погано в CFS, а спроба звернути увагу на те, що «чесний» розподіл процесорного часу – не найтривіальніше завдання.

Ще один важливий момент у шедулері – preemption. Це потрібно, щоб вигнати процес, що зажерся, з процесора і дати попрацювати іншим. Процес вигнання називається context switching, перемикання контексту процесора. При цьому зберігається весь контекст таски: стан стека, регістри та інше, після чого процес вирушає чекати, а на його місце встає інший. Це дорога операція для ОС, і використовується вона рідко, але насправді нічого поганого в ній немає. Часте перемикання контексту може говорити про проблему в ОС, але зазвичай йде безперервно і ні на що особливо не вказує.

Така довга розповідь потрібна для пояснення одного факту: чим більше ресурсів процесора намагається спожити процес у чесному шедулері Linux, тим швидше він буде зупинений, щоб інші процеси теж могли попрацювати. Правильно це чи ні — складне питання, яке за різних навантажень вирішується по-різному. У Windows донедавна шедулер був орієнтований на пріоритетну обробку десктопних програм, через що могли зависати фонові процеси. У Sun Solaris було п'ять різних класів шедулерів. Коли запустили віртуалізацію, додали шостий, Fair share scheduler, тому що попередні п'ять працювали із віртуалізацією Solaris Zones неадекватно. Докладне вивчення цього питання рекомендую почати з книжок на кшталт Solaris Internals: Solaris 10 та OpenSolaris Kernel Architecture або Розуміння ядра Linux.

2.4. Як моніторити steal?

Моніторити steal усередині віртуальної машини, як і будь-яку іншу процесорну метрику, просто: можна користуватися будь-яким засобом знімання метрик процесора. Головне, щоб віртуалка була на Linux. Windows чомусь таку інформацію своїм користувачам не надає. 🙁

Steal: хто краде у віртуалок процесорний час
Виведення команди top: деталізація навантаження на процесор, у крайній правій колонці - steal

Складність виникає при спробі отримати інформацію з гіпервізора. Можна спробувати спрогнозувати steal на хостовій машині, наприклад, за параметром Load Average (LA) - усередненого значення кількості процесів, що чекають у черзі на виконання. Методика підрахунку цього параметра непроста, але загалом, якщо пронормований за кількістю потоків процесора LA більше 1, це свідчить, що сервер з лінуксом чимось перевантажено.

Чого ж чекають на всі ці процеси? Очевидна відповідь – процесора. Але відповідь не зовсім правильна, тому що іноді процесор вільний, а LA зашкалює. Згадайте, як відвалюється NFS і як при цьому росте LA. Приблизно так само може бути з диском, і з іншими пристроєм вводу/виводу. Але насправді, процеси можуть очікувати закінчення будь-якого блокування, як фізичного, пов'язаного з пристроєм вводу/виводу, так і логічного, наприклад, мьютексу. Туди ж ставляться блокування лише на рівні заліза (того ж відповіді від диска), чи логіки (так званих блокувальних примітивів, куди входить купа сутностей, mutex adaptive і spin, semaphores, condition variables, rw locks, ipc locks…).

Ще одна особливість LA в тому, що воно вважається як середнє значення операційної системи. Наприклад, 100 процесів конкурують за файл, і тоді LA=50. Таке велике значення, начебто, свідчить, що операційній погано. Але для іншого криво написаного коду це може бути нормальним станом, при тому що погано тільки йому, а інші процеси в операційній системі не страждають.

Через це усереднення (причому не менше, ніж за хвилину), визначення чогось за показником LA — не найвдячніше заняття, з дуже невизначеними результатами в конкретних випадках. Якщо ви спробуєте розібратися, то виявите, що у статтях на Вікіпедії та інших доступних ресурсах описані лише найпростіші кейси, без глибокого пояснення процесу. Всіх, хто цікавиться, відправляю, знову ж таки, сюди, до Brendann Gregg  - Далі за посиланнями. Кому ліньки англійською переклад його популярної статті про LA.

3. Спецефекти

Тепер зупинимося на основних кейсах появи steal, з якими ми стикалися. Розповім, як вони випливають із усього вищесказаного і як співвідносяться з показниками на гіпервізорі.

Переутилізація. Найпростіше і найчастіше: гіпервізор переутилізовано. Дійсно, багато запущених віртуалок, велике споживання процесора всередині них, велика конкуренція, утилізація по LA більше 1 (у нормуванні процесорних тредів). Усередині всіх віртуалок все гальмує. Steal, що передається з гіпервізора, також росте, треба перерозподіляти навантаження чи когось вимикати. Загалом, все логічно та зрозуміло.

Паравіртуалізація проти одиноких інстансів. На гіпервізорі одна єдина віртуалка, вона споживає невелику частину, але дає велике навантаження по вводу/выводу, наприклад по диску. І десь у ній з'являється невеликий steal, до 10% (як показують кілька проведених експериментів).

Випадок цікавий. Steal тут виникає саме через блокування на рівні паравіртуалізованих драйверів. Усередині віртуалки створюється переривання, обробляється драйвером і йде в гіпервізор. Через обробку переривання на гіпервізорі для віртуалки це виглядає як відправлений запит, вона готова до виконання і чекає на процесор, але процесорного часу їй не дають. Віртуалка думає, що цей час вкрадено.

Це відбувається в момент відправлення буфера, він йде в kernel space гіпервізора, і ми починаємо його чекати. Хоча, з погляду віртуалки, він має одразу повернутися. Отже, за алгоритмом розрахунку steal цей час вважається вкраденим. Швидше за все, у цій ситуації можуть бути й інші механізми (наприклад, обробка ще якихось sys calls), але вони не повинні сильно відрізнятися.

Шедулер проти високонавантажених віртуалок. Коли одна віртуалка страждає від steal більше за інших, це пов'язано саме з шедулером. Чим сильніше процес навантажує процесор, тим швидше шедулер його вижене, щоб решта теж могла попрацювати. Якщо віртуалка споживає трохи, вона майже не побачить steal: її процес чесно сидів і чекав, треба йому давати більше часу. Якщо віртуалка робить максимальне навантаження по всіх своїх ядрах, її частіше виганяють із процесора і намагаються не давати багато часу.

Ще гірше, коли процеси всередині віртуалки намагаються отримати більше процесора, тому що не справляються з обробкою даних. Тоді операційна система на гіпервізорі, за рахунок чесної оптимізації, даватиме все менше процесорного часу. Цей процес відбувається лавиноподібно, і steal підскакує до небес, хоча решта віртуалок його може майже не помічати. І чим більше ядер, тим гірше машині, що потрапила під роздачу. Коротше кажучи, найбільше страждають високонавантажені віртуалки з безліччю ядер.

Низький LA, але є steal. Якщо LA приблизно 0,7 (тобто гіпервізор, здається недозавантажений), але всередині окремих віртуалок спостерігається steal:

  • Вже описаний вище варіант із паравіртуалізацією. Віртуалка може отримувати метрики, що вказують на steal, хоча гіпервізор все добре. За результатами наших експериментів, такий варіант steal не перевищує 10% і не повинен істотно впливати на продуктивність додатків усередині віртуалки.
  • Неправильно вважається параметр LA. Точніше, у кожен момент він вважається правильно, але за усереднення за одну хвилину виходить заниженим. Наприклад, якщо одна віртуалка на третину гіпервізора споживає всі свої процесори рівно півхвилини, LA за хвилину на гіпервізорі буде 0,15; чотири такі віртуалки, що працюють одночасно, дадуть 0,6. А те, що півхвилини на кожній із них був дикий steal під 25% за показником LA, вже не витягти.
  • Знову ж таки, через шедулера, який вирішив, що хтось занадто багато їсть, і нехай цей хтось зачекає. А я поки що переключаю контекст, пообробляю переривання і займуся іншими важливими системними речами. У результаті одні віртуалки не бачать жодних проблем, інші відчувають серйозну деградацію продуктивності.

4. Інші спотворення

Є ще мільйон причин для спотворень чесної віддачі процесорного часу на віртуалці. Наприклад, складнощі в розрахунки вносять гіпертредінг та NUMA. Вони остаточно заплутують вибір ядра для виконання процесу, тому що шедулер використовує коефіцієнти - ваги, які при перемиканні контексту роблять підрахунок ще складнішим.

Бувають спотворення через технології типу турбобусту або, навпаки, режим енергозбереження, які при підрахунку утилізації можуть штучно підвищувати або знижувати частоту або навіть квант часу на сервері. Включення турбобусту зменшує продуктивність одного процесорного треду через збільшення продуктивності іншого. У цей момент інформація про актуальну частоту процесора віртуальній машині не передається, і вона вважає, що її час хтось тирит (наприклад, вона запитувала 2 ГГц, а отримала вдвічі менше).

Загалом причин спотворень може бути багато. У конкретній системі ви можете знайти щось ще. Почати краще з книг, на які я дав лінки вище, і знімання статистики з гіпервізора утилітами типу perf, sysdig, systemtap, яких десятки.

5. висновки

  1. Якась кількість steal може виникати через паравіртуалізацію, і її можна вважати нормальним. В інтернеті пишуть, що ця величина може становити 5-10%. Залежить від програм всередині віртуалки і від того, яке навантаження вона дає на свої фізичні пристрої. Тут важливо звертати увагу на те, як почуваються програми всередині віртуалок.
  2. Співвідношення навантаження на гіпервізорі та steal усередині віртуалки не завжди однозначно взаємопов'язані, обидві оцінки steal можуть бути помилковими у конкретних ситуаціях при різних навантаженнях.
  3. Шедулер погано ставиться до процесів, що багато просять. Він намагається давати менше тим, хто просить більше. Великі віртуалки – зло.
  4. Невеликий steal може бути нормою і без паравіртуалізації (з урахуванням навантаження всередині віртуалки, особливостей навантаження сусідів, розподілу навантаження за тредами та іншими факторами).
  5. Якщо ви хочете з'ясувати steal у конкретній системі, доводиться досліджувати різні варіанти, збирати метрики, ретельно їх аналізувати та продумувати, як рівномірно розподіляти навантаження. Від будь-яких кейсів можливі відхилення, які треба підтверджувати експериментально або дивитися у дебагері ядра.

Джерело: habr.com

Додати коментар або відгук