Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 95

НАЦІОНАЛЬНИЙ ТЕХНІЧНИЙ УНІВЕРСИТЕТ УКРАЇНИ

«КИЇВСЬКИЙ ПОЛІТЕХНІЧНИЙ ІНСТИТУТ


імені ІГОРЯ СІКОРСЬКОГО»

Навчально-науковий інститут прикладного системного аналізу


Кафедра Системного Проектування

До захисту допущено:
Завідувач кафедри
Вадим МУХІН
«___»___________2023 р.

Дипломна робота
на здобуття ступеня бакалавра
за освітньо-професійною програмою «Інтелектуальні сервіс-
орієнтовані розподілені обчислення»
спеціальності Комп’ютерні науки
на тему: «Механізми автоматичного масштабування та планування у
хмарних обчисленнях»

Виконав:
студент IV курсу, групи ДА-91
Тринус Нікіта Вячеславович__________
Керівник:
Доц. к.т.н
Булах Богдан Вікторович__________

Консультант з нормоконтролю:
доцент, к.т.н. Кирюша Б.А. __________

Рецензент: __________

Засвідчую, що у цій дипломній роботі


немає запозичень з праць інших
авторів без відповідних посилань.
Студент(-ка)

Київ – 2023 року


2

Національний̆ технічний̆ університет України

«Київський політехнічний̆ інститут імені Ігоря Сікорського»

Навчально-науковий інститут прикладного системного аналізу

Кафедра Системного проектування

Рівень вищої освіти – перший (бакалаврський)

Спеціальність – 122 «Комп'ютерні науки»

Освітньо-професійна програма «Інтелектуальні сервіс-орієнтовані


розподілені обчислення»

ЗАТВЕРДЖУЮ
Завідувач кафедри
Вадим МУХІН
«___»____________2023р.

ЗАВДАННЯ
на дипломну роботу студенту Тринусу Нікіті Вячеславовичу

1. Тема роботи «Механізми автоматичного масштабування та планування


у хмарних обчисленнях», керівник роботи Булах Богдан Вікторович, доцент,
затверджені наказом по університету від «_30_»___05______ 2023 р. №2065-с
2. Термін подання студентом роботи «_10__»____06_____ 2023 р.
3. Вихідні дані до роботи:
4. Зміст роботи:
1. Вступ
2. Хмарні Обчислення
3. Kubernetes
4. Існуючі інструменти автоматичного масштабування
5. Алгоритми автоматичного масштабування.
6. Реалізація інструменту автоматичного масштабування
7. Функціонально-вартісний аналіз програмного продукту
8. Висновки
5. Перелік ілюстративного матеріалу (із зазначенням плакатів,
презентацій тощо):
1. Електронна презентація
6. Дата видачі завдання «20» 03 2023 р.
3

Календарний план
Термін
№ Назва етапів виконання виконання
Примітка
з/п дипломної роботи етапів
роботи
1 Отримання завдання 20.03.2023

2 Дослідження літературних джерел присвячених 24.03.2023


проблемі масштабування хмарних ресурсів
3 Вивчення складу сервісів найбільших провайдерів 18.04.2023
хмарних ресурсів (AWS, Google, MS)
4 Аналіз можливостей існуючих інструментів 02.05.2023
масштабування
5 Порівняння найбільш поширених підходів до 10.05.2023
масштабування хмарних ресурсів, пропозиції щодо
можливого їх удосконалення
6 Дослідження можливостей постановки практичних 28.05.2023
експериментів з масштабуванням хмарних ресурсів
7 Оцінка способів використання автоматичного 31.05.2023
масштабування у реальних системах

Студент Тринус Н.В.

Керівник Булах Б.В.


4

АНОТАЦІЯ
до дипломної роботи Тринуса Нікіти
на тему:
«Механізми автоматичного масштабування та планування у хмарних
обчисленнях»
Структура дипломної роботи: Загальний об’єм пояснювальної записки:
95 сторінок, 48 рисунків, 6 таблиць, 32 посилання, 9 додаток.

Актуальність теми. Найбільш популярною перевагою у використанні


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

Мета дослідження. Знаходження оптимальних підходів для


автоматичного масштабування в залежності від поставленої задачі шляхом
створення сервісу для автоматичного масштабування, який підтримує різні
підходи масштабування.

Об’єктом дослідження. Аналіз та порівняння підходів до автоматичного


масштабування у хмарних обчисленнях.

Предметом дослідження. Автоматичне масштабування у хмарних


обчисленнях.

Ключові слова: Хмарні Обчислення, GCP, GKE, Python, Docker,


Kubernetes, Мікросервісна Архітектура API, LSTM, Машине Навчання,
Часові Ряди, Нейронні Мереж, Tensorflow
5

ABSTRACTION
to the Trynus Nikita bachelor’s degree thesis
on the topic:
“Methods of autoscaling and scheduling in Cloud Computing”
Thesis structure: Total volume of the explanatory note: 95 pages, 48 figures,
6 tables, 32 references, 9 appendix.

Relevance of the topic. The most popular benefit of using autoscaling is the
potentially large costs savings. However, this technology also makes it possible to
increase the overall availability of the system, reduce the use of resources, decrease
the time required for system maintenance, make the system able to expand faster
and respond to unexpected load spikes.

The purpose of the study. Finding optimal autoscaling approaches depending


on the task by creating an autoscaling service that supports different scaling
approaches.

The object of research Analysis and comparison of approaches to autoscaling


in cloud computing.

The subject of the study Autoscaling in cloud computing.

Keywords: Cloud Computing, GCP, GKE, Python, Docker, Kubernetes,


Microservice Architecture, API, LSTM, Machine Learning, Time Series, Neural
Networks, Tensorflow
6

ЗМІСТ
Зміст....................................................................................................................6

Перелік умовних позначень, скорочень та термінів......................................8

Вступ...................................................................................................................9

1. Хмарні Обчислення.................................................................................11

1.1. Провайдери Хмарних Обчислень....................................................12

1.2. Сервіси в Хмарних Провайдерів, що потребують автоматичного


масштабування..................................................................................................15

1.3. Висновки до розділу 1......................................................................18

2. Kubernets...................................................................................................19

2.1. Архітектура Kubernetes....................................................................20

2.2. Абстракції для виконання контейнерів..........................................21

2.3. Сервіси в Kubernetes.........................................................................22

2.4. Висновки до розділу 2......................................................................23

3. Існуючі інструменти автоматичного масштабування..........................24

3.1. Інструменти автоматичного масштабування з відкритим


програмним кодом.............................................................................................24

3.2. Інструменти автоматичного масштабування, які належать


провайдерам Хмарних Обчислень...................................................................26

3.3. Висновки до Розділу 3......................................................................27

4. Алгоритми Автоматичного Масштабування........................................28

4.1. Алгоритми на основі статистичних моделей.................................29

4.2. Алгоритми на основі ансамблів......................................................36

4.3. Алгоритми на основі нейронних мереж.........................................40

4.4. Висновки до розділу 4......................................................................42


7

5. Реалізація інструменту автоматичного масштабування......................43

5.1. Загальна архітектура.........................................................................43

5.2. Вибір алгоритмів передбачення......................................................52

5.3. Стратегії застосування змішаних підходів автоматичного


масштабування у реальних середовищах........................................................57

5.4. Недоліки реалізованих алгоритмів..................................................60

5.5. Висновки до розділу 5......................................................................61

6. Функціонально-вартісний аналіз програмного продукту....................62

6.1. Постановка задачі техніко-економічного.......................................62

6.2. Обґрунтування системи параметрів програмного продукту........65

6.3. Аналіз рівня якості варіантів реалізації функцій...........................71

6.4. Обґрунтування системи параметрів програмного продукту........72

6.5. Висновки до розділу 6......................................................................77

Висновки..........................................................................................................78

Список використаних джерел........................................................................79

Додаток.............................................................................................................82
8

ПЕРЕЛІК УМОВНИХ ПОЗНАЧЕНЬ, СКОРОЧЕНЬ ТА


ТЕРМІНІВ
GCP – Google Cloud Platform
AWS – Amazon Web Services
API – Application Programming Interface
GKE – Google Kubernetes Engine
HTTP – Hyper Text Transfer Protocol
CI – Continuous Integration
CD - Continuous Delivery
CSV - Comma-separated values
9

ВСТУП

У сучасній розробці на провідні місця виходить мікросервісна


архітектура[1]. Багато уваги приділяється способам написання, тестування та
розгортання мікросервісів. Саме завдяки цьому архітектурному підходу і
набули популярності такі технології як Docker та Kubernetes. Тим більше, з
паралельним стрімким розвитком хмарних обчислень, командам розробки
стає все легше створювати нові системи та з легкістю їх розгортати, знявши з
себе забов’язання щодо управління фізичними серверами та мережами.

Сервіси, що входять в склад сучасних провайдерів хмарних обчислень,


відзначаються неймовірно зручними налаштуваннями для швидкого
розширення, масштабування та, звичайно ж, пропонують величезну
обчислювальну силу в кілька дій. Проте, за такою простотою ховається, іноді
досить непередбачено, велика ціна за використані ресурси. Наприклад,
система в певній компанії працює досить стабільно без жодних збоїв, проте в
певний момент, можливо відбувається велике локальне свято, кількість
запитів стрімко росте. Інженери компанії в паніці додають багато
віртуальних машин до кластеру, що дозволяє впоратись з навантаженням.
Проте надалі машини ці не видаляються, бо команда боїться повторення
такого навантаження. Саме через це компанія і втратить дуже багато коштів
при використанні ресурсів 10-15% від можливих.

Для вирішення таких проблем існують різні інструменти для


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

Дані інструменти дозволяють елегантно розширяти фізичні можливості


системи використовуючи підходи горизонтального та вертикального
10

масштабування. А в певний момент, коли вже навантаження на систему


спадає, повільно зменшити кількість ресурсів що використовуються.

Також слід звернути увагу на те що команді розробки потрібно буде


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

Саме тому метою даного дослідження є аналіз існуючих інструментів для


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

Також у ході виконання роботи було написано власний інструмент для


автоматичного масштабування мовою Python, в якому реалізовано основні
підходи для масштабування сервісу, який розгорнутий у GKE кластері.

У центрі дослідження був пошук можливих варіантів використання


автоматичного масштабування у реальних задачах, враховуючи всі
обмеження.
11

1. ХМАРНІ ОБЧИСЛЕННЯ

Раніше всі великі компанії використовували власні великі серверні для


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

Цю проблему було вирішено створенням компанії Amazon своїх перших


сервісів Simple Storage Service(S3) та Elastic Compute Cloud(EC2), що
дозволило командам розробки фокусуватись більше не на налаштуванні
серверів, а на розгортанні рішень.

Зараз більшість компаній використовує вже готові сервіси від різних


провайдерів Хмарних Обчислень. Всім відомо, що Netflix навіть має
особливий договір з AWS. Хоча ще 20 років тому така велика компанія точно
б мала власні сервери.

Рішення для хмарних обчислень розділяються на різні категорії згідно


рівню управління інфраструктури, яку беруть на себе провайдери:

- Infrastructure as a Service (IaaS): Це нейменьший рівень контролю над


ресурсами. Користувачам надається можливість обирати певні
конфігураційні деталі. Можна представити на цьому рівні надання
віддалених віртуальних машин.
- Platform as a Service (PaaS): На даному рівні вся інфраструктура є
певною абстрацією. Тобто розробники, як правило, не мають прямого
доступу до предоставлених фізичних ресурсів. Код подається цій
системі часто як Docker контейнери.
- Software as a Service (SaaS): На цьому рівні кінцевий користувач взагалі
нічого не знає про фізичні та існує у світі чарів. Прикладом можна
назвати Google Docs.
12

Рис. 1.1 Розподілення сервісів [6]

1.1. Провайдери Хмарних Обчислень

Наразі ринок хмарних обчислень фактично повністю контролюється


трьома компаніями: Amazon(підрозділ AWS), Microsoft(підрозділ Microsoft
Azure) та Google(підрозділ GCP).
1.1.1 Amazon Web Services[2]
Компанія Amazon стала першою на ринку хмарних обчислень, що
дозволило їй захопити лідерство в цьому напрямку. Головні переваги
компанії наступні:

- Найбільш широка пропозиція різних сервісів за всіма напрямками.


Обчислення, зберігання даних, машинне навчання, бази даних та
аналітика.
13

- Amazon на вершині та має запас в напрацюваннях. Саме це дозволяє


компанії фокусуватись на найновітніших технологіях та підходах,
випереджаючи своїх конкурентів

1.1.2 Google Cloud Computing[3]


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

Також слід підкреслити наступні переваги над конкурентами:

- Великий нахил у сторону технологій з відкритим кодом. Це дозволяє


компаніям не зазнавати так званої проблеми “Vendor Lock”, коли вся
система побудована на сервісах, які є доступними лише в одній
компанії і не мають аналогів у інших. Таким чином, компанії стають
залежними від умов, які диктує їм провайдер. Такі ситуації є дуже
небажаним, і кожний розробник мусить розуміти відповідальність та
ризики.
- Готові інструменти для CI/CD. Можна згадати про зручність
використання Cloud Build сервісу, підв’язка репозиторіїв з кодом до
розгортання у сервісах.
- Фокус на Машинному Навчанню та Штучному Інтелекту. Google
пропонує різноманітні натреновані моделі, сервіси AutoML та
інфраструктура для простого розгортання моделей та процесів, що
стосуються Машинного Навчання у компанії
14

1.1.3 Microsoft Azure[4]


Фокус компанії Microsoft, як не дивно, був направлений для полегшення
менеджменту інфраструктури своїм великим ентерпрайз клієнтам, в яких
багато інфраструктури розташовано локально, а не в Хмарних Обчисленнях.

Саме тому, Microsoft Azure має наступні переваги перед своїми


конкурентами:

- Безшовна інтеграція зі всією екосистемою Microsoft, що відкриває


доступ до Microsoft Window Server, Microsoft Active Directory та Office
365. Ця перевага є дуже цінною для великих компаній, які плідно
використовують різні послуги компанії Microsoft.
- Великі можливості для побудови так званих «Гібридних Хмар»[5].
Такі сервіси як Azure Arc та Azure Stack дозволяють поєднувати
локальну інфраструктуру з хмарною, що полегшує управління та
плавний перехід до повноцінних хмарних обчислень.
- Маючі великий досвід роботи з великими компаніями з різними
ключових для різних країн секторів таких як фінанси компанія
Microsoft має неймовірне якісне розуміння юридичних та безпекових
процесів. Саме за цю перевагу великі компанії обирають Microsoft
Azure як провайдер інфраструктури.
15

1.2. Сервіси в Хмарних Провайдерів, що потребують


автоматичного масштабування

Провайдери хмарних обчислень складаються з багатьох різним за


напрямком сервісів таких як:

- Обчислювальні: відповідають за сервіси, яку використовуються


напряму для проведення обчислень. Такі сервіси зазвичай
відрізняються за рівнем управління платформою, наприклад існують
рішення, де потрібно обирати операційну систему, тип пам’яті та інші
низько рівневі налаштування і навпаки існують сервіси, де взагалі
нічого не потрібно обирати, лише подати на вхід Docker контейнер чи
посилання на репозиторій
- Зберігання: такі сервіси пропонують різні способи збереження даних.
Можливо приєднувати сервіси зберігання даних з сервісами
обчислювальними, налаштовуючи окремо кожен з них
- Бази Даних: сервіси для менеджменту баз даних, де провайдер бере на
себе управління та адмініструванням баз. Це дозволяє командам
розробки сфокусуватись на бізнес задачах та додаванні нових
можливостей в їхні системи, а не оновлювати Postgres з версії 14.1 на
15, ламаючи все.

Для кожного з цих напрямків сервісів можливо та рекомендується


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

Розв’язання задачі автоматичного масштабування Баз Даних досить


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

Тому, ідеальним кандидатом для проведення різного роду досліджень


залишаються обчислювальні сервіси, бо дозволяють як горизонтальне так і
вертикальне розширення.

1.2.1. Обчислювальні сервіси в AWS


- Amazon EC2(Elastic Compute Cloud)[2]: надає доступ до віртуальних
серверів у AWS, пропонуючи різноманітні комбінації з потужностями
машин. Цей сервіс є досить гнучким до модифікацій. Також до нього зручно
підключати сервіс AWS Autoscaling, який пропонує динамічно збільшувати
та зменшувати кількість машин в групі машин, про що буде розказано в
існуючих рішеннях.

- AWS Lambda: безсерверна платформа, зо дозволяє виконувати код без


керування серверами. Головною перевагою Lambda ж підтримка подій.
Тобто, код запускається після певного запиту користувача чи на будь-яку
іншу подію. Також цей сервіс зручно підключати до черг повідомлень і
обробляти повідомлення кожні n секунд або кожні N повідомлень.

- Amazon ECS(Elastic Container Service): це платформа для оркестрації


контейнерів, що полегшує всі стадії управління Docker контейнерами. ECS
забезпечує високу масштабованість, доступності та оптимізацію ресурсів.
Дозволяє запускати контейнери на кластері EC2 машин або на AWS Fargate -
безсерверній платформі для запуску контейнерів. ECS дозволяє повністю
налаштовувати життєвий цикл контейнерів, керуючи масштабуванням і
17

балансуванням. Це дозволяє використовувати ECS в мікросервісній


архітектурі, де дуже цінується наявність подібного функціоналу.

- Amazon EKS(Elastic Kubernetes Service): це платформа, яка включає у


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

1.2.2 Обчислювальні сервіси в GCP


- Google Compute Engine (GCE): віртуальні машини в Хмарних
Обчисленнях від компанії Google. Головною перевагою є висока
продуктивність машин та великий список того, що можливо в них
налаштувати. Також GCE дуже добре інтегрується з іншими сервісами, що
дозволяє легко комбінувати різні архітектурні рецепти.

- Google Kubernetes Engine (GKE): Сервіс, який значно полегшує


розгортання контейнерів, беручи на себе відповідальність за керування та
масштабування. В GCP пропрієтарний Kubernetes вийшов просто на
космічний рівень порівняно з опонентами. Оскільки Google є автором
технологій, вона дуже успішно розвивається всередині компанії.

- Google App Engine: Це платформа, яка повністю забирає від розробників


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

- Google Cloud Functions: Це платформа, яка для користувача видається


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

1.2.3. Обчислювальні сервіси в Microsoft Azure


- Azure Virtual Machines: Віртуальні машини всередині Azure. Дуже
гнучкі на конфігурації та операційні системи. Також дуже здібні до
розширення.

- Azure Kubernetes Service (AKS): Головною перевагою цього сервісу є


якісна інтеграція з іншими сервісами AKS. ASK може використовувати Azure
Active Directory для авторизації та автентифікації. Також дуже зручно
інтегрується з CI/CD сервісами Azure, що робить розгортання нових версій
системи безшовним та дуже легким.

1.3. Висновки до розділу 1

У першому розділі було розглянуто сам підхід до організації Хмарних


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

Найбільшу увагу привернули Обчислювальні сервіси через їхню простоту


до масштабування та легкість для того, аби такі масштабування
застосовувати.

Проаналізувавши доступні обчислювальні сервіси, які присутні в


кожному з провайдерів Хмарних Обчислень можна побачити, що у кожному
з них присутній сервіс, що дозволяє розгортати кластери Kubernetes та
полегшує всі процеси пов’язані з запусками контейнерів всередині кластеру.
І дійсно підкупає той факт, що Kubernetes – це технологія з вільним кодом.
Тобто, фактично досягається уникнення так званого “Vendor Lock”.
19

Саме тому наступний розділ буде саме про технологію Kubernetes, та


застосування автоматично масштабування для цієї задачі.

2. KUBERNETS

У світі мікросервісів та контейнерів в певний момент стає просто


неможливою підтримка та адміністрування навіть десятків різних
мікросервісів. Особливо, якщо розглядати проблеми масштабування, коли
необхідно створювати кілька контейнерів та балансувати між ними
навантаження. Важко навіть уявити що робити у випадках великих компаній,
в яких існують сотні мікросервісів, кожний з яких потребує горизонтального
масштабування, тобто існування кількох екземплярів.

Можливо тільки уявити складність моніторингу та перезапуску тисяч


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

Для вирішення цієї проблеми, компанія Google розробила інструмент


Kubernetes, який грає роль оркестратору контейнерів. Тобто, Kubernetes
можливо представити як диригента, а контейнери – як театральну трупу.

Головні можливості, які має Kubernetes наступні[7]:

- Автоматичне розгортання. Можливість задавати потрібний Docker


Image[8], кількість екземплярів та потрібні ресурси
- Автоматичне масштабування. Kubernetes дозволяє має вбудований
інструмент автоматичного масштабування[9] Horizontal Pod
Autoscaling, який відштовхується від метрик.
- Балансування навантаження. Kubernetes містить вбудовані готові
сервіси для балансування навантаження на екземпляри. Тобто, для
кількох екземплярів надається єдиний зовнішній IP адрес через який
20

розподіляється навантаження на кілька контейнерів. Алгоритм


балансування розробник обирає сам поміж кількох стратегій
- Самовідновлення та відмовостійкість. Коли екземпляр знищується
через помилку Kubernetes автоматично перестворює екземпляр згідно
попередньо заданих конфігурацій та дозволяє системі бути надійною
та зменшити кількість навантаження на команду розробки.
- Вбудовані алгоритми для розгортання та відкату розгортання.
Kubernetes з самого початку містить вже готові найпопулярніші
алгоритми розгортання нових версій сервісів таких як: rolling, recreate,
blue/green та canary.

2.1. Архітектура Kubernetes

Kubernetes складається з наступних компонентів:

- kube-apiserver: відповідає за доступ для адміністраторів кластеру.


Можна назвати кермом керування. Всі запити для управління
кластером здійснюються саме через цей компонент. Також можливо
запускати кілька екземплярів цього компоненту паралельно для
покращення доступності.
- etcd: база даних типу Ключ-Значення, яка підтримує CA з CAP
теореми[10]. Використовується для зберігання усіх даних, що
стосуються подій та метаданих у кластері. Також існують підходи для
створення резервного копіювання для уникнення випадків втрати всіх
даних про кластер.
- kube-scheduler: адміністративний сервіс, що відповідає за планування
та запуск нових Pod всередині вузлів кластеру. Сервіс аналізує усі
важливі метрики вузлів, шукає найбільш незагружений вузол, якому
вистачить ресурсів для запуску нових Pod.
- kube-controller-manager: сервіс для управління різними операційними
процесами кластеру такими як: перевірка чи відповідають вузли,
21

перевірка запуску запланованих задач, створення зв’язку між Pod-ми


та сервісами.
- kubelet: демон процес, що виконується на кожному вузлі кластеру. Це
дозволяє пересвідчуватись чи запущені контейнери в Pod-ах та чи
вони знаходяться у коректному стані.
- kube-proxy: демон процес, що запущенний на кожному вузлі кластеру
та відповідає за передачу пакетів у вузол та з нього, встановлюючи
спеціальний зв’язок між вузлами всередині кластеру.

Рис. 2.1 Загальна архітектура Kubernetes

2.2. Абстракції для виконання контейнерів

Kubernetes містить різні підходи для запуску програм. Наступні приклади


є найпопулярнішими з них:

- Pod: найменша частинка всередині Kubernetes. Загальне правило


свідчить, що всередині однієї Pod має розгортатись один контейнер.
- Deployment: використовуються для управління Replica Set-ми та Pod-
ми всередині них. Deployments створюються за допомогою YAML
файлів, де задається необхідна кількість реплік для Pod-и.
22

- ReplicaSet: відповідають за підрахунок кількості запущених Pod


одного типу. Використовується всередині Deployment
- StatefulSet: використовуються для управління програмами, які
зберігають свій стан(бази даних, файли та інше). Для таких програм
необхідна постійна пам’ять із збереженням стану для кожної з Pod.
Вимагається додаткові процеси для управління виділенням ресурсів.
- DaemonSet: використовуються для того, аби переконатись що певні
Pod-и запущені на кожному з вузлів кластеру. Такі програми зазвичай
виконують роль збору логів чи метрик, тобто більш операційна
діяльність ніж якась бізнес-логіка
- Job: використовуються для запуску одинарних задач. В ході виконання
створюється одна чи кілька Pod і потім кожна з Pod безповоротно
видаляється після виконання задачі.
- CronJob: фактично повторюють функціонал Job, додаючи можливість
для задання розкладу запуску, тобто вводячи багатоповторне
виконання задач

2.3. Сервіси в Kubernetes

Сервіси дозволяють створювати один IP адрес для кількох под одного


Deployment для того, аби коли перестворювались поди не втрачався зв’язок
до них. Сервіси можна викласти в певну ієрархію, де кожен наступний сервіс
містить можливості попередника.

Взагалі існують наступні види сервісів всередині Kubernetes:

- ClusterIP: це сервіс за замовчуванням. Виставляє сервіс на загальний


кластерний IP адрес. Цей тип комунікації ідеально підходить для
внутрішньо кластерних комунікацій між сервісами.
- NodePort: виставляє сервіс на статичний порт на кожному з вузлів
кластеру. Це дозволяє під’єднатись з «зовнішнього світу» до цього
23

сервісу, дізнавшись IP адресу окремого вузла та порт. Проте не


рекомендується використовувати у продакшені.
- LoadBalancer: має під собою все те що має NodePort, проте робить ще
один крок у абстракції, яке розподіляє навантаження на вузли, які вже
далі прокидують далі трафік до сервісу. У подарунок отримується
балансування навантаження.

Рис 2.2 Типи сервісів у Kubernetes [11]

2.4. Висновки до розділу 2

У цьому розділі було розглянуто архітектуру Kubernetes, що є важливим


аспектом даної роботи, бо сама задача автоматичного масштабування буде
розглядатись саме в контексті поєднання Deployment та Load Balancer. І
масштабуватись буде кількість Pod.

Можна побачити, що саме гнучкість та здатність до масштабування


Kubernetes дозволяє досить легко управляти ресурсами всередині. Також в
цьому розділі було сказано про HPA – Horizontal Pod Autoscaling. Проте чи
задовольняє цей автоматичний масштабувальник всі вимоги буде розглянуто
вже у наступному розділі
24

3. ІСНУЮЧІ ІНСТРУМЕНТИ АВТОМАТИЧНОГО


МАСШТАБУВАННЯ

Проблема автоматичного масштабування є досить актуальною, саме тому


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

3.1. Інструменти автоматичного масштабування з


відкритим програмним кодом

- HPA(Horizontal Pod Autoscaling): інструмент від Kubernetes, який


базується на метриках, які мають Pod-и. HPA дозволяє встановити
який рівень використання ресурсів, час для переключення та
обмеження по кількості створених Pod.
25

Рис. 3.1 Принцип роботи HPA[9]

- Predictive Horizontal Pod Autoscaler[12]: розширює базовий функціонал


HPA за допомогою додавання частини з передбаченнями,
використовуючи наступні статистичні моделі: Holt-Winters
Smoothing[13], Linear Regression[14].
- KEDA[15]: це набір інструментів, які дозволяють автоматично
масштабуватись відносно кількості повідомлень, які наприклад
прийшли в RabbitMQ[16] чергу. Великою перевагою KEDA можна
зазначити ще наявність великої кількості різних джерел для даних
таких як: Kafka, Pulsar, ArangoDB, AWS CloudWatch, AWS DynamoDB,
Cassandra, InfluxDB, AWS SQS.
26

Рис. 3.2 Архітектура KEDA

3.2. Інструменти автоматичного масштабування, які


належать провайдерам Хмарних Обчислень

- AWS EC2 Auto Scaling: дозволяє динамічно змінювати кількість EC2


машин під’єднаних до, так званої, групи EC2 віртуальних машин.
Даний інструмент дозволяє масштабується базуючись на метриках.
Також можливо активувати режим передбачення. Проте даний підхід
має ряд обмежень[17]: режим передбачення стає доступним лише
після 24 годин, для того, аби зібрати більше метрик, потрібно
переконатись, що дані є дійсно циклічними та мають певні тренди та
повторюваність.
27

Рис. 3.3 Архітектура EC2 Auto Scaling

- GCP Managed Instance Group Autoscaler: інструмент для автоматичного


масштабування для групи віртуальних машин. Можна обирати ряд
метрик. Також доступний режим передбачення, який має схожі
вимоги для передбачення як і в AWS.
- GCP App Engine Autoscaling: інструмент для автоматичного
масштабування, який вбудований всередині App Engine сервісу та
дозволяє розширюватись чи зменшувати кількість екземплярів в
залежності від використання Центрального Процесора, оперативної
пам’яті, кількості паралельних запитів.
28

Рис. 3.4 Демонстрація автоматичного масштабування за допомогою


передбачення

3.3. Висновки до Розділу 3

Даний розділ був присвячений основним інструментам автоматичного


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

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


напряму залежить від метрик за певним крітерієм.

4. АЛГОРИТМИ АВТОМАТИЧНОГО
МАСШТАБУВАННЯ

Основні алгоритми автоматичного масштабування:

- Rule-Based Scaling: масштабування на основі певних попередньо


заданих правил. Наприклад, може бути встановлене правило для
розширення кількості екземплярів за умови, що утилізація
центрального процесора перевищує значення 80%. Розширення буде
29

відбуватись допоки середнє навантаження на екземпляри буде


перевищувати задане обмеження
- Predictive Scaling: масштабування на основі передбачення значень
ключових метрик на основі попередніх даних.
- Event-Driven Scaling: масштабування, яке базується на подіях. Тобто,
не чекаючи на збільшення навантаження на екземпляри, алгоритм
збільшує кількість екземплярів в залежності від довжини черги. Даний
підхід дозволяє передбачити навантаження та превентивно його
нівелювати.

Правила для Rule-Based Scaling та Event-Driven Scaling є досить


очевидними. Для Predictive Scaling навпаки існує ціле різноманіття різних
підходів.

Надалі задачу автоматичного масштабування на основі передбачень буде


розглянуто як задачу Time Series[18]. А сам набір даних виглядає наступним
чином:
30

Рис. 4.1 Циклічний набір даних для представлення часових рядів

4.1. Алгоритми на основі статистичних моделей

В першу чергу, дану задачу можливо розглядати як певну статистичну


модель. І розв’язувати задачу передбачення наступних кроків часового ряду.

Для цього потрібно розуміти ключові поняття з точки зору часових


рядів[19]:

- Рівень: основа будь-якого часового ряду, додаючи до якого інші


компоненти і формується фінальний часовий ряд.
- Тренд(T): задає напрямок в якому рухається ряд. Цей напрямок може
бути направленим вгору чи вниз.
- Циклічність(C): здатність часового ряду до тривалого зростання та
падіння.
- Сезонність(S): повторюваний шаблон всередині часового ряду.
Важливо відмітити, що для сезонності дуже важлива саме
періодичність, тобто повторювання одного й того ж шаблону
стабільно кожні N проміжків часу. Головною відмінністю від
Циклічності є те що, Сезонні коливання можливо передбачити, тобто
вони є очікуваними. Наприклад, існує сезонність у пляжному
відпочинку на Майорці.
- Шум(R): це випадкові коливання всередині даних, які неможливо
передбачити, проте вони ніяк не впливають на загальний тренд.
31

Рис. 4.2 Розбиття часового ряду на складові[19]

В теорії часових рядів існує два варіанти розкладання часового ряду на


компоненти:

- Мультиплікативне розкладання: представлення часового ряду як


добуток основних компонентів: Y = T * S* R
- Адитивне розкладання: представлення часового ряду у вигляді суми
основних компонентів: Y = T + S+ R

Головне правило для того, щоб обрати правильний спосіб декомпозиції


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

Інші важливі статистичні аспекти для аналізу та передбачення в часових


рядах:

- Автокореляція: це показник того як часто та з якою регулярністю


повторюється те чи інше значення
32

- Стаціонарність: вміння часового ряду не змінювати свої ключові


статистичні характеристики протягом часу. Ключовими показниками є
тренд та середнє значення.

Рис. 4.3 Демонстрація стаціонарних та не стаціонарних рядів[20]

4.1.1. Типові перетворення для часових рядів


Для того, щоб зрозуміти ключові підходи в прогнозуванні також необхідно
розглянути типові популярні перетворення:

- Рухоме середнє[22]: згладжування на певній підмножині значень


часового ряду.

Найпростійший вид рухомого середнього називається Simple Moving


Average(SMA). Найпростіший спосіб застосування рухомого середнього це
наприклад певний фільтр.

Формула для підрахунку SMA на k останніх значеннях, де загальна


кількість всіх значень це n, можна побачити на Рис.4.4 .
33

Рис. 4.4 Формула підрахунку SMA на k-останніх значеннях[22]

Приклад застосування рухомого середнього можна побачити на Рис. 4.5.

Рис.4.5 Застосування рухомого середнього[22]

Приклад застосування рухомого середнього на початковому наборі даних


з метриками можна побачити на Рис. 4.6

Рис. 4.6 Рухоме середнє з кроком 50


34

- Експоненційне згладжування[21]: процес згладжування та


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

Загальна формула експоненційного згладжування можна побачити на


Рис. 4.7. Тут слід звернути увагу на параметр α – це фактор згладжування, xt
– це теперішнє значення, а st-1 це попереднє згладжене значення.

Рис. 4.7 Формула для експоненційного згладжування

Застосування на початковому наборі даних з Рис. 4.1 можна побачити на


Рис. 4.7.

Рис. 4.7 Експоненційне згладжування, з параметром α=0.1


35

4.1.2 Популярні алгоритми для прогнозування часових рядів


Якщо розглядати суто статистичні моделі, тоді фігурують три головні
алгоритми: ARIMA, SARIMA та SARIMAX.

Кожний з цих алгоритмів має спільну базу:

1. AR – Autoregressive component

Для передбачення за допомогою даного методу використовується


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

Рис. 4.8 Формула для вирахування Autoregressive компоненти[23]

2. I – Integrated. Крок для передопрацювання даних до стаціонарного


виду.
3. MA – Moving Average.

Формулу для підрахунку наступних значень можна знайти на Рис. 4.9

Рис. 4.9 Формула знаходження передбачення на основі Moving Average[24]

Де, μ – середнє значення, θ1…n – це ваги тобто параметри які можливо


налаштувати, та ε – це помилки.

Отже, на основі даних компонентів будуються наступні алгоритми:


36

- SARIMA:

Загальна формула для підрахунку наступного значення можна побачити


на Рис. 4.10.

Рис. 4.10 Формула для підрахунку наступного значення в SARIMA

Головною особливістю даної моделі є додавання сезонної компоненти.

- ARIMAX та SARIMAX(Seasonal ARIMAX):

Формулу для підрахунку кожного наступного значення для даних


моделей можна побачити на Рис. 4.11

Рис. 4.11 Формула для передбачення даних у моделі SARIMAX

Головною особливістю є додавання «зовнішніх даних», тобто беруться до


уваги і інші параметри з набору даних, а не тільки цільове значення та
часовий ряд

4.2. Алгоритми на основі ансамблів


Алгоритми, що базуються на ансамблях – це підхід у Машинному
Навчанні, коли обирається кілька «слабких» моделей і вони поєднуються,
даючи досить потужний результат. Найбільшої популярності цей напрям
набув, після створення алгоритму XGBoost[25].

Розвиток цього напрямку відбувався наступним чином[26]:

1. Дерева рішень – це алгоритм у машинному навчанні. Який базується


на підрахунку ентропії інформації. Алгоритм визначає найбільш
37

привабливі для нього особливості в даних. Алгоритм можна


використовувати як для задач класифікації так і для задач регресії.

Серед переваг дерев рішень слід відзначити наступні фактори[27]:

- Можливість візуального зображення дерева з вагами


- Не потребує значних зусиль з підготовки даних
- Допускає статистичні перевірки для моделі

На Рис. 4.12 можна побачити приклад викорстання Дерева Рішень для


задачі регресії

Рис. 4.12 Використання дерева рішень у задачі регресії[27]


38

Рис. 4.13 Візуалізація Дерева Рішень[29]

2. Bagging – алгоритм машинного навчання, який вже починає


використовувати підхід ансамблів. Тобто складається з кількох
моделей, що натреновані на випадкових даних та кожна з моделей дає
свою відповідь. Відповіді складаються і за допомогою голосування
обирається переможний варіант.
39

Рис. 4.14 Візуалізація Bootstrap aggregating[28]

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


підході Bagging, проте з відміннюстю, що кожне дерево-модель
будується лише на основі певної обмеженої кількості особливостей
даних. Це дозволяє значно покращити якість алгоритму через те що
моделі менше корелюють між собою, таким чином покращується
знаходження часом мало значимих особливостей
4. Boosting – Цей підхід є протилежним до Bagging, бо в цьому випадку
кожна наступна модель будується базуючись на недоліках попередніх
моделей. Тобто, моделі виконуються поступово, а не паралельно.
40

Рис. 4.15 Boosting алгоритм[29]

5. Градієнтний Boosting – Фактично це і є Boosting, проте мінімізація


здійснюється за допомогою алгоритму градієнтного спуску
6. XGBoost – це алгоритм, який продовжує напрямок Boosting, проте має
ще додаткові переваги такі як регуляризація та різні обчислювальні
оптимізації, що дозволяють значно збільшити швидкість роботи
алгоритму.

4.3. Алгоритми на основі нейронних мереж

В сучасному світі жодну задачу передбачення не розв’язують без


використання нейронних мереж. Велика кількість різноманітних архітектур
та здатність до налаштування роблять цей підхід дуже універсальним для
різноманітних задач. В тому числі для задачі передбачення часових рядів.

З багатьох різноманітних архітектур нейронних мереж найкраще для


вирішення проблеми часових рядів підходить саме Рекурентні нейронні
мережі(Recurrent neural network).
41

І на це є кілька причин:

- RNN мережі створенні саме для обробки послідовних даних, що


фактично чудово відповідає задачі прогнозування часових рядів.
- Розуміння контексту. Нейронні мережі цієї архітектури здатні якісно
розуміти циклічність та сезонність даних, зберігаючи правильним
чином інформацію про минулі кроки
- Можливість обробки вхідних даних різної довжини.
- Здатність бути використаною в задачах обробки даних в реальному
часі.

Існують два головні рішення згідно цієї архітектури:

- LSTM(Long Short-Term Memory): архітектура даної мережі


складається з наступних компонентів: стану(самого LSTM юніту),
вхідних та вихідних вентилів та вентилю «забування», який відповідає
за вибір інформації, яка варта збереження

Рис. 4.16 Архітектура LSTM мережі[30]

- GRU(Gated Recurrent Unit): вентиль оновлення, який відповідає за


вибір інформації, що буде оновлена з попереднім схованим станом;
42

вентиль збросу навпаки відповідає за забуття інформації з схованого


стану; схований стан зберігає важливу для прогнозування інформацію;
теперішня пам’ять зберігає корисну інформацію на даному кроці.

Рис. 4.17 Архітектура GRU мережі[30]

4.4. Висновки до розділу 4

У цьому розділі було розглянуто різні алгоритми для прогнозування


часових рядів. Всю увагу було приділено алгоритмам для Predictive Scaling,
через складність цієї задачі та через тривіальність задачі Rule Based.

Кожен з напрямків передбачення має свій унікальний спектр дії та набір


прийомів для покращення результату. В ході дослідження найзручнішими
виявились алгоритми на основі XGBoost та LSTM через здатність цих
алгоритмів добре підлаштовуватись під складні шаблони даних без
попердньої обробки та аналізу даних шаблонів як це потребують статистичні
моделі.
43

5. РЕАЛІЗАЦІЯ ІНСТРУМЕНТУ АВТОМАТИЧНОГО


МАСШТАБУВАННЯ

Для перевірки гіпотез та пошуку оптимальної стратегії для автоматичного


масштабування Pod у Kubernetes кластері було розроблено набір сервісів. У
якості основної мови програмування використовувався Python, для простої
інтеграції моделей машинного навчання с кодом.

5.1. Загальна архітектура

Загальну архітектуру створеного рішення можна побачити на Рис. 5.1


44

Рис. 5.1

Рішення складається з наступних компонентів:

- Busy Server: це Deployment, над яким будуть здійснюватися операції


автоматичного масштабування. Сервер написаний за допомогою
фреймворку FastAPI[31] і виконує генерацію(заповнення випадковими
числами) та підрахунок детермінанту матриці
45

Рис. 5.2 Головний функціонал Busy Server

Для комунікації з сервісом існує один єдиний API виклик - /matrix/{size},


що викликає генерацію та обчислення детермінанту матриці з розміром size.
Головною задачею даного сервісу це виконання ресурсоємних обчислень, для
того щоб була можливість достатньо навантажити Pod.

- Kubernetes Cluster. Фактично, даний сервіс не залежить від реалізації


кластеру. Проте в існуючій реалізації було обрано використати сервіс
GKE всередині GCP, бо надається безкоштовні 300 $ та додаткові 100
$ через реєстрацію за допомогою університетського поштового адресу.

Також слід згадати, що GCP відзначається простотою конфігурації, що


можливо лише підтвердити. Створення кластеру не забрало багато часу і
GKE має досить непогану візуалізацію ключових метрик Deploymeny-у.
46

Рис. 5.3 Доступні метрики для Deployment у GKE

- Балансувальник навантаження. Для доступу до Busy Server з


зовнішнього світу було створено Load Balancer service для Deployment-
у intelliscale-busy-server. За допомогою цього сервісу стає можливим
доступ до Pod з підключеним балансуванням.
- Testing service: сервіс для проведення тестування, який навантажує
сервер запитами. Налаштовується кількість паралельно надсилаємих
запитів, контролюючи показник, що визначає кількість потоків
всередині ThreadPool. Та кількість надісланих запитів в цілому

Рис. 5.4 Приклад тестової компанії


47

- Logs extractor: сервіс, який, роблячи запити на Kubernetes client API,


дістає метрики всіх Pod в Deployment та записує всі значення до черги
повідомлень RabbitMQ.

Рис. 5.5 Код для діставання метрик

Після діставання метрик, вони конвертуються та приводяться до єдиної


форми.

Рис. 5.6 Код приведення значень ЦП та ОЗУ


48

Рис. 5.7 Форма запису даних до черги повідомлень

- RabbitMQ: черга повідомлень, куди передаються метрики. Вибір був


зроблений в сторону цього інструменту через його простоту у
використанні.
- Scaler: ключовий сервіс, який зчитує метрики, що приходять до
RabbitMQ за допомогою polling підходу[32] та в залежності від
значень метрики здійснює відповідні стратегії для масштабування
Deployment. У розпорядженні в сервісу знаходяться наступні способи
визначення коректної кількості Pod: ML Predictor та Rule Based
Predictor. Паралельно Scaler записує всі отриманні метрики у вигляді
CSV файлу до файловою системи(теоретично можливо зберігати
метрики у віддаленому сховищі такому як Amazon S3). У випадку коли
сервер потребує змін(додаванню чи зменшенню кількості Pod), Scaler
за допомогою HTTP викликів накладає потрібні для масштабування
зміни.
49

Рис. 5.8 Головна логіка Scaler

Рис. 5.9 Логіка оновлення кількості Pod

- ML Predictor: сервіс, що відповідає за визначення кількості необхідних


Pod шляхом застосування методів Машинного Навчання. Всього існує
2 доступні способи для передбачення: передбачення кожного
наступного кроку за допомогою LSTM та передбачення певного
заданого інтервалу наперед за допомогою XGBoost.
50

Рис. 5.10 Логіка роботи моделі, що передбачає наступний крок

Рис. 5.11 Оцінка роботи моделі за метриками MAE, MSE, MAPE

- Rules Based Predictor: сервіс, що калькулює необхідне значення,


базуючись на заданих обмеженнях. В своїх діях повністю імітує HPA
51

Рис. 5.11 Принцип роботи Rule Based Predictor

- Model Trainer: сервіс, що вичитує CSV-файл з метриками, що


знаходиться у файловій системі та треную модель(LSTM чи
XGBRegressor).
52

Рис. 5.12 Навчання LSTM моделі та її зберігання у файловій системі

5.2. Вибір алгоритмів передбачення

У розділі 4 було згадано наступні алгоритми за різними напрямками:

1. Часові ряди
- ARIMA/SARIMA(Seasonal ARIMA)
- ARIMAX/SARIMAX(Seasonal ARIMAX)
2. Ансамблі
- XGBoost
3. Нейронні мережі
- LSTM
- GRU(більш швидкий, але менш якісний LSTM)

Порівняння алгоритмів між собою здійснюються за категоріями:


53

- Прогнозування кожного наступного кроку. В цій категорії приймає


участь LSTM та XGBoost, через здатність якісно передбачати навіть
невеликі флуктуації даних.
- Прогнозування великого часового проміжку. В цій категорії будуть
приймати участь статистичні моделі для часових рядів та XGBoost

5.2.1 Прогнозування на великі часові проміжки


Оцінка буде проводитись згідно стандартних метрик MSE, MAPE та MAE

Набір даних для проведення дослідження зображено на Рис. 5.13

Рис. 5.13 Набір даних для проведення дослідження

Дані були зібрані за допомогою запуску Scaler та Metrics Extractor сервісу


паралельно запущеній тестовій кампанії.
54

На жаль, класичні статистичні алгоритми не змогли впоратись з задачею.


(При умові мінімального налаштування для чесності розрахунків)

Наприклад, ARIMA мав наступні результати, які зображені на Рис. 5.14 та


Рис. 5.15

Рис. 5.14 Графік передбачення ARIMA

Рис. 5.15 Метрики ARIMA

У випадку з XGBoost все набагато краще, модель стрімко вчиться і


розуміє шаблони в даних на льоту(Рис. 5.16 та Рис. 5.17):
55

Рис. 5.16 Графік передбачення алгоритмом XGBoost

Рис. 5.17 Метрики алгоритму XGBoost

5.2.2 Прогнозування наступного кроку


Фантастично себе продемонстрував алгоритм LSTM(Рис. 5.18 та Рис.
5.19)
56

Рис. 5.18 Графік передбачення алгоритмом LSTM

5.19 Метрики алгоритму LSTM

Більш скромні, але якісні результати мав XGBoost з передбачення лише


наступного кроку, але доволі високі(Рис. 5.20, Рис. 5.21)
57

Рис. 5.20

Рис. 5.21

5.3. Стратегії застосування змішаних підходів


автоматичного масштабування у реальних середовищах
Для того, щоб інструменти автоматичного масштабування мали успіхи
необхідно чітко розуміти потребу для їхнього застосування.

Слід розглянути можливі сценарії, які трапляються у реальних


середовищах та можливі способи використання автоматичного
масштабування для таких ситуацій:

1. Нехай існує система, яка ПОВИННА швидко відповідати на запити з


мінімальними затримками.
58

В умові сказано, що система мусить швидко відповідати без затримок. В


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

В цій задачі потрібно дослідити циклічність та наявність певних


шаблонів.

Якщо метрики генеруються так як зображено на Рис. 5.22, тоді фактично


немає шансу використати алгоритми для передбачення, бо дані фактично
випадкові.

Можлива стратегія масштабування: на самому початку виділити багато


Pod(щоб утилізація не доходила до заданої межі), і зробити крок розширення
не 1 Pod, а наприклад 20-35% від загальної кількості Pod. Це дозволить
перекривати випадки різкого зросту навантаження. Відхід же навпаки
зробити повільним(видалення по 1-2 Pod з перевіркою загальної утилізації).
59

Рис. 5.22 Симуляція нециклічного навантаження

Якщо дані все ж таки мають певну циклічність як зображено на Рис. 5.23,
тоді відкривається певний простір для здійснення передбачень.

Можлива стратегія масштабування: здійснення передбачення алгоритмом


XGBoost на певний інтервал часу. Береться найбільше значення Pod і
опціонально додається x*N, де x – відсоток та N – максимальне теоретично
потрібне значення Pod згідно прогнозу.
60

Рис. 5.23 Симуляція навантаження з певною циклічністю

5.4. Недоліки реалізованих алгоритмів


Головні недоліки та протипоказання у використанні Rule-based алгоритмів:

- Даний алгоритм не рекомендується підключати з мінімальним


очікуванням між змінами в кількості Pod. Оскільки, існує ймовірність
постійних стрибків вгору та вниз

Головні недоліки та протипоказання у використанні ML-based


алгоритмів:

- Якщо дані абсолютно не циклічні, не мають жодної логіки та


абсолютно випадкові, в такому випадку Predictive Scaling буде лише
заважати
- Моделі постійно мусять перетреновуватись, аби відслідковувати нові
особливості у даних
61

5.5. Висновки до розділу 5


В даному розділі було розглянуто побудований з нуля інструмент для
автоматичного масштабування Deployment-в всередині Kubernetes кластеру,
який розташований у GCP.

Було показано реалізовані методи та вказано їхні недоліки. Також на


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

6. ФУНКЦІОНАЛЬНО-ВАРТІСНИЙ АНАЛІЗ
ПРОГРАМНОГО ПРОДУКТУ
Функціонально-вартісний аналіз – це підхід для оцінювання фактичної
вартості продукту. Всі кроки розбиваються на функції та підраховується
кожну частину продукту.

Метод починається з зіставлення послідовності функцій, які потрібні для


створення продукту. Вони розділяються на дві різні групи: ті що впливають
на вартість та ті що ні.

Йде визначення витрат для кожної з функцій, підрахунок кількості


витрачених годин. У кінці вводиться підрахунок загальної вартості для виго.

6.1. Постановка задачі техніко-економічного


Метод ФВА був використаний під час написання роботи у рамках
проведення техніко-економічного аналізу.

Функціональні та нефункціональні вимоги до продукту:

1. Доступність навіть на звичайних персональних комп’ютерах;


2. Обробка даних в near real-time форматі;
3. Чудовий Користувацький Досвід;
4. Просте налаштування та масштабування;
5. мінімізація розходів на виробництво продукту.

6.1.1. Обґрунтування функцій програмного продукту


Головна функція F0 – розробка інструменту для автоматичного
масштабування в Kubernetes кластері:

F1 – вибір мови програмування;

F2 – вибір алгоритмів машинного навчання;

F3 – вибір метрик оцінки роботи системи;


63

Кожна з функцій має кілька варіантів реалізації:

Функція F1:

1. Python
2. Golang

Функція F2:

1. GCP
2. AWS
3. Microsoft Azure

Функція F3:

1. MSE
2. MAPE

6.1.2. Варіанти реалізацій основних функцій


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

Рис. 6.1 Морфологічна мапа опцій реалізації функцій


За допомогою морфологічної карти можна побудувати Таблицю з
позитивно-негативним варіантами основних функцій (Таблиця 1).

Таблиця 1 – Позитивно-негативна матриця варіантів основних функцій


Варіант
Функція Переваги Недоліки
реалізації
А Простота використання Низька Швидкість
F1
Б Швидкість Простота використання
А Зручність сервісів Висока Ціна
Б Різноманіття сервісів Важкість в налаштуванні
F2
Інтегрованість з екосистемою Погано інтегрується з
В
Microsoft іншими сервісами
А Покарання великих помилок Час підрахунку
F3 Можливі проблеми з
Б Загальна оцінка помилок
нульовими значеннями

Проаналізувавши таблицю 1 можна дійти до висновків щодо того, які


варіанти функцій потрібно виключити для полегшення подальшого аналізу
через їхні незадовільні показники.
65

Функція F1:

Варто відкинути варіант Б оскільки продукт експериментальний і йому не


потрібно бути надзвичайно швидким

Функція F3:

Варто відкинути варіант В, оскільки його переваги неможливо


використати у існуючій задачі

Отже, буде розглянуто наступні варіанти:

– F1(A) – F2(A) – F3(A)


– F1(A) – F2(A) – F3(Б)
– F1(А) – F2(Б) – F3(А)
– F1(А) – F2(Б) – F3(Б)

6.2. Обґрунтування системи параметрів програмного


продукту
6.2.1. Опис параметрів
Базуючись на вимогах до основних функцій, визначаються необхідні
параметри виробу.

Для створення характеристики щодо програмного продукту будуть


використовуватись наступні параметри:

– X1 – Швидкість виконання мови;


– X2 – об’єм пам’яті для обчислення та збереження даних;
– X3 – час навчання даних.
– Х4 – потенційний об’єм програмного коду
66

6.2.2. Кількісна оцінка параметрів

Таблиця 2 – Основні параметри програмного продукту


Значення параметра
Умовні Одиниці
Опис параметру середн
позначення виміру гірші кращі
і
Швидкість виконання мови X1 Оп/мс 40 90 110
Об’єм пам’яті для обчислення
X2 МБ 65 50 20
та збереження даних
Час навчання даних X3 мс 100 70 50
Кількіст
Потенційний об’єм
Х4 ь рядків 90 25 15
програмного коду
коду

За даними таблиці будуються графічні характеристики параметрів

X1 оп/мс
120

100

80

60

40

20

0
1 2 3

Рис. 6.1 Швидкість виконання мови


67

X2 Мб
66

64

62

60

58

56

54

52

50
1 2 3

Рис. 6.2 Об’єм пам’яті для обчислення та збереження даних

X3 мс
120

100

80

60

40

20

0
1 2 3

Рис. 6.3 Час навчання даних


68

X4 с
95

90

85

80

75

70

65

60

55

50
1 2 3

Рис. 6.4 Потенційний об’єм програмного коду

6.2.3. Аналіз експертного оцінювання параметрів


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

Важливість кожного з параметрів визначається за допомогою

Значимість кожного параметра визначається методом попарного


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

1) визначення рівня значимості параметра шляхом присвоєння різних рангів;


2) перевірку придатності експертних оцінок для подальшого використання;
3) визначення оцінки попарного пріоритету параметрів;
4) обробку результатів та визначення коефіцієнту значимості.
Результати експертного ранжування наведені нище.

Таблиця 3 – Результати ранжування показників


Ранг параметра за оцінкою експерта Відхиленн
Парам Сума я, 2
Δi
етр 1 2 3 4 5 6 7 рангів Δi

X1 1 2 3 4 1 2 3 16 -1,5 2,25
69

X2 4 1 2 3 4 1 2 17 -0,5 0,25
X3 3 4 1 2 3 4 1 18 0,5 0,25
X4 2 3 4 1 2 3 4 19 1,5 2,25
Разом 10 10 10 10 10 10 10 70 0 5
Для погодження достовірності експертних оцінок потрібно знайти
параметри:

а) сума рангів кожного з параметрів і загальна сума рангів:


N
Ri=∑ ¿ r ij =70 ,
j=1

де r ij – ранг і-го параметра, визначений j-м експертом;

N – число експертів.

б) середня сума рангів T:

1
T = Ri =17.5
n

в) відхилення суми рангів кожного параметра від середньої суми рангів:

Δ i=Ri−T .

г) загальна сума квадратів відхилення:


n
S=∑ ¿ ∆2i =5.
i=1

д) коефіцієнт конкордації:

12 S 12 ∙5
W= = =0,878>W k =0 , 67.
N ( n −n )
2 3
7 ( 33−3 )
2

Ранжирування є достовірним, оскільки підрахований коефіцієнт


узгодженості перевищує нормативний рівний 0,67. Числове значення, що
визначає ступінь переваги i-го параметра над j-тим, a ij визначається за
формулою:

a ij={1 ,5 x i > x j ; 1, 0 x i=x j ; 0 , 5 x i < x j .


70

Таблиця 4 – Результати ранжування параметрів

Параме Експерти Підсумкова


три 1 2 3 4 5 6 7 оцінка
X1,X2 > > > > > > > >
X1,X3 > > > > > > > >
X1,X4 > > > > > > > >
X2,X3 > > > > > > > >
X2,X4 > < > > < < > >
X3,X4 > > > > > > > >

З введених переваг отримуємо матрицю A=‖aij ‖. Для кожного параметру


розраховується вагомість K B за формулою:
i

bi
KB= i n
,
∑ ¿ bi
i=1

де b i=∑ aij – вагомість і-го параметра за результатами оцінок всіх


j=1

експертів;
a ij –коефіцієнт переваги i-го на j-тим параметром.

'
bi
KB= i n
,
∑ ¿b '
i
i=1

де b 'i=∑ ¿ aij b j.
j=1

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


не перевищує 2%, тому додаткові ітерації не потрібні.

Таблиця 5 – Розрахунок вагомості параметрів


j Перша ітерація Друга ітерація
i Bi KB 1
Bi
1
KB
X1 X2 X3 i i

X1 1 1,5 1,5 1,5 5,5 0,344 21,25


X2 0,5 1 0,5 1,5 3,5 0,219 12,25
X3 0,5 1,5 1 1,5 4,5 0,281 16,25
71

Разом 16 1 59 1

6.3. Аналіз рівня якості варіантів реалізації функцій


Рівень якості кожного варіанту виконання основних функцій
визначається окремо.

Абсолютні значення параметрів X1(об’єм пам’яті для збереження даних)


та Х2 (середній час) відповідають технічним вимогам умов функціонування
даного ПП.

Абсолютне значення параметра Х3 (кількість строк коду) обрано не


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

Коефіцієнт технічного рівня якості для кожного варіанта реалізації ПП


розраховується за формулою:
n
K TP =∑ ¿ K B Bi ,i
i=1

де n – кількість параметрів;
K B – коефіцієнт вагомості i–го параметра;
i

Bi – оцінка i–го параметра в балах.

Розрахунок показників рівня якості представлено відповідно в таблиці.

Таблиця 6 – Розрахунок показників якості


Основні Варіант Абсолютне Бальна Коефіцієнт Коефіцієнт
функції реалізації значення оцінка вагомості рівня
параметра параметра параметра якості
F1 А 95 80 0.25 12.3
F2 А 55 50 0.3 4.5
Б 79 70 0.4 6.7
F3 А 60 60 0.5 8.9
Б 50 50 0.6 1.2

За цими даними визначаємо рівень якості кожного з варіантів:


72

– F1(A) – F2(A) – F3(A) = 12.3 + 4.5 + 8.9 = 25.7

Тобто найкраща комбінація це Python – GCP - MSE

Отже, найкращим є перший варіант, для якого коефіцієнт технічного


рівня має найбільше значення.

6.4. Обґрунтування системи параметрів програмного


продукту
Для визначення вартості розробки програмного продукту спочатку
проведемо розрахунок трудомісткості.

Всі варіанти включають в себе два окремих завдання:

1. Розробка проекту програмного продукту;

2. Розробка алгоритму збору даних.

Завдання 1 за ступенем новизни відноситься до групи В, завдання 2 – до


групи Г. За складністю алгоритми, які використовуються в завданні 1
належать до групи 2; а в завданні 2 – до групи 3.

Для реалізації завдання 1 використовує інформацію у вигляді даних, а


завдання 2 використовує стандартні методи збору даних. Проведемо
розрахунок норм часу на розробку та програмування для кожного з завдань.
Загальна трудомісткість обчислюється за формулою.

Т О=Т Р ∙ К П ∙ К СК ∙ К М ∙ К СТ ∙ К СТ . М

де Т Р – трудомісткість розробки програмного продукту;

К П – поправочний коефіцієнт;

К СК – коефіцієнт на складність вхідної інформації;

К М – коефіцієнт рівня мови програмування;

К СТ – коефіцієнт використання стандартних модулів і прикладних


73

програм;
К СТ . М – коефіцієнт стандартного математичного забезпечення.

Для першого завдання, виходячи із норм часу для завдань розрахункового


характеру степеню новизни В та групи складності алгоритму 2,
трудомісткість дорівнює: Т Р =28 людино-днів. Поправочний коефіцієнт, який
враховує вид вхідної інформації для першого завдання: К П =1 ,2. Поправочний
коефіцієнт, який враховує складність контролю вхідної та вихідної
інформації рівний К СК =1. Оскільки при розробці першого завдання
використовуються стандартні модулі, врахуємо це за допомогою коефіцієнта
К СТ =0 ,8 . Тоді загальна трудомісткість програмування першого завдання

дорівнює:

Т 1=28 ∙1.2 ∙ 0.8=26 , 88 людино-днів.

Проведемо аналогічні розрахунки для другого завдання, в якому


використовується алгоритм третьої групи складності зі ступенем новизни Г,
тобто Т Р =8 людино-днів, К П =0 , 6, К СК =1, К СТ =0 ,8:

Т 2=8 ∙ 0.6 ∙0.8=3 , 84 людино-днів.

Оскільки загальна трудомісткість усіх варіантів реалізації збігаються, їх


можна об’єднати в одну групу.

Загальна трудомісткість складає:

Т О=( 26 , 88+3 ,84 ) ∙ 8=245 , 76 людино-годин;

Команда розробки складається з: Software Engineer з зарплатне. 300000


грн., Test Engineer з зарплатнею 40000 грн, та Data Engineer з зарплатнею
100000 грн. Визначимо середню зарплату за годину за формулою:
74

M
С Ч= грн .,
T m∙ t

де М – місячний оклад працівників;


T m – кількість робочих днів на місяць;

𝑡 – кількість робочих годин в день.

3 0 0 000+ 40000+1 00 000


С Ч= =916 , 66 грн .
3 ∙20 ∙ 8

Тоді, розрахуємо заробітну плату за формулою:

С ЗП =С Ч ∙T i ∙ К Д ,

де С Ч – величина погодинної оплати праці програміста;

T i – трудомісткість відповідного завдання;

К Д – норматив, який враховує додаткову заробітну плату.

Зарплата Software Engineer за варіантами становить:

С ЗП =916 , 66 ∙ 245 ,76 ∙ 1 , 2=43277 ,51 грн .

Налогові зняття становлять 22%:

С СВ=С ЗП ∙ 0 ,22=43277 , 51∙ 0 , 22=9521, 05

Наступним кроком потрібно підрахувати вартість техніки на гоодину.


Оскільки одна ЕОМ обслуговується одним інженером апаратного
забезпечення з окладом 22000 грн. та коефіцієнтом зайнятості К З=0 , 2 то для
однієї машини отримаємо:

С Г =12∙ М ∙ К З=12 ∙22 000 ∙ 0 ,2=52800 грн .

З урахуванням додаткової заробітної плати:

С ЗП =С Г ∙ ( 1+ К З ) =52800 ∙ ( 1+0 ,2 ) =63360 грн .

Відрахування на соціальний внесок становить 22%:

С СВ=С ЗП ∙ 0 ,22=57 600∙ 0 , 22=12038 грн .


75

Амортизаційні відрахування розраховуємо за формулою при амортизації


25% та вартості ЕОМ – 60 000 грн.:

С А=К ТМ ∙ К А ∙ Ц ПР =1, 15 ∙ 0 ,25 ∙ 60000=17250 грн .

де К ТМ – коефіцієнт, який враховує витрати на транспортування та монтаж

приладу у користувача;
К А – річна норма амортизації;

Ц ПР – договірна ціна приладу.

Витрати на ремонт та профілактику розраховуємо за формулою:

С Р =К ТМ ∙ К Р ∙ Ц ПР=1,155 ∙ 0 ,05 ∙ 6034000=345340 грн .

де К Р– відсоток витрат на поточні ремонти.

Ефективний годинний фонд часу ПК за рік розраховуємо за формулою:

Т ЕФ =( Д К − Д В− Д С − Д Р ) ∙ t ∙ К В , Т ЕФ =( 365−104−12−164 ) ∙ 8 ∙ 0 , 91=1 67785 ,6 год .

де Д К – календарна кількість днів у році;

Д В , Д С – відповідно кількість вихідних та святкових днів;

Д Р – кількість днів планових ремонтів устаткування;

t – кількість робочих годин в день;


К В – коефіцієнт використання приладу у часі протягом зміни.

Витрати на оплату електроенергії розраховуємо за формулою:

С ЕЛ =Т ЕФ ∙ N C ∙ К З ∙ Ц ЕЛ =1 577 , 6 ∙ 0 ,65 ∙ 0 , 23 ∙ 4,4584=758 ,80 грн .

де N C – середньо-споживча потужність приладу;

К З – коефіцієнтом зайнятості приладу;

Ц ЕЛ – тариф за 1 КВт-годин електроенергії.

Накладні витрати розраховуємо за формулою:

С Н =Ц ПР ∙0,655=61 050 ∙0,597=41203 грн .

Тоді, річні експлуатаційні витрати будуть складати:


76

С ЕК =С ЗП +С СВ +С А +С Р+ С ЕЛ +С Н =6345360+1234534038+17253450+345450+753 , 80+ 40200=1

Собівартість однієї машино-години ЕОМ дорівнюватиме:

С ЕК 13705451.80
С МГ = = =50 , 70 грн /год .
Т ЕФ 1 67567 , 6

Оскільки в даному випадку всі роботи, які пов‘язані з розробкою


програмного продукту ведуться на ЕОМ, витрати на оплату машинного часу
складають:

С М =С МГ ∙ Т =84 ,60 ∙ 284 , 43=1445431 , 49 грн .

Накладні витрати складають 67% від заробітної плати:

С Н =С ЗП ∙0 , 67=43277 ,51 ∙ 0 ,67=28995 , 93 грн .

Отже, вартість розробки програмного продукту за варіантами становить:

С ПП =С ЗП +С СВ +С М +С Н =43277 , 51+9521 , 05+14431 , 49+28995 , 93=96225.98 грн .

Розрахуємо коефіцієнт техніко-економічного рівня за формулою:

КК 8,4 −5
К ТЕР = = =8 , 72 ∙10
С ПП 96225.98

6.5. Висновки до розділу 6


На цьому етапі було проведено комплексний аналіз програмного
продукту, охоплюючи як його функціональність, так і відповідні витрати.
Було ретельно оцінено основні характеристики програмного забезпечення та
вивчено різні параметри, що визначають його характеристики.
77

Після детального функціонального та вартісного аналізу програмного


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

Як результат аналізу було обрано наступний набір функцій:

– Python
– GCP
– MSE

ВИСНОВКИ
У ході виконання дослідження було глибоко розглянуто тему автоматичного
масштабування та необхідності в даному інструменті. На мою думку, для
кожної задачі існує певна стратегія автоматичного масштабування, яка
78

дозволить зробити сервіс гнучким до змін. І хоча було розглянуто лише


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

Раніше було очевидним, що найкращим способом витримати велике


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

Проте, слід зауважити, що ключ до успішного використання даного


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

Вся відповідальність лежить на людині, яка носить титул Software Engineer.


Маючи у розпорядженні способи передбачення та постійної корекції в
реальному часу відносно заданих метрик, з різними способами додавання та
видалення допоміжних ресурсів, можливо дуже тонко та акуратно
налаштувати масштабування, враховуючи всі деталі.
79

СПИСОК ВИКОРИСТАНИХ ДЖЕРЕЛ

[1] “What are microservices” HYPERLINK


"https://microservices.io/"https://microservices.io/ (accessed June 10, 2023)

[2] “Cloud computing with AWS” https://aws.amazon.com/what-is-aws (accessed


June 10, 2023)

[3] “Google Cloud overview” https://cloud.google.com/docs/overview (accessed


June 10, 2023)

[4] “Get to know Azure” https://azure.microsoft.com/en-us/explore (accessed June


10, 2023)

[5] “Hybrid and multicloud solutions”


https://azure.microsoft.com/en-us/solutions/hybrid-cloud-app/ (accessed June
10, 2023)

[6] “IaaS vs. PaaS vs. SaaS”


https://www.redhat.com/en/topics/cloud-computing/iaas-vs-paas-vs-saas
(accessed June 10, 2023)

[7] “Production-Grade Container Orchestration” https://kubernetes.io/ (accessed


June 10, 2023)

[8] “Dockerfile reference” https://docs.docker.com/engine/reference/builder/


(accessed June 10, 2023)

[9] “Horizontal Pod Autoscaling” https://kubernetes.io/docs/tasks/run-


application/horizontal-pod-autoscale/ (accessed June 10, 2023)

[10] “What is the CAP theorem?” https://www.ibm.com/topics/cap-theorem


(accessed June 10, 2023)
80

[11]“Kubernetes Networking Fundamentals”


https://www.techbeatly.com/kubernetes-networking-fundamentals/ (accessed
June 10, 2023)

[12] “Predictive Horizontal Pod Autoscaler”


https://github.com/jthomperoo/predictive-horizontal-pod-autoscaler
(accessed June 10, 2023)

[13] “Holt-Winters’ seasonal method” https://otexts.com/fpp2/holt-winters.html


(accessed June 10, 2023)

[14] “Linear Regression”


http://www.stat.yale.edu/Courses/1997-98/101/linreg.htm (accessed June 10, 2023)

[15] “KEDA Concepts” https://keda.sh/docs/2.10/concepts/#architecture (accessed


June 10, 2023)

[16] “Messaging that just works — RabbitMQ” https://www.rabbitmq.com/


(accessed June 10, 2023)

[17] “Predictive scaling for Amazon EC2 Auto Scaling”


https://docs.aws.amazon.com/autoscaling/ec2/userguide/ec2-auto-scaling-
predictive-scaling.html (accessed June 10, 2023)

[18] “Time series” https://en.wikipedia.org/wiki/Time_series (accessed June 10,


2023)

[19] “Time Series Forecasting — A Complete Guide”


https://medium.com/analytics-vidhya/time-series-forecasting-a-complete-guide-
d963142da33f (accessed June 10, 2023)

[20] “Stationary process” https://en.wikipedia.org/wiki/Stationary_process


(accessed June 10, 2023)

[21] “Exponential smoothing”


https://en.wikipedia.org/wiki/Exponential_smoothing (accessed June 10, 2023)
81

[22] “Moving average” https://en.wikipedia.org/wiki/Moving_average (accessed


June 10, 2023)

[23] “Time Series Forecasting with ARIMA , SARIMA and SARIMAX”


https://towardsdatascience.com/time-series-forecasting-with-arima-sarima-and-
sarimax-ee61099e78f6 (accessed June 10, 2023)

[24] “Moving-average model” https://en.wikipedia.org/wiki/Moving-


average_model (accessed June 10, 2023)

[25] “XGBoost: A Scalable Tree Boosting System”


https://arxiv.org/pdf/1603.02754.pdf (accessed June 10, 2023)

[26] “XGBoost Algorithm: Long May She Reign!”


https://towardsdatascience.com/https-medium-com-vishalmorde-xgboost-
algorithm-long-she-may-rein-edd9f99be63d

[27] “Decision Trees” https://scikit-learn.org/stable/modules/tree.html (accessed


June 10, 2023)

[28] “Bootstrap aggregating” https://en.wikipedia.org/wiki/Bootstrap_aggregating


(accessed June 10, 2023)

[29] “Boosting (machine learning)”


https://en.wikipedia.org/wiki/Boosting_(machine_learning) (accessed June 10,
2023)

[30] “Recurrent neural network”


https://en.wikipedia.org/wiki/Recurrent_neural_network (accessed June 10, 2023)

[31] “FastAPI” https://fastapi.tiangolo.com/ (accessed June 10, 2023)

[32] Polling (computer science)


https://en.wikipedia.org/wiki/Polling_(computer_science) (accessed June 10, 2023)
82

ДОДАТОК
Metrics Extractor Service
import datetime
import time
from functools import reduce

import pandas as pd
import pika
import typer

import kubernetes_helper

app = typer.Typer()
DEPLOYMENT_NAME = "intelliscale-busy-server"

@app.command()
def run_metrics_collection(step_timeout: int):
metrics_api = kubernetes_helper.create_kubernetes_metrics_api_client()
connection =
pika.BlockingConnection(pika.ConnectionParameters(host="localhost"))
channel = connection.channel()
channel.queue_declare(queue="metrics")

if metrics_api is None:
print("API was not initialized. Exiting...")
exit(1)
while True:
raw_metrics = kubernetes_helper.get_pods_metrics(
deployment_name=DEPLOYMENT_NAME, api=metrics_api
)
pods_count = len(raw_metrics)
pods_metrics = [
{"cpu": pod["usage"]["cpu"], "memory": pod["usage"]["memory"]}
for pod in raw_metrics
]
cpu = reduce(
lambda x, _: x + _,
map(
lambda pod:
kubernetes_helper.convert_cpu_metric(pod["cpu"]),
pods_metrics,
),
)
memory = reduce(
lambda x, _: x + _,
map(
lambda pod:
kubernetes_helper.convert_memory_metric(pod["memory"]),
pods_metrics,
),
)
cpu_utilization = cpu / (pods_count *
kubernetes_helper.LIMITS["cpu"])
83
memory_utilization = memory / (pods_count *
kubernetes_helper.LIMITS["memory"])
metrics = [
str(pods_count),
str(cpu),
str(memory),
str(cpu_utilization),
str(memory_utilization),
str(datetime.datetime.now()),
]
message = ",".join(metrics)
channel.basic_publish(exchange="", routing_key="metrics",
body=message)
print(" [x] Sent %r" % message)

time.sleep(step_timeout)

if __name__ == "__main__":
app()

Scaler Service
import datetime
import time

import pandas as pd
import pika
import typer

import kubernetes_helper
import ml_algorithms
import rule_based_algorithms

app = typer.Typer()
DEPLOYMENT_NAME: str = "intelliscale-busy-server"

@app.command()
def ping():
print("Pong")

# @app.command()
def main(
polling_timeout: float,
mode: str,
upper_cpu_threshold: float = 0.8,
bottom_cpu_threshold: float = 0.1,
scaling_period: int = 60,
backup_timeout: int = 30,
awaiting_timeout: int = 60,
metric_log_filename: str = "metrics_log.csv",
):
metrics_df = pd.DataFrame(
columns=[
84
"pods_count",
"cpu",
"memory",
"cpu_utilization",
"memory_utilization",
"timestamp",
]
) # type: ignore
apps_api = kubernetes_helper.create_kubernetes_apps_api_client()
if apps_api is None:
print("API was not initialized. Exiting...")
exit(1)
connection =
pika.BlockingConnection(pika.ConnectionParameters(host="localhost"))
channel = connection.channel()
channel.queue_declare(queue="metrics")
current_pods_count = 0
desired_pods_count = 0
last_backup_timestamp = datetime.datetime.now()
last_modification_timestamp = None
while True:
current_timestamp = datetime.datetime.now()
print("Polling...")
_, _, raw_metrics = channel.basic_get(queue="metrics",
auto_ack=True)
if raw_metrics:
metrics = raw_metrics.decode().split(",")
print(f"Receiving metric {metrics=}")
(
pods_count,
cpu,
memory,
cpu_utilization,
memory_utilization,
timestamp,
) = metrics
current_pods_count = int(pods_count)

metrics_df.loc[len(metrics_df.index)] = [ # type: ignore


int(pods_count),
float(cpu),
float(memory),
float(cpu_utilization),
float(memory_utilization),
timestamp,
]
if mode == "rule-based":
desired_pods_count =
rule_based_algorithms.calculate_pods_count(
pods_count=int(pods_count),
metrics_df=metrics_df,
upper_threshold=upper_cpu_threshold,
bottom_threshold=bottom_cpu_threshold,
scaling_period=scaling_period,
)
elif mode == "ml-based":
if len(metrics_df) > 10:
desired_pods_count =
ml_algorithms.calculate_pods_count(
df=metrics_df
85
)
print(f"ML based desired pods count:
{desired_pods_count}")

if desired_pods_count != current_pods_count:
if last_modification_timestamp is not None:
if (
current_timestamp - last_modification_timestamp
).seconds > awaiting_timeout:
kubernetes_helper.update_deployment(
api=apps_api,
deployment_name=DEPLOYMENT_NAME,
replicas_count=desired_pods_count,
)
last_modification_timestamp =
datetime.datetime.now()
if (current_timestamp - last_backup_timestamp).seconds >
backup_timeout:
last_backup_timestamp = current_timestamp
metrics_df.to_csv(metric_log_filename, sep=",", index=False,
mode="w")
# metrics_df.drop(metrics_df.index, inplace=True)

time.sleep(polling_timeout)

if __name__ == "__main__":
main(1.0, "rule-based", 0.8, 60)

Model Trainer Service


import datetime
import pickle

import matplotlib.pyplot as plt


import numpy as np
import pandas as pd
import seaborn as sns
import tensorflow as tf
import xgboost as xgb
from keras.callbacks import ModelCheckpoint
from keras.layers import *
from keras.losses import MeanSquaredError
from keras.metrics import RootMeanSquaredError
from keras.models import Sequential
from keras.optimizers import Adam
from sklearn.metrics import mean_absolute_error
from sklearn.model_selection import (GridSearchCV, TimeSeriesSplit,
train_test_split)
from sklearn.multioutput import MultiOutputRegressor
from statsmodels.tsa.statespace.sarimax import SARIMAX

import utils

sns.set()
86

from sklearn.metrics import (mean_absolute_error,


mean_absolute_percentage_error,
mean_squared_error)

def evaluate_model(y_test, prediction):


print(f"MAE: {mean_absolute_error(y_test, prediction)}")
print(f"MSE: {mean_squared_error(y_test, prediction)}")
print(f"MAPE: {mean_absolute_percentage_error(y_test, prediction)}")

def plot_predictions(testing_dates, y_test, prediction):


df_test = pd.DataFrame({"timestamp": testing_dates, "actual": y_test,
"prediction": prediction })
figure, ax = plt.subplots(figsize=(10, 5))
plt.plot(df_test.index, df_test["prediction"].rolling(3).mean(),
label="prediction")
plt.plot(df_test.index, df_test["actual"].rolling(3).mean(),
label="actual")
plt.legend(["Actual", "Prediction"])
plt.show()

def generate_xgboost_model():
df = pd.read_csv("metrics_log_fixed.csv")

# df["cpu"] = df["cpu"].rolling(100).mean()
df = df.dropna()
df["timestamp"] = pd.to_datetime(df["timestamp"], format="%Y-%m-%d %H:
%M:%S.%f") # type: ignore
train_df = df[df["timestamp"] < str("2023-06-10 22:00:29.140694")]
test_df = df[df["timestamp"] > str("2023-06-10 22:00:29.140694")]
plt.plot(train_df["timestamp"], train_df["cpu"], label="Training", )
plt.plot(test_df["timestamp"], test_df["cpu"], label="Testing", )
train_df["hour"] = train_df["timestamp"].dt.hour
train_df["minute"] = train_df["timestamp"].dt.minute
train_df["second"] = train_df["timestamp"].dt.second
train_df["microsecond"] = train_df["timestamp"].dt.microsecond

test_df["hour"] = test_df["timestamp"].dt.hour
test_df["minute"] = test_df["timestamp"].dt.minute
test_df["second"] = test_df["timestamp"].dt.second
test_df["microsecond"] = test_df["timestamp"].dt.microsecond

x_train = train_df[["hour", "minute", "second", "microsecond"]]


y_train = train_df["cpu"]
x_test = test_df[["hour", "minute", "second", "microsecond"]]
y_test = test_df["cpu"]
model = xgb.XGBRegressor(
n_estimators=1000,
max_depth=9,
subsample=0.1,
min_child_weight=1,
learning_rate=0.5,
colsample_bytree=0.9,
objective="reg:pseudohubererror", # reg:pseudohubererror
tree_method="exact",
)

trained_model = model.fit(x_train, y_train) # type: ignore


87

prediction = trained_model.predict(x_test)
evaluate_model(y_test, prediction)
with open("xgboost_model.pkl", "wb") as file_obj:
pickle.dump(trained_model, file_obj) # pickle.load(), "rb"

def df_to_X_y(df, window_size=5):


df_as_np = df.to_numpy()
X = []
y = []
for i in range(len(df_as_np)-window_size):
row = [[a] for a in df_as_np[i:i+window_size]]
X.append(row)
label = df_as_np[i+window_size]
y.append(label)
return np.array(X), np.array(y)

def generate_lstm_model():
df = pd.read_csv("metrics_log.csv")
df = df[["cpu", "timestamp"]]
df["timestamp"] = pd.to_datetime(df["timestamp"], format="%Y-%m-%d %H:
%M:%S.%f")
df.index = df["timestamp"] # type: ignore
cpu = df["cpu"]
WINDOW_SIZE = 5
X1, y1 = df_to_X_y(cpu, WINDOW_SIZE)
dataset_length = len(X1)
train_split = int(0.7*dataset_length)
validation_split = train_split + int(0.15*dataset_length)
X_train1, y_train1 = X1[:train_split], y1[:train_split] # TODO: issue
van be here
X_val1, y_val1 = X1[train_split:validation_split],
y1[train_split:validation_split]
X_test1, y_test1 = X1[validation_split:], y1[validation_split:]
model1 = Sequential()
model1.add(InputLayer((5, 1)))
model1.add(LSTM(64))
model1.add(Dense(8, "relu"))
model1.add(Dense(1, "linear"))
model1.compile(loss=MeanSquaredError(),
optimizer=Adam(learning_rate=0.0001), metrics=[RootMeanSquaredError()])
model1.fit(X_train1, y_train1, validation_data=(X_val1, y_val1),
epochs=10)
cpu_predictions = model1.predict(X1).flatten()
test_results = pd.DataFrame(data={"Test Predictions":cpu_predictions,
"Actuals":y1})
evaluate_model(y_test=test_results["Actuals"],
prediction=test_results["Test Predictions"])

model1.compile(loss=MeanSquaredError(),
optimizer=Adam(learning_rate=0.0001), metrics=[RootMeanSquaredError()])
model1.fit(X1, y1, epochs=10)

model1.save("lstm_model")

generate_lstm_model()
88

ML Predictor
import datetime
import os
import pickle

import numpy as np

os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3"

import logging

import matplotlib.pyplot as plt


import pandas as pd
import tensorflow as tf
from keras.models import load_model
from sklearn.metrics import (
mean_absolute_error,
mean_absolute_percentage_error,
mean_squared_error,
)

import utils

tf.get_logger().setLevel(logging.ERROR)

def evaluate_model(y_test, prediction):


print(f"MAE: {mean_absolute_error(y_test, prediction)}")
print(f"MSE: {mean_squared_error(y_test, prediction)}")
print(f"MAPE: {mean_absolute_percentage_error(y_test, prediction)}")

def df_to_X_y(df, window_size=5):


df_as_np = df.to_numpy()
X = []
y = []
for i in range(len(df_as_np) - window_size):
row = [[a] for a in df_as_np[i : i + window_size]]
X.append(row)
label = df_as_np[i + window_size]
y.append(label)
return np.array(X), np.array(y)

def calculate_pods_count(df: pd.DataFrame, threshold: float = 0.8) -> int:


model1 = load_model("lstm_model/")
cpu = df["cpu"]
WINDOW_SIZE = 5
X1, y1 = df_to_X_y(cpu, WINDOW_SIZE)
X1 = [
[
list(X1[-1][0]),
list(X1[-1][1]),
list(X1[-1][2]),
89
list(X1[-1][3]),
list(X1[-1][4]),
]
]
y1 = y1[-1]
print(f"Last 5 CPU Values: {X1}")
print(y1)
cpu_predictions = model1.predict(X1).flatten()
print(f"Predicted next CPU value {cpu_predictions}")
pods_to_add = int(cpu_predictions[0] / threshold)

return pods_to_add + 1

Rule Based Predictor


import datetime

import pandas as pd

def calculate_pods_count(
pods_count: int,
metrics_df: pd.DataFrame,
upper_threshold: float,
bottom_threshold: float,
scaling_period: int,
) -> int:
"""
Receiving pandas DataFrame
pd.DataFrame(
columns=[
"pods_count",
"cpu",
"memory",
"cpu_utilization",
"memory_utilization",
"timestamp",
]
)
"""
last_timestamp = datetime.datetime.strptime(
metrics_df["timestamp"].max(), "%Y-%m-%d %H:%M:%S.%f"
) - datetime.timedelta(seconds=scaling_period)

df = metrics_df[metrics_df["timestamp"] > str(last_timestamp)]


mean_cpu_utilization = df["cpu_utilization"].mean()
print(mean_cpu_utilization)
if mean_cpu_utilization > upper_threshold:
return pods_count + 1
elif mean_cpu_utilization < bottom_threshold and pods_count > 1:
return pods_count - 1
else:
return pods_count
90

Testing Service
import concurrent.futures
import os
import time

import matplotlib.pyplot as plt


import numpy as np
import requests
import typer

BASE_URL = os.getenv("BASE_URL", "http://34.27.213.44")

app = typer.Typer()

@app.command()
def run_stress_load_campaign(requests_count: int):
print("Running stress load campaign")
start = time.time()
destination_url = f"{BASE_URL}/matrix/{100}"
for _ in range(requests_count):
response = requests.get(destination_url)
print(response.text)
print(f"Total execution time: {time.time() - start}")

@app.command()
def run_parallel_stress_load_campaign(requests_count: int):
print("Running stress load campaign")
start = time.time()
destination_url = f"{BASE_URL}/matrix/{100}"
with concurrent.futures.ThreadPoolExecutor(max_workers=8) as executor:
futures = []
for _ in range(requests_count):
futures.append(executor.submit(requests.get,
url=destination_url))
for future in concurrent.futures.as_completed(futures):
future.result().text
print(f"Total execution time: {time.time() - start}")

@app.command()
def run_cyclic_stress_load_campaign(resolution: int, cycles: int):
print("Running cyclic load campaign")
length = np.pi * 2 * cycles
sin_wave = np.sin(np.arange(0, length, length / resolution))
start = time.time()
requests_strategy = abs((sin_wave*30).astype(int))
print(requests_strategy)
destination_url = f"{BASE_URL}/matrix/{100}"
with concurrent.futures.ThreadPoolExecutor(max_workers=8) as executor:
futures = []
for requests_count in requests_strategy:
for _ in range(requests_count):
futures.append(executor.submit(requests.get,
url=destination_url))
for future in concurrent.futures.as_completed(futures):
print(future.result().text)
time.sleep(2)
91
print(f"Total execution time: {time.time() - start}")

@app.command()
def run_custom_stress_load_campaign():
print("Running cyclic load campaign")
start = time.time()
requests_strategy = [(50, 1), (300, 8), (500, 16), (600, 24)]
print(requests_strategy)
destination_url = f"{BASE_URL}/matrix/{100}"
for requests_count, threads_count in requests_strategy:
print(f"!!!!!! Running with params {requests_count=},
{threads_count=}")
with
concurrent.futures.ThreadPoolExecutor(max_workers=threads_count) as
executor:
futures = []
for _ in range(requests_count):
futures.append(executor.submit(requests.get,
url=destination_url))
for future in concurrent.futures.as_completed(futures):
future.result().text
time.sleep(10)
print(f"Total execution time: {time.time() - start}")

@app.command()
def run_custom_pods_load_campaign():
print("Running cyclic load campaign")
start = time.time()
requests_strategy = [(2000, 4), (2000, 8), (2000, 16), (2000, 24),
(2000, 32), (2000, 40), (2000, 48)]
print(requests_strategy)
destination_url = f"{BASE_URL}/matrix/{100}"
for requests_count, threads_count in requests_strategy:
print(f"!!!!!! Running with params {requests_count=},
{threads_count=}")
with
concurrent.futures.ThreadPoolExecutor(max_workers=threads_count) as
executor:
futures = []
for _ in range(requests_count):
futures.append(executor.submit(requests.get,
url=destination_url))
for future in concurrent.futures.as_completed(futures):
future.result().text
time.sleep(20)
print(f"Total execution time: {time.time() - start}")

if __name__ == "__main__":
app()

Kubernetes Helper
import typing

from kubernetes import client, config


92

MEMORY_TRANSFORM_TABLE = {
"Ei": 1_152_921_504_606_846_976,
"Pi": 1_125_899_906_842_624,
"Ti": 1_099_511_627_776,
"Gi": 1_073_741_824,
"Mi": 1_048_576,
"Ki": 1_024,
}

CPU_TRANSFORM_TABLE: typing.Dict[str, int] = {"m": 1000, "n": 1000000000}

LIMITS = {
"cpu": 1.0,
"memory": 1_073_741_824.0,
}

def create_kubernetes_metrics_api_client() ->


typing.Optional[client.CustomObjectsApi]:
print("Initiating client connection...")
api = None
try:
config.load_kube_config()
api = client.CustomObjectsApi()
except Exception:
print("Connection failed :-(")
else:
print("Connection Succeeded!")

return api

def create_kubernetes_apps_api_client() ->


typing.Optional[client.AppsV1Api]:
print("Initiating client connection...")
api = None
try:
config.load_kube_config()
api = client.AppsV1Api()
except Exception:
print("Connection failed :-(")
else:
print("Connection Succeeded!")

return api

def get_pods_metrics(
deployment_name: str, api: client.CustomObjectsApi
) -> typing.List[typing.Dict[str, typing.Any]]:
print(f"Extracting metrics for deployment {deployment_name}")
k8s_pods = api.list_cluster_custom_object("metrics.k8s.io", "v1beta1",
"pods")
pods_metrics = []

for pod in k8s_pods["items"]:


if deployment_name in pod["metadata"]["name"]:
pods_metrics.append(pod["containers"][0])
93
return pods_metrics

def convert_cpu_metric(pretty_cpu: str) -> float:


cpu = 0
for suffix, factor in CPU_TRANSFORM_TABLE.items():
if pretty_cpu.endswith(suffix):
cpu = int(pretty_cpu.replace(suffix, "")) / factor

return cpu

def convert_memory_metric(pretty_memory: str):


memory_bytes = 0
for suffix, factor in MEMORY_TRANSFORM_TABLE.items():
if pretty_memory.endswith(suffix):
memory_bytes = int(pretty_memory.replace(suffix, "")) * factor

return memory_bytes

def update_deployment(api: client.AppsV1Api, deployment_name: str,


replicas_count: int):
# patch the deployment
print(f"Patching {deployment_name=} with {replicas_count=}")
resp = api.patch_namespaced_deployment_scale(
name=deployment_name, namespace="default", body={"spec":
{"replicas": replicas_count}}
)

print("\n[INFO] deployment's container image updated.\n")

Busy Server

main.py
from fastapi import FastAPI

import utils
import time
import logging

app = FastAPI()
logger = logging.getLogger("uvicorn")
logger.setLevel(logging.DEBUG)

@app.get("/ping")
def ping():
return {"response": "pong"}

@app.get("/matrix/{size}")
async def matrix_multiplication(size: int):
matrix_a = utils.create_non_singular_matrix(size=size)
start = time.time()
logger.info("Creating matrix took %.5f", start - time.time())
matrix_b = matrix_a
94
start = time.time()
matrix_c = utils.multiply_matrix(matrix_a=matrix_a, matrix_b=matrix_b)
logger.info("Multiplying matrix took %.5f", start - time.time())
determinant = utils.calculate_determinant(matrix=matrix_c)
start = time.time()
logger.info("Calculating determinant of matrix took %.5f", start -
time.time())
logger.info("Determinant is %.5f", determinant)

return {"code": 200, "determinant": str(round(determinant, 5))}

utils.py
import typing
import random

def multiply_matrix(
matrix_a: typing.List[typing.List[int]], matrix_b:
typing.List[typing.List[int]]
) -> typing.List[typing.List[int]]:
c = [[] for _ in range(len(matrix_a))]
for i in range(len(matrix_a)):
for j in range(len(matrix_a)):
sum = 0
for k in range(len(matrix_a)):
sum += matrix_a[i][k] * matrix_b[k][j]
c[i].append(sum)

return c

def create_non_singular_matrix(size: int) ->


typing.List[typing.List[int]]:
matrix = [[0] * size for _ in range(size)]

for i in range(size):
for j in range(size):
matrix[i][j] = (i + 1) * (j + 1) + random.randint(-5, 5)

matrix[size - 1], matrix[size - 2] = matrix[size - 2], matrix[size -


1]

return matrix

def calculate_determinant(matrix: typing.List[typing.List[int]]) -> float:


"""
Calculate the determinant of a square matrix iteratively.
"""
n = len(matrix)

if n != len(matrix[0]):
raise ValueError("The matrix must be square.")

det = 0

temp = [row[:] for row in matrix]


95
for k in range(n - 1):
for i in range(k + 1, n):
factor = temp[i][k] / temp[k][k]
for j in range(k, n):
temp[i][j] -= factor * temp[k][j] # type: ignore

det = 1
for i in range(n):
det *= temp[i][i]

return det

You might also like