Налаштування EKS-кластерів за допомогою Karpenter: DevOps-практика

💡 Усі статті, обговорення, новини про DevOps — в одному місці. Приєднуйтесь до DevOps спільноти!

Вітаю! Мене звати Євген Чемерис, я DevOps Engineer у компанії Profisea Labs. Для підтримки роботи платформи Uniskai by Profisea Labs та деяких тестових навантажень ми використовуємо AWS EKS. Нещодавно ми зіткнулися із наступною проблемою у наших EKS-кластерах:

Більшість «проплачених» ресурсів не була задіяна. Зважаючи на показники CPU limits на графіку вище, можна було б непогано зекономити на ціні обчислювальних потужностей EKS після виправилення ситуації. 

Через велику кількість робочого навантаження з різними вимогами (деякі з них використовують процесор, інші — переважно пам’ять) більшість Nodes були використані лише частково. Тож ми в команді вирішили спробувати інший механізм менеджменту Nodes у кластері — Karpenter. 

Спойлер: після реалізації Karpenter в одному з кластерів ми маємо таку ситуацію щодо використання ресурсів: 

Абсолютна кількість CPU requests є однаковою для обох графіків.

Як частина підготовки кластеру до використання Karpenter, requests / limits на більшості наяних ресурсів були переглянуті. Додавання їх, особливо на DaemonSets, робить останній графік менш об’єктивним.

Огляд

Простими словами, Karpenter — це Amazon EKS-заміна для cluster-autoscaler та aws-node-termination-handler з великою кількістю цікавих функцій для менеджменту Nodes вашого кластеру. 

FYI: 7 листопада 2023 року став доступним Karpenter для AKS (Azure). Але ця стаття описує лише досвід використання Karpenter з Amazon EKS-кластером.

Переваги Karpenter у порівнянні з cluster-autoscaler: 

  • Karpenter NodePools є більш гнучкими, ніж EKS NodeGroups. Ви можете задати велику кількість характеристик для ваших Nodes: spot/on-demand (або обидва), прийнятну кількість CPU, Availability Zones тощо. 
  • Під час спостереження за непризначеними Pods Karpenter враховує NodePool та Pod affinity конфігурації та знаходить найбільш вигідний тип і розмір інстансу під нову Node. 
  • Karpenter може спостерігати за вільними ресурсами на існуючих Nodes та перерозподіляти робоче навантаження відповідно до цього. 
  • Karpenter піднімає Nodes швидше завдяки можливості уникати cluster-autoscaler процесу скейлінгу: EKS NodeGroup -> AutoscalingGroup-скейлінг. Karpenter піднімає інстанси напряму. 
  • Функціонал spot interruption-handling надає можливість спостерігати за EC2-івентами та обробляти можливі незаплановані завершення інстансів. 

Як це працює

Після встановлення Karpenter починає стежити за Pods, що не призначені до жодної Node. Щоб почати створювати Nodes, вам необхідно мати хоча б один NodePool. NodePool же посилається на інший концепт — NodeClass. 

NodeClass — це набір притаманних AWS конфігурацій інстансів: AMI, теги ресурсу, призначення EBS тощо. 

NodePool — це набір вимог до Nodes: Availability Zone, кількість CPU, сімейство інстансу тощо. Також NodePool описує taints, labels і конфігурацію kubelet. Ви можете створити декілька NodePools, щоб гарантувати використання Reserved Instances і Savings Plans або щоб підтримувати мультиархітектурні робочі навантаження, не використовуючи taints та tolerations. 

Порада: будьте обережними під час використання параметру weight. Ви можете використовувати його, щоб задати пріоритет обрання NodePool. Але він не гарантує, що наступний Pod буде призначено до Node з NodePool із найвищим weight. Дивіться Note в цій секції. 

Діаграми нижче було побудовано відповідно до документації Karpenter. 

Щоб піднімати нові Nodes, Karpenter звертається до EC2 Fleet API. Він же намагається підняти інстанс відповідно до Price Capacity Optimized allocation strategy. 

Для обробки spot interruption івентів Karpener використовує default EventBridge bus і попередньо створену SQS-чергу. Такий підхід дозволяє стежити за можливими івентами видалення інстансів і завчасно підготувати заміну для Pods із Node, яку ви видаляєте:

Spot interruption-handling є опціональною функцією, що може бути включена заданням параметру settings.interruptionQueue у Helm чарті. 

Підготовка до встановлення 

У Karpenter Best Practicies двічі згадується про важливість перегляду requests і limits робочого навантаження. Для уникнення можливих помилок OOMKilled після міграції на Karpenter необхідно встановити ресурси пам’яті так, щоб limits дорівнювали requests. 

Порада: для встановлення ресурсів на EKS-аддонах можна використовувати їхні configuration values. 

Звісно ж, ви захочете підтримувати Karpenter на попередньо створених Nodes, що працюють, незважаючи ні на що. Оскільки Karpenter не може створити Nodes сам для себе (за відсутністю інших Nodes 🐔🥚), необхідно створити EKS NodeGroup для нього. 

Порада: ви можете використовувати сімейство інстансів AWS Graviton для Karpenter EKS NodeGroup, щоб зекономити на підтримці Karpenter у вашому кластері. 

Встановлення

У нашому випадку найпростішим способом встановлення було використання Terraform Karpenter-сабмодулю, що містить ресурси AWS, необхідні для роботи Karpenter у кластері. 

Позначте необхідні Security Groups та VPC Subnets тегом karpenter.sh/discovery, щоб вказати Karpenter, де піднімати Nodes. 

Тепер необхідно встановити Karpenter Helm чарт у кластер і виставити toleration та nodeSelector як параметри Helm-чарту, щоб запускати Karpenter у Karpenter NodeGroup.  

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

Застереження 

  • Після встановлення Karpenter ми зіткнулися з проблемою нестабільності деяких робочих навантажень. Після детального дослідження стало зрозуміло, що проблема виникає лише на одній категорії інстансу, яка не може підтримувати навантаження. Отож ми опинились у ситуації, коли задали ресурси для робочих навантажень, але, вірогідно, через різні апаратні характеристики встановлені рамки не є універсальними для всіх категорій інстансів.
    Ми змінили NodePool на підняття тільки протестованих категорій інстансів, що можуть підтримувати задане робоче навантаження.
  • Деякі старі робочі навантаження були проігноровані під час перегляду requests/limits, що спричинило постійні OOMKilled-помилки.
    Ми оновили ці робочі навантаження новими ресурсами пам’яті, де requests дорівнюють limits. 
  • Через тиждень після встановлення Karpenter ми зіткнулися з проблемою, коли Pods не могли зайти на жодну Node і нові Nodes не створювались. 
    Причиною було перевищення NodePool-лімітів на ресурси. Необхідно обачно встановлювати ці ліміти, щоб обмежувати та контролювати кількість можливого робочого навантаження у кластері. 

Karpenter та Uniskai by Profisea Labs 

Хоча Karpenter безпосередньо поки що не підтримується Uniskai by Profisea Labs, наявний функціонал Cloudsitter може допомогти планувати «сон» для Kubernetes Namespaces. 

Як ми це робимо? 

Уявімо, ви маєте EKS-кластер зі встановленим Karpenter. Namespaces кластеру можуть бути поділені на декілька логічних груп: 

  • власне Karpenter; 
  • інструменти управління кластером (моніторінг, логування, CI/CD інструменти); 
  • робоче навантаження, розподілене між різними Namespaces. 

Оскільки ми хочемо, щоб Karpenter був доступним цілодобово, створюємо Cloudsitter-полісі, яка буде підтримувати його робочим 24/7. 

Також ми хочемо мати всі інструменти управління кластером доступними під час певних часових періодів з понеділка до п’ятниці. Тому створюємо відповідну Cloudsitter-полісі. 

Ми хочемо вмикати робоче навантаження кластера вручну, коли воно справді нам потрібне. Тому створюємо Cloudsitter-полісі для постійного сну та надалі вмикаємо тільки необхідні Namespaces саме тоді, коли ми їх потребуємо. 

Недоліком цього рішення є відсутність 100% гарантії, що Karpenter видалить усі Nodes, коли всі Namespaces будуть спати. Але зазвичай це працює добре, а в кластері залишається тільки декілька маленьких Nodes. 

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

Разом з Karpenter та Cloudsitter від Uniskai by Profisea Labs ми створили систему контроля Nodes в кластері, коли можемо визначати набір необхідного на конкретний момент робочого навантаження  та підтримувати його на найвигідніших інстансах.

Висновки

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

Незважаючи на певні труднощі (тонке налаштування NodePools для відповідності конкретним категоріям інстансів і важливість встановлення точних обмежень ресурсів), інтеграція Karpenter разом з Cloudsitter від Uniskai by Profisea Labs дала нам змогу динамічно керувати Kubernetes Namespaces і досягти оптимального балансу між доступністю та ефективністю хмарних витрат.

👍ПодобаєтьсяСподобалось10
До обраногоВ обраному6
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Цікаво було б почитати як оптимально використовувати Karpenter (aka Node autoprovisioning) в Azure разом з резерваціями, щоб досягнути раціонального використання ресурсів та зменшення витрат, коли власне ця фіча вийде з прев’ю.

Підписатись на коментарі