Autoscaling — інструмент автоматичного вертикального масштабування ресурсів (CPU|RAM|HDD)

На початку цього року у наших хмарних VDS з'явився публічний API. Він дозволяє клієнту робити практично все ті ж дії з хмарними віртуальними машинами та дисками, що і в панелі: створювати, видаляти диски і ВМ, змінювати тарифи та розмір диска і тп.





Разом з появою API з'явилася ідея на його основі реалізувати систему моніторингу за ресурсами віртуальної машини (ВМ), що працює всередині машини, і автоматичного збільшення/зменшення необхідних ресурсів за необхідності — автоскейлинг (autoscaling, АЅ).
Деякі пояснення по ASТут варто уточнити, що оскільки система AS базується на API, в її завдання не входить миттєве надання ресурсів на вимогу, у момент появи потреби в них, або вгадування/прогнозування майбутньої потреби в ресурсах. Суть AS полягає в тому, що він має зафіксувати момент, коли можна з упевненістю сказати, що ресурсів поточного тарифного плану стає недостатньо для гарантованого* і своєчасного** виконання запущених на віртуальній машині процесів, і автоматично перевести ВМ на наступний тариф.

**Своєчасного — тому що, якщо виконання запущених процесів впирається в процесорний ресурс, то процеси так чи інакше виконуватися. Але час їх завершення стає непередбачуваним.
*Гарантованого, тому що якщо оперативна пам'ять ВМ близька до вичерпання і на ВМ не налаштований swap, то це означає, що близька ситуація, коли якийсь із запущених на ВМ процесів буде завершено аварійно операційною системою, якщо сумарне споживання пам'яті всіма процесами перевищить її загальний обсяг. Якщо ж swap налаштований, то поки він також не вичерпається, ніхто убитий не буде, але швидкодія ВМ також сильно просяде, т. к. залежатиме від швидкості роботи swap-розділу, яка в будь-якому випадку на порядок менше, ніж швидкість роботи оперативної пам'яті.

Для того, щоб зрозуміти, що ситуація близька до критичної, AS повинен провести спостереження протягом деякого часу. Адже ситуація з миттєвим короткочасним сплеском навантаження, швидше за все не є хорошим приводом для зміни тарифу. Таким чином, варто враховувати, що коли AS розуміє, що ситуація є критичною, цілком ймовірно, що деяка нехороша ситуація вже сталася (сторінка вашого сайту вже хвилину не завантажується у якогось відвідувача, або якісь процеси на вашій ВМ вже були завершені аварійно OOM-Killer-му — підсистемою ядра linux, що вибирає, який процес треба принести в жертву, щоб інша система продовжувала працювати, якщо оперативна пам'ять вичерпана). Тобто, AS не є срібною кулею, що гарантовано захищає ваші сервіси від нестачі ресурсів. Але він налаштований таким чином, щоб звести до мінімуму негативні наслідки від вичерпання ресурсів за рахунок своєчасного їх розширення. Він зробить це, напевно швидше, ніж ви самі помітите або навіть дізнаєтесь, що на вашому сервері з'явилися проблеми і встигнете дотягнутися до панелі керування ВМ, щоб руками розширити ресурси. Більше того, у більшості випадків (все залежить від профілю зростання навантаження) AS перемкне тариф раніше, ніж брак ресурсів встигне проявитися явним негативним чином.

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

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

Ми розділили можливість включення AS за різними критеріями. Наприклад, ви можете окремо включити autoscaling за критерієм пам'яті, але не включати його по процесору, якщо вважаєте, що швидкодія запущених всередині вашої ВМ процесів не настільки критично, як стабільність їх роботи, і не потребує додаткових витрат грошей. Наприклад, якщо у вас на віртуальній машині регулярно запускається якийсь важкий довготривалий процес, скажімо, вивантаження великого масиву даних в базу, або інша планова обробка великого масиву даних, то вам, можливо, не так принципово, коли саме завершиться цей процес: через годину чи через два, але набагато більш критично, що б цей процес завершився в принципі, а не був убитий випадково операційною системою в разі нестачі оперативної пам'яті.

Або якщо у вас на ВМ працює маленький додаток, і ви впевнені, що для його роботи не потрібні додаткові пам'ять і CPU, але побоюєтеся, що постійно накопичуються логи від нього можуть забити весь диск і призвести до зупинки важливого процесу, можна включити автоматичне розширення диска і вибрати максимальний межа, до якого ви готові дозволити йому розширюватися. Для скейлінгу за процесору та/або пам'яті також потрібно визначити верхню і нижню межу зміни ресурсів на лінійку тарифних планів.

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

Давайте розглянемо докладніше, як все це працює.

Отже, в нашому випадку autoscaling — це набір python-скриптів, які виконуються за cron-у. При запуску вони читають попередню історію вимірів, виробляють додаткову необхідну діагностику системи, зберігають оновлену історію для наступного запуску, виробляють оцінку отриманих даних і при необхідності роблять дії по зміні тарифу або розширення диска, або просто завершуються.

Autoscaling диска.
До віртуальній машині може бути підключено кілька дисків. Autoscaling можна включати окремо для кожного диска, і окремо виставляти ліміт, до якого диск може бути розширений.

Обмеження:
За замовчуванням на новому диску ставиться ФС ext4, але користувач може пізніше на свій диск встановити будь-яку іншу ФС, таблицю розділів, lvm або щось ще, що йому необхідно. Оскільки ФС має, так само як і сам диск, розширюються автоматично (інакше втрачається суть AS), існує ризик зробити якісь деструктивні дії по відношенню до того, що встановлено на диску і в самому неприємному випадку це може привести, зокрема, до втрати даних. Тому ми вирішили обмежити можливість роботи AS тільки штатної ФС ext4.

Також ми не даємо включати autoscaling для диска на системах, заснованих на Centos6, так як в них використовується занадто старе ядро 2.6, в якому некоректно працює udev, в наслідок чого файлова система автоматично не розширюється після розширення диска.

Механізм роботи:
За cron-раз у 7 хвилин запускається autoscaling по диску. З усіх доступних джерел збирається інформація про всіх підключених до системи дисках.
1. При налаштуванні AS панелі, на віртуальну машину відправляється конфіг у форматі json /etc/autoscaling/autoscaling.conf. Там є перелік усіх дисків, підключених до даної ВМ (їх uid), інформація про те, включений для них autoscaling і до якогось максимуму їх можна розширювати;
2. Всередині ВМ з /sys/class/block беремо перелік усіх дисків, видимих в системі;
3. З /proc/partitions беремо розміри дисків;
4. З /proc/mounts отримуємо інформацію про тип ФС і про те, змонтована вона в режимі read-write;
5. З висновку df отримуємо інформацію про розмірі ФС і про те, скільки на ній залишилося вільного місця.
У підсумку, в результаті збору інформації у нас повинно вийти щось на зразок заповненої таблиці по всіх дисків:
Disk id disk_size max_size autoscale_enabled fs_type is_writable fs_size fs_free
------------------------------------------------------------------------------------------
vda 1081 5 40 True ext4 True 5 1.2
vdb None 0.1 None None iso9660 False 0.1 0.1
vdc 1082 10 45 True ext4 True 6.2 10
vdd 1234 5 15 False ext4 True 5 0.3


Якщо нам вдалося зібрати всю потрібну інформацію з диска, на ньому знаходиться відповідна ФС (ext4) і якщо для нього включений AS, то виконується перевірка: скільки залишилося відсотків вільного місця на ФС. Якщо це значення виявляється меншою порогових 10%, то ініціюється відправлення виклику API в панель на збільшення даного диска. Панель відправляє відповідну команду на майстер-сервер, де працює дана ВМ, про збільшення даного диска. (А також панель відправляє користувачу лист на контактний email про подію з усіма необхідними подробицями). При розширенні диска udev всередині користувальницької системи перехоплює цю подію і запускає процес розширення ФС (resize2fs).

Всі дії AS логируются всередині віртуальної машини /var/log/syslog і в логах панелі.

Autoscaling тарифів.

Для зміни пам'яті або процесора застосовується перехід на інший тариф. У нас є лінійка тарифів, яку ми можемо отримувати через API в форматі json.
Виглядає на даний момент вона ось так:
[
{
"memsize": 1024,
"name": "tiny",
"ncpu": 2
},
{
"memsize": 2048,
"name": "small",
"ncpu": 2
},
{ 
"memsize": 4096,
"name": "medium",
"ncpu": 4
},
{
"memsize": 8192,
"name": "large",
"ncpu": 4
},
{
"memsize": 8192,
"name": "xl8",
"ncpu": 8
},
{
"memsize": 16384,
"name": "xl16",
"ncpu": 8
}
]


Як видно, деякі переходи на сусідній тариф не дають збільшення/зменшення одного з параметрів (cpu/memsize). Наприклад, перехід з «TINY» на «SMALL» тільки збільшує обсяг пам'яті, але не додає CPU. Для того, щоб збільшити CPU перебуваючи на «TINY», треба перейти відразу на «MEDIUM». Це враховується при виборі потрібного тарифу.

Autoscaling тарифів. Пам'ять.

Інформація по споживанню пам'яті береться з /proc/meminfo.

На основі параметрів 'MemTotal', 'MemFree', 'Buffers', 'Cached' і 'Shmem' знаходимо, скільки пам'яті на даний момент витрачено і не може бути звільнена при необхідності.

'Used' = 'MemTotal' — 'MemFree' — 'Buffers' — 'Cached' + 'Shmem'

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

'Cached' — кеш файлів прочитаних з диска. Ця пам'ять може бути оперативно звільнена.

Але є також 'Shmem' — спільна пам'ять, використовувана як найшвидший спосіб межпроцессного взаємодії (IPC). У /proc/meminfo 'Shmem' є частиною 'Cached', але ця частина пам'яті не може бути оперативно звільнена, поки вона використовується якимось процесів.

Тоді відсоток вільної пам'яті разом з тією, яка може бути звільнена за вимогою, можна порахувати як:
('MemTotal' - 'Used') * 100 / 'MemTotal'

або
('MemFree' + 'Buffers' + 'Cached' - 'Shmem') * 100 / 'MemTotal'

Для прийняття рішення про зміну тарифу в більшу сторону ми робимо кілька вимірів разів у хвилину, кожен раз зберігаючи історію попередніх вимірів. Якщо середнє значення вільної пам'яті за останні 5 хвилин виявиться меншою порогових 20%, то ми вважаємо, що ВМ користувача вже стабільно перейшла у критичну зону, і щоб уникнути подальших проблем пора збільшити тариф до наступного тарифу, де пам'яті більше, ніж на поточному. Застосовується відповідний виклик API. Тариф змінюється практично миттєво, без перезавантажень, і у вашій системі відразу ж стає істотно більше пам'яті. Користувачеві надсилається лист на контактний email про подію, що сталася.

Історія збору статистики при зміні тарифу обнуляється, щоб уникнути впливу на нові виміри в нових умовах. Таким чином, наступна зміна тарифу відбудеться не раніше, ніж збереться нова історія вимірів.

Також, всі дії AS логируются у /var/log/syslog і в логах панелі.

Autoscaling тарифів. Процесор.

Статистика по споживанню CPU збирається раз на хвилину і враховується історія за останні 10 хвилин. Статистика по CPU береться з /proc/stat, але інтерпретувати її дещо складніше, ніж у випадку з пам'яттю.

Справа в тому, що для більш точного розподілу ресурсів ми видаємо віртуальній машині не фактичні ядра процесора майстер-сервера, а видаємо завжди будь віртуальній машині 12 ядер майстер-сервера та за допомогою cgroups лімітуючи, скільки відсотків від одного ядра даний процес віртуальної машини може спожити (Як відомо, cgroups — це технологія ядра Linux, що дозволяє обмежити надання ресурсів для процесів або груп процесів ОС). Так що, якщо на тарифі «MEDIUM» ми надаємо 4 ядра CPU для ВМ, то це означає, що фактичні ядер буде надано 12, і за допомогою cgroups буде обмежено споживання CPU чотирмастами відсотками від одного ядра. Даний підхід дає переваги у точності і гнучкості надання ресурсів, так як якщо ми видаємо віртуальній машині фактичні ядра, і на одному ядрі працює більше однієї ВМ, то вони в підсумку ділять ресурс цього ядра CPU між собою, тоді як лімітування за допомогою cgroups дозволяє видати віртуальній машині рівно те, що обіцяно (за умови, що ми стежимо за завантаженістю процесора на майстер-серверах і своєчасно мігруючи віртуальні машини між більш і менш завантаженими майстер-серверами). Також це дозволяє видати не ціле число ядер CPU, а, припустимо, 123% від одного ядра (ми цим не користуємося).

З іншого боку, у цього підходу є і мінус — менш очевидна стає ситуація для власника віртуальної машини. Так як у /proc/cpuinfo, /proc/stat і у всіх утиліти типу top, htop, atop, користувач бачить 12 ядер CPU. І щоб коректно оцінити ситуацію за допомогою подібних утиліт, потрібно трохи більше розуміння суті цих лімітів (втім, для зручності користувачів, в панелі керування виводиться вся необхідна інформація в більш звичному вигляді).
Отже, з /proc/stat ми бачимо приблизно таку картину:
cpu 308468 0 155290 2087891920 139327 0 1717 323604 0 0
cpu0 52005 0 18792 173912232 41000 0 734 40868 0 0
cpu1 20211 0 23423 173905770 5134 0 253 47490 0 0
cpu2 58747 0 22624 173929843 17311 0 162 35843 0 0
cpu3 46602 0 17294 173965248 16777 0 100 31919 0 0
cpu4 21629 0 9426 174009578 8572 0 66 21842 0 0
cpu5 17057 0 10685 174021499 6986 0 53 18868 0 0
cpu6 14844 0 8881 174011454 5011 0 58 25400 0 0
cpu7 17524 0 10358 174023057 6182 0 66 20343 0 0
cpu8 15126 0 8597 174030215 7034 0 47 19455 0 0
cpu9 15437 0 9150 174023863 10817 0 56 20722 0 0
cpu10 15456 0 8545 174028209 8363 0 62 21390 0 0
cpu11 13826 0 7511 174030947 6135 0 56 19460 0 0


Причому, інформація про 12 ядрах в даному випадку недоречна. Досить першого рядка, підсумковій інформацію для всіх ядер.
cpu 308468 0 155290 2087891920 139327 0 1717 323604 0 0

Числові поля даної рядки означають по порядку наступне:user — процесорний час, витрачений на обробку процесів у просторі користувача.
nice — те ж саме, але для процесів із зміненим пріоритетом (nice).
system — час, витрачений на виконання системних викликів.
idle — час у режимі простою (поки процесор не зайнятий ніякими іншими завданнями з цього списку).
iowait — час, проведений процесором в очікуванні операцій вводу-виводу.
irq — час на обробку переривань.
softirq — якісь «легкі» переривання. (Очевидно, щоб зрозуміти, що це таке, треба випити стільки ж поганої горілки, скільки і Олексій Кузнєцов під час їх написання).
steal — час, недоотримане даної віртуальної машиною у зв'язку з тим, що майстер віддав цей ресурс інший ВМ (і/або спрацював cgroups-ліміт, як у нашому випадку).
guest — час, витрачений для роботи віртуального CPU для гостьової системи під управлінням Linux (цей параметр має сенс тільки на майстер-системі).
guest_nice — аналогічно, для гостьових систем із зміненим пріоритетом.

А також для підрахунку статистики за CPU, крім інформації з /proc/stat необхідна інформація про те, скільки насправді ядер CPU виділено за допомогою cgroups для даної ВМ. Тривіальним способом зсередини самої ВМ це не дізнатися.

Тому, використовується ще один виклик API, що видає основну інформацію про віртуальній машині.
В json її можна представити так:
{
"id": 11004,
"ips": [
{

"id": 11993,
"ipvalue": "185.41.161.231"
}
],
"memsize": 1024,
"monitoring_enabled": false,
"name": "esokolov_AS_test",
"ncpu": 2,
"ssh_keys": [
42
],
"state": "active",
"storages": [
12344
],
"type": "tiny",
"vm_id": "vm_40b5d315"
}


Для можливості скейлінгу вниз так само треба знати, скільки ядер у разі переходу на менший тариф. Ця інформація береться з таблиці тарифів, наведеної раніше. Щоб кожну хвилину не смикати API з одними і тими ж запитами інформації від панелі (про тарифи і про параметри даної ВМ), ця інформація зберігається в кеші. До зміни тарифу інформацію з кеша можна цілком вважати актуальною. Кеш скидається у разі зміни тарифу, перезавантаження системи або в разі, якщо він зберігається довше заданого часу (доба у нашому випадку).

У /proc/stat інформація зберігається у вигляді простих чисел лічильників часу (одиниця — це одна сота частка секунди на більшості архітектур) від старту системи.

Так що, зберігання історії нам знадобиться не тільки для отримання статистики за останні 10 хвилин, але і щоб зрозуміти, скільки було витрачено ресурсів у кожен конкретний період вимірювань (вирахуванням значення попереднього періоду з поточних значень. Назвемо ці величини'cur_user', 'cur_nice', 'cur_system' ітд).

Отже, з /proc/stat ми можемо обчислити загальний обсяг ресурсу CPU (назвемо його 'total').
'total' = 'user' + 'nice' + 'system' + 'idle' + 'iowait' + 'irq' + 'softirq' + 'steal'

Загальний обсяг ресурсу CPU за поточний період часу (назвемо його 'cur_total' виходить відніманням 'total' від попереднього виміру з 'total' поточного виміру). Це ресурс всіх 12 ядер. При цьому, частина, віддана даної віртуальній машині ('my_cur_total'), буде дорівнює:
'my_cur_total' = 'cur_total' * ncpu / 12

де ncpu береться з інформацією про ВМ (cgroups-ліміт в ядрах).

Тепер ми можемо розрахувати справжній 'idle' (час бездіяльності CPU) поточного періоду часу (назвемо його 'my_cur_idle') VM.

'my_cur_idle' = 'my_cur_total' - 'cur_user' - 'cur_nice' - 'cur_system' - 'cur_iowait' - 'cur_irq' - 'cur_softirq'


Зрозуміло, що ця величина має мало спільного з 'idle' з /proc/stat, т. к. там ця величина означає час простою всіх 12 ядер.

Величини 'my_cur_idle', 'cur_iowait', 'cur_steal', і т. п., виражені у відсотках від 'my_cur_total' — це по суті ті величини, які зазвичай показують утиліти типу top, atop, htop.
Вони ж і є підставою для перемикання тарифу. Тобто, обчислюється поточний відсоток вільного CPU і записується в історії вимірів. Якщо середнє значення цього відсотка за останні 10 хвилин опускається нижче критичних 10%, то ми перемикаємо тариф на тариф з більшою кількістю CPU. Тобто, підставою для перемикання вгору є ситуація, коли на віртуальній машині за останні 10 хвилин споживалося в середньому понад 90% CPU.

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

Autoscaling тарифу вниз.

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

Але для виключення постійного перемикання тарифів туди-сюди порогові значення для перемикання вниз трохи відрізняються від порогових значень для перемикання вгору.
Так для перемикання вниз на меншому тарифі після перемикання повинно залишитися мінімум 20% невитраченого CPU (idle) і мінімум 30% вільної пам'яті (або тієї, яка може бути звільнена — буфери і дисковий кеш). І підставою для перемикання також є не разовий завмер, а середній результат за останніми 10 вимірам.

PS. Якщо ви вже наш клієнт, то підключити autoscaling можете в панелі управління — bit.ly/Panel-NetAngels.
Для реєстрації нового аккаунта вам сюди


Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.