Максимізація заощаджень за рахунок «сну» ресурсів Kubernetes поза робочими годинами

pic

Ця стаття вперше була опублікована на блозі PerfectScale.

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

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

  1. Вимикання Kubernetes вузлів
  2. Вимикання Kubernetes додатків

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

У цій статті я розгляну 3 методи автоматичного вимкнення Kubernetes додатків. Останній з них — це "Бонус" для технічно підкованих.

  1. Cron Scaler
  2. Custom Metric Scaler
  3. Network Scaler*

pic

Бажаєте дізнатися більше про Event-Driven Autoscaling з KEDA? Приєднуйтесь до цікавої сесії з Збінеком Роубаліком, технічним директором Kedify і Maintainer KEDA проєкту.

Kubernetes KEDA для подійно-орієнтованого автоскейлінгу

KEDA — це автоскейлер, заснований на Kubernetes, орієнтований на події. Завдяки KEDA ви можете керувати масштабуванням будь-якого контейнера в Kubernetes залежно від кількості подій, які потрібно обробити.

Подія може бути будь-чим, що генерується дією. Це може бути API виклик, повідомлення в черзі, зміна в файловій системі тощо. У нашому випадку, під час оцінки методів масштабування до і від 0, «подія» — це або «час» сам по собі, або запит на сон. KEDA доповнює функціональність рідного Horizontal Pod Autoscaler (HPA) Kubernetes, керуючи ним.

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

Для того, щоб KEDA могла масштабувати ваші робочі навантаження до “0” в позаробочий час, ми розглянемо кілька методів. Перший метод є просто орієнтованим на час, а другий дозволяє більш детально контролювати графік сну ваших робочих навантажень. Третій метод реалізує незріле, але цікаве рішення цієї проблеми за допомогою спеціального ресурсу під назвою “Network Scaler”.

ПРИМІТКА: Приклад коду для нижченаведеного посібника доступний у нашому GitHub репозиторії.

pic

Просте рішення — Cron Scaler

Основний KEDA CRD (Custom Resource Definition) називається ScaledObject. ScaledObject визначає, скільки реплік має певне робоче навантаження (Deployment, StatefulSet тощо) в певний час. На момент написання цієї статті, Cron Scaler наразі має проблеми при визначенні того, коли є позаробочі години.
Але, він може визначати, коли є робочі години, щоб ваші робочі навантаження знали, коли повинні бути активними, а коли — спати.

Припустимо, у вас є просте розгортання, і ви виконуєте наступну команду в терміналі: kubectl apply -f deployment.yaml:

apiVersion: apps/v1  
kind: Deployment  
metadata:  
 name: sleepy-workload  
spec:  
 replicas: 1  
 selector:  
 matchLabels:  
 app: sleepy-workload  
 template:  
 metadata:  
 labels:  
 app: sleepy-workload  
 spec:  
 containers:  
 - name: busybox  
 image: busybox  
 command:  
 - sleep  
 - "3600"

Тепер давайте додамо ScaledObject KEDA CRD та прив’яжемо його до нашого сплячого робочого навантаження (ваш часовий пояс можна знайти тут). Припустимо, ви хочете, щоб це навантаження працювало з 9:00 до 17:00 за часом Нью-Йорка.

apiVersion: keda.sh/v1alpha1  
kind: ScaledObject  
metadata:  
 name: sleepy-workload-scaler  
spec:  
 scaleTargetRef:  
 # Обов'язково. Має бути в тому ж просторі імен, що й ScaledObject  
 name: sleepy-workload  
 # Інтервал опитування для перевірки подій масштабування  
 pollingInterval: 10  
 # Змушує робоче навантаження одразу заснути, коли йому наказують, замість того, щоб читати казку на ніч протягом X секунд  
 cooldownPeriod: 0  
 # За замовчуванням робоче навантаження СОННЕ, в іншому випадку — активне.  
 minReplicaCount: 0   
 triggers:  
 - type: cron  
 metadata:  
 # Допустимі значення — це значення з бази даних часового поясу IANA.  
 timezone: America/New_York   
 # О 09:00 кожного дня з понеділка по п’ятницю  
 start: 0 9 * * 1-5  
 # О 17:00 кожного дня з понеділка по п’ятницю   
 end: 0 17 * * 1-5  
 # Тобто мінімальна кількість реплік для цього робочого навантаження  
 desiredReplicas: "2"

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

(KEDA обчислює кількість реплік для всіх тригерів на основі функції MAX. Якщо ви додасте ще один масштаб до масиву тригерів, ваш CRON буде використовуватись як мінімальна кількість реплік, і будь-яке додавання може перевищити її.)

Розширене рішення — Custom Metrics API

pic

А що, якщо ви хочете, щоб зовнішня система визначала, коли і як робочі навантаження сплять, і ви хочете, щоб ваші навантаження були обізнані про цей стан і діяли відповідно? Саме тут на допомогу приходить Metrics API Scaler.

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

У цьому випадку я використав AWS DynamoDB, AWS Lambda та Jenkins як сервер автоматизації та планувальник cron. Однак ви можете замінити будь-яку з цих технологій на будь-яку іншу Database+API Server+Automation Scheduler, яку виберете.

Cron scaler не є частиною цього рішення. Для цього ми реалізуємо нашу бізнес-логіку через спеціальну точку API, на яку вказує KEDA Metrics Scaler:

apiVersion: keda.sh/v1alpha1  
kind: ScaledObject  
metadata:  
 name: sleepy-workload-scaler  
spec:  
 scaleTargetRef:  
 # Обов'язково. Має бути в тому ж просторі імен, що й ScaledObject  
 name: sleep-workload  
 # Інтервал опитування для перевірки подій масштабування  
 pollingInterval: 10  
 # Змушує робоче навантаження одразу заснути, коли йому наказують, замість того, щоб читати казку на ніч протягом X секунд  
 cooldownPeriod: 0  
 # За замовчуванням робоче навантаження СОННЕ, в іншому випадку — активне.  
 minReplicaCount: 0  
 triggers:  
 - type: metrics-api  
 metricType: Value  
 metadata:  
 # Мінімальна кількість реплік для робочих годин. Будь-яке значення, яке повертається  
 # API менше цього значення буде ігноруватись.
targetValue: "2"   
 # Ваша користувацька точка доступу API  
 url: "https://off-hours-api.my.domain/sleep?workload=sleepy-workload&replicas=2"  
 # Який ключ містить інформацію про кількість реплік у JSON-відповіді  
 valueLocation: "replicaCount"

У цьому прикладі наш скейлер sleepy-workload вказує на зовнішній URL та передає йому два параметри запиту:

  1. Workload = Назва робочого навантаження (ви можете замінити Workload на Namespace, щоб вимкнути ціле простір імен, направивши всі ваші робочі навантаження на одну й ту саму точку доступу з одним і тим самим запитом, який є їхнім спільним Namespace).
  2. Replicas = Якщо я не сплю, скільки реплік я маю мати?

Metrics scaler (скейлер метрик) потім очікує наступну JSON-відповідь від GET-запиту, якщо робоче навантаження є Активним:

{  
 "replicaCount": 2  
}

І якщо робоче навантаження вважається Сплячим:

{  
 "replicaCount": 0  
}

Припустимо, у нас є наступна таблиця DynamoDB під назвою state-of-my-workloads:

pic

Я написав псевдокод, який дозволяє пробуджувати ваше робоче навантаження та класти його спати. Давайте напишемо просту Python Lambda, яка відповідає на точний запит, який KEDA робить. Ви можете реалізувати та розгорнути будь-який API-сервер, з яким вам зручно працювати, і який підходить для вашої технологічної стека та варіанту використання:

def get_sleep_value(name_of_workload):  
 ...  
 is_sleeping = True # або False  
 return is_sleeping
def lambda_handler(event, context):  
 workload = query_params.get("workload", "Unknown")  
 replicas = query_params.get("replicas", 0) res = {  
 "statusCode": 200,  
 "headers": {"Content-Type": "application/json"},  
 "body": json.dumps(  
 {"replicaCount": (0 if get_sleep_value(workload) else replicas)}  
 ),  
 }  
 return res

Функцію getsleepvalue() можна реалізувати наступним чином, якщо, скажімо, ви зберігаєте стан ваших робочих навантажень/просторів імен у AWS DynamoDB:

def get_sleep_value(workload):  
 dynamodb = boto3.resource("dynamodb")  
 table = dynamodb.Table("state-of-my-workloads")  
 response = table.get_item(Key={"workload": workload})  
 item = response.get("Item")  
 print(item)  
 if item is not None:  
 sleep_value = item.get("sleep")  
 if sleep_value is not None:  
 return sleep_value  
 return False

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

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

Розумне рішення — Network Scaler

KEDA Network Scaler (мережевий скейлер KEDA) перебуває на бета-стадії вже кілька років. Незважаючи на це, це захоплюючий додаток, який вирішує одну з найпоширеніших проблем масштабування у світі — мережевий трафік. Спостерігаючи за метрикою мережевого трафіку, а не просто за обчислювальними ресурсами, ми відкриваємо для себе цілу нову низку можливостей. Одна з них — як ви вже здогадалися — зменшення масштабів. Це приклад реалізації патерну "serverless" на Kubernetes.

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

Щоб розпочати, потрібно додати доповнення KEDA HTTP до вашої наявної установки KEDA в кластері:

helm install http-add-on kedacore/keda-add-ons-http

Працюючи з HTTP, вам очевидно потрібен Kubernetes Service, який вказує на ваше робоче навантаження:

apiVersion: v1  
kind: Service  
metadata:  
 name: sleepy-workload-service  
spec:  
 selector:  
 app: sleepy-workload  
 ports:  
 - name: sleepy-workload-service-port  
 protocol: TCP  
 port: 80  
 targetPort: sleepy-workload-http-port

І ось тепер магія починається.
Оскільки ви встановили HTTP додаток, тепер у вас є доступ до наступного CRD. Ви можете налаштувати його таким чином (версія додатку v0.7):

kind: HTTPScaledObject  
apiVersion: http.keda.sh/v1alpha1  
metadata:  
 name: sleepy-workload  
spec:  
 hosts:  
 - myhost.com  
 scaleTargetRef:  
 name: sleepy-workload  
 kind: Deployment  
 apiVersion: apps/v1  
 service: sleepy-workload-service  
 port: 80  
 replicas:  
 min: 0  
 max: 1

Тепер, що відбудеться, так це те, що оператор http-add-on обробить CRD і, після налаштування, ви побачите нову службу, яка готова маршрутизувати HTTP трафік до вашого Deployment. Щоб підключити службу http-add-on до служби вашого робочого навантаження, потрібно виконати одну останню дію.

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

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

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

kubectl port-forward svc/keda-add-ons-http-interceptor-proxy 8080:80 -n keda

А в іншому терміналі:

curl localhost:8080 -H 'Host: myhost.com'

І ось воно!


Ця стаття перераховує кілька способів використання KEDA як механізму для досягнення масштабування до 0. Існує ще безліч інших методів, і немає правильного чи неправильного відповіді. Ці рішення просто використовують логіку, яка вже існує в open-source, для досягнення цієї мети. Якщо у вас є інші пропозиції, буду радий прочитати їх у коментарях, разом з будь-якими питаннями, які у вас можуть виникнути.

Перекладено з: Maximize Cost Savings by Putting Your Kubernetes Resources to Sleep During Off-Hours

Leave a Reply

Your email address will not be published. Required fields are marked *