Helm vs. Operator: приклад Hazelcast

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

Привіт, мене звати Сергій. Я працюю Software engineer в Cloud-native команді в компанії Hazelcast. В цій команді ми займаємось тим, щоб зробити наш продукт по справжньому cloud-native.

Коли ж ми говоримо про cloud-native, одне з перших що приходить на думку — це Kubernetes. І саме інтеграції з ним ми приділяємо особливу увагу. В даній статті я розповім про наш досвід інтеграції з даною платформою та розповім чому ми вирішили написати оператор, вже маючи справний та активно підтримуваний Helm chart.

Популярність Kubernetes робить його однією з найрозповсюдженіших платформ в сучасній cloud-native розробці. І однією з важливих задач є деплой на Kubernetes. Щоб цю задачу спростити є багато різних рішень, але найбільше виділяються два: Helm та Operator.

Дисклеймер: ця стаття є перекладом оригіналу, опублікованого тут. Переклад з англійської мови організовано редакцією DOU.

Чому потрібен Hazelcast Platform Operator

Hazelcast дотримується концепції cloud-native, і для цього в нас є Helm chart, який є зрілим та активно підтримуваним, та дозволяє швидко та легко задеплоїти Hazelcast та Management Center на Kubernetes.

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

Використовуючи Helm, досить просто встановити кластер Hazelcast, але для використання складнішого функціоналу, користувачам потрібно мати певний досвід роботи з Hazelcast та Kubernetes. І щоб спростити це для користувачів, ми вирішили не намагатись заімплементити у Helm те, для чого він не передбачений, а натомість написати Kubernetes Operator.

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

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

Проблеми, які вирішує оператор

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

Operator

Helm

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

Робить Hazelcast нативним для Kubernetes

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

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

Повний контроль над конфігурацією Hazelcast кластеру

Автоматичне керування життєвим циклом кластера та функціоналу

Проста абстракція над складною конфігурацією Hazelcast

Підтримується весь функціонал Hazelcast

Custom Resource Definition (CRD)

За допомогою оператора Hazelcast кластер стає насправді нативним для Kubernetes, використовуючи CRD. Ми маємо ресурс Hazelcast CR, та можемо створювати та керувати ним як будь-яким іншим ресурсом Kubernetes.

apiVersion: hazelcast.com/v1alpha1 
kind: Hazelcast 
metadata:   
  name: my-hazelcast

І Helm, і Operator можуть мати набір конфігурації за замовчанням, щоб таким чином користувач міг змінювати тільки те, що йому потрібно. Однак за допомогою оператора ми можемо в реальному часі визначати, яку конфігурацію використовувати.

Для прикладу, Hazelcast Platform Operator використовує іншу конфігурацію SecurityContext, коли кластер встановлюється на платформі OpenShift. Використовуючи Helm, користувачеві доведеться самому змінити конфігурацію SecurityContext для різних платформ.

І, звичайно, Hazelcast не був би по справжньому нативним для Kubernetes, якби ми не могли спостерігати за станом кластера:

$ kubectl get hazelcast my-hazelcast 
NAME         STATUS  MEMBERS EXTERNAL-ADDRESSES 
my-hazelcast Running 3/3

Динамічна конфігурація

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

Наприклад, починаючи зі звичайного in-memory датаґріду, користувач може захотіти використати функціонал WAN Replication, щоб реплікувати всі дані між двома кластерами. Це можливо налаштувати за допомогою Helm.

Однак, подібна зміна налаштування призведе до рестарту кластера. Використовуючи ж оператор, потрібно лише створити WanReplication ресурс, і оператор динамічно налаштує кластер та не буде потребувати рестарту.

apiVersion: hazelcast.com/v1alpha1
kind: WanReplication 
metadata:   
  name: wanreplication-sample 
spec:   
  mapResourceName: my-map   
  targetClusterName: dev   
  endpoints: "203.0.113.61"

API

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

Одним з прикладів є підключення до кластера Hazelcast з-за меж Kubernetes за допомогою smart-клієнта. Щоб це зробити, кожному користувачеві потрібно створити Service для кожного Hazelcast Pod. Це можна зробити або вручну, що викличе проблеми у випадку масштабування, або використовувати сторонні інструменти, такі як metacontroller.

За допомогою оператора, користувачеві потрібно лише декларативно описати exposeExternally конфігурацію в Hazelcast CR.

apiVersion: hazelcast.com/v1alpha1
kind: Hazelcast
metadata:
  name: hazelcast
spec:
  licenseKeySecret: hazelcast-license-key
  exposeExternally:
    type: Smart
    discoveryServiceType: LoadBalancer
    memberAccess: NodePortExternalIP

Оператор подбає про створення сервісів вказаного типу для кожного Hazelcast Pod.

Життєвий цикл функціонала

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

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

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

apiVersion: hazelcast.com/v1alpha1
kind: Hazelcast
metadata:
  name: hazelcast
spec:
  licenseKeySecret: hazelcast-license-key
  persistence:
    baseDir: "/data/hot-restart/"


apiVersion: hazelcast.com/v1alpha1
kind: HotBackup
metadata:
  name: hot-backup
spec:
  hazelcastResourceName: my-hazelcast
  bucketURI: "s3://operator-backup"
  secret: "br-secret-s3"

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

Висновки

Ми розглянули, наскільки Hazelcast Platform Operatom може бути корисним користувачам Hazelcast. Однак, це зовсім не означає, що Helm chart більше не потрібен. Helm все ще є дуже корисним для досвідчених користувачів, які хочуть самі контролювати всі процесу свого кластеру.

Але якщо ви надаєте перевагу простоті, автоматизації та Kubernetes-native досвіду, тоді оператор буде правильним вибором для вас. На перший погляд, може здатись, що Hazelcast Platform Operator підтримує менше функціоналу, однак він все ще активно розробляється, та новий функціонал додається з кожним новим релізом.

Крім того, Custom Configuration покриє відсутній функціонал, допоки він не стане доступним в операторі нативно, тож ви можете обирати, орієнтуючись на ваші потреби.

Діліться вашим досвідом у коментарях, у Slack та Github.

👍ПодобаєтьсяСподобалось2
До обраногоВ обраному0
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

Було б добре показати на прикладах Metacontroller основні типи операторів, пояснити, наприклад, як функціонал операторів можна замінити Composite Functions там десь в Crossplane. Та порівняти різні варіанти роботи з K8S API, на рівні прямого доступу там десь через TypeScript клієнт, типові кодогенератори на основі Kubebuilder та Operator SDK, та той же Java Operator Framework.

Бо коли відповідно виникає потреба в реалізації оператора, той же Metacontroller поверх Knative закриває 90% потреб, а на розробку з кодогенераторами в Kubebuilder потрібно буде на багато більше зусиль із-за доволі специфічної й негнучкої інструментації. Часто навіть на rust/scala3 з макросами меньше мороки й там з якимось k8s_openapi клієнтом, ніж з Kubebuilder.

Порівнювати Helm й оператори було б більш доречно там в рамках Operator SDK for Helm, бо там є відповідні байндінги з OLM’ом, а так практичної користі від подібної статті дуже мало.

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

Стаття більше подібна на «Переваги Operator над Helm», аніж на

Helm vs. Operator

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

Дякую за коментар! Звичайно що срібної кулі не існує, і в кожного підходу є свої недоліки. В даному ж випадку, я описав конкретний кейс Hazelcast, та навіщо потрібен оператор коли вже є Helm чарт, так як з Helm-ом люди більше менш знайомі, а оператор, хоч і зовсім не новий патерн, але все ще лишається «екзотичним» 🙂

Kuber це не cloud native аж ніяк навіть якщо managed, а навпаки cloud agnostic.
Cloud native рішення буде на противагу контейнерам якийсь багатостеповий serverless обмазанний з різних сторін in/out різними хмарними компонентами.

Cloud agnostic ніяк не суперечить концепції cloud native, ми ж не говоримо про aws/gcp/azure-native, головне що це cloud. Не просто так же K8s є частиною CNCF та чи не найбільшим проектом в Cloud native landscape 🙂

Cloud agnostic як раз таки суперечить поняттю cloud native оскільки вимагає абстрагування від рішень конкретного вендора — як програмних так і інфраструктурних де треба більше менеджити інфраструктуру, дизайнити інтеграцію компонентів через різні oss клієнти/бібліотеки, полягатися на протоколи і інструментарій, що не може на повну використовувати готові фічі конкретного cloud (якщо вони не стандартизовані).
Cloud native передбачає що ви максимально і наповну використуваєте все готове, а не лімітуєте себе у фічах і імплементите те саме або шукаєте в oss рішеннях якщо його поставляє cloud provider обраний , а інший — ні.

Якщо подивитись на продукти Cloud Native Landscape (landscape.cncf.io), то можна побачити що переважна більшість продуктів які там представлені є саме cloud agnostic.
Сама ж концепція cloud native не має в собі обовʼязкового vendor lock-in, основне це те що аплікейшен має бути розроблений під роботу в клауді (знов ж таки, привʼязка до вендора не є обовʼязковою, бо клауд може бути навіть приватним) 🙂
І самі CNCF називають Kubernetes прикладом cloud native, open source проектів:

The Cloud Native Computing Foundation was created with the charter to define the term cloud native and provide a home for the cloud native, open source projects such as Kubernetes, Prometheus and others

The Cloud Native Computing Foundation (CNCF) is a Linux Foundation project that was founded in 2015 to help advance container technology[1] and align the tech industry around its evolution.

Це організація просто розвиває впровадження рішень на контейнерах і щоб вони отримували в тому числі кращу нативну підтримку (а не стандарт рішень ansi/iso якийсь який варто булоб референсити чи хоча б architecture reference від cloud providers).
Якщо ви дійсно хочете робити cloud native рішеня, то там serverless буде набагато попереду контейнеризованих рішень в наборі готових фіч для вирішення різних бізнесс задач, а ніж контейнери розгорнуті в клауді. у тому контексті що ви використовуєте цей термін і назва організації — це просто маркетинговий buzzword.

Тим не менш, саме ця організація сьогодні є рушійною силою розвитку cloud native технологій. Більше того, на Kubernetes самі себе називають cloud native технологією (kubernetes.io/...​the-future-of-software/)

Cloud-native technologies, and especially Kubernetes, arose to meet this need, providing the automation and observability necessary to manage applications at scale and with high velocity

.
І навіть самі AWS кажуть що Kubernetes є частиною cloud-native (aws.amazon.com/what-is/cloud-native ):

Established in 2015, the CNCF supports the open-source community in developing critical cloud-native components, including Kubernetes

Public cloud та serverless є частиною cloud-native, але далеко не тільки це. Використання цього терміну досить широке, що, деякою мірою, і зробило його баззвордом. Але якщо відкинути хайп навколо цього терміну, то cloud native — це вимога більшості сучасних додатків.

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

Все змішались люди, коні..

однак це рішення не підходить для stateful аплікейшенів.

learn.microsoft.com/...​urable-functions-overview

Якщо порівнювати контейнери і serverless вони вміють все те саме іноді готове іноді кастомне.. те що робить serverless cloud native — саме повна готова інтеграція з экосистемою cloud комплнентів, у випадку контейнерів вам або треба буде розгортати oss рішення , або інтегруватись через підключення ліб сторонніх, розробки потрібного системного дизайну і т.д. Простіше кажучи витрачати додатковий час на все починаючи з інфраструктури і розгортанню коду, закінчуючи імплементацією потрібного рішення , замість готового запропонованого cloud provider.

Durable Functions, це все ж таки розширення можливостей стандартного serverless. Не кажучи вже про те, що намагатись запхати Hazelcast у функції — це використання інструментів не за призначенням.
Крім того використання кліента Hazelcast в serverless підтримується як частина cloud native інтеграцій. Як користувачів які хочуть мати повний serverless experience, також є доступний Hazelcast Viridian Serverless 🙂

Поясніть мені, як п’ятирічній дитині, що таке Hazelcast, і як його можна використовувати?

Найбільш поширенішим використанням Hazelcast — це, однозначно, розподілений кеш. Наприклад, інтегруючи його зі Spring додатком: docs.hazelcast.com/...​latest/spring/add-caching. Одна тільки на цьому його функціонал не закінчується. Так, наприклад, Hazelcast дає можливість розподіленої обробки великих даних в реальному часі (real-time data processing): docs.hazelcast.com/...​latest/pipelines/overview.
Загалом, Hazelcast має досить багатий набір функціоналу для вирішення різних задач розподілених обчислень, такі як Executor Service (docs.hazelcast.com/...​omputing/executor-service), різноманітні розподілені структури даних (docs.hazelcast.com/...​stributed-data-structures), тощо. Так історично склалось що найбільшу підтримку та впізнаваність Hazelcast має саме в Java світі, але ми працюємо над тим щоб це виправити 🙂
Пояснення вийшло не зовсім як пʼятирічнй дитині, але сподіваюсь що зміг пояснити що таке і для чого потрібен Hazelcast

я б навіть тупенко пояснив: do distributed pretending local. Це насправді головна ідея продукту

Hazelcast дає можливість розподіленої обробки великих даних в реальному часі (real-time data processing)

а у чому переваги над, наприклад, Sparkом?

Важко сказати що щось одне має перевагу над іншим, але внутрішня імплементація Hazelcast та Spark краще підходить під різні use-cases. Якщо коротко, то Hazelcast краще підходить під low-latency stream processing (такі типові юзкейси як Real-Time Fraud Detection, Real-Time Recommendations, та інше де latency є важливим фактором), в той час як Spark краще підходить для batch processing. Більш детальна різниця, включно з деталями імплементації є описано ось тут: hazelcast.com/...​compares-to-apache-spark

Хазлкаст — це як редіс, тільки на джаві і з кількома вагонами додаткового функціоналу.

Найбазовіше використання — як аналог редісу. А далі якщо вам потрібні додаткові фічі distributed світу — вибирайте що хочте.

Однією з цікавих фіч, наприклад, є можливість запустити хазлкаст в ембеддед-режимі. Тобто, вам не потрібний хазлкаст як сервер. Сам ваш додаток (спрінг аппка наприклад) і є сама собі сервером. Запускаєте ще +1, +2.... +Х інстансів — вони формують кластер і ви маєте embedded distributed data layer прям в додатку. Самі роблять leader election. І ви легко можете достукатись до цього через публічне апі. Бо для цього ж в редісі, наприклад, треба писати свої алгоритми-надбудови над RLock.

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