Нашият опит в разработването на CSI-драйвер за Kubernetes за Яндекс.Облака

Нашият опит в разработването на CSI-драйвер за Kubernetes за Яндекс.Облака

С радостией съобщаваме, че компанията «Флант» допринася за Open Source инструментите за Kubernetes с пускането на алфа версия на CSI драйвера (Container Storage Interface) за Yandex.Cloud.

Но преди да преминем към подробностите на реализацията, нека отговорим на въпроса защо това е нужно, когато Яндекс вече предлага услуга Managed Service for Kubernetes.

Въведение

Защо е нужно?

В нашата компания, още от самото начало на експлоатацията на Kubernetes в продукция (т.е. вече няколко години), се развива собствен инструмент (deckhouse), който, между другото, в близко бъдеще също планираме да направим достъпен като Open Source проект. С него конфигурираме и настройваме единообразно всички свои клъстери, а в момента те вече са над 100, и то на най-различни хардуерни конфигурации и във всички налични облачни услуги.

Клъстерите, в които се използва deckhouse, разполагат с всички необходими компоненти за работа: балансьори, мониторинг с удобни графики, метрики и аларми, автентикация на потребители чрез външни доставчици за достъп до всички дашбордове и така нататък. Такъв „усилен“ клъстер няма смисъл да се поставя в managed решение, тъй като често това е невъзможно или ще наложи деактивирането на половината компоненти.

NB: Това е нашият опит и той е доста специфичен. Ние по никакъв начин не твърдим, че всеки трябва самостоятелно да се занимава с разгръщането на клъстери Kubernetes вместо да ползва готови решения. Между другото, нямаме реален опит с експлоатация на Kubernetes от Яндекс и няма да даваме оценка на тази услуга в настоящата статия.

Какво е това и за кого?

И така, вече говорихме за съвременния подход към хранилищата в Kubernetes: как е устроен CSI и как общността стигна до такъв подход.

В момента много големи доставчици на облачни услуги са разработили драйвери за използване на своите „облачни“ дискове като Persistent Volume в Kubernetes. Ако обаче такава драйвер не съществува при доставчика, но всички необходими функции се предоставят чрез API, нищо не пречи на реализирането на драйвер със собствени усилия. Именно така се случи и с Яндекс.Облака.

За основа на разработката взехме CSI драйвера за облака DigitalOcean и няколко идеи от драйвера за GCP, тъй като взаимодействието с API на тези облаци (Google и Яндекс) има редица прилики. По-конкретно, API-то и при GCP, и при Yandex връща обект Операция за проследяване на статуса на дълготрайни операции (например, създаване на нов диск). За взаимодействие с API на Yandex.Cloud се използва Yandex.Cloud Go SDK.

Резултат от извършената работа е публикуван в GitHub и може да е полезен за тези, които по някаква причина използват собствена инсталация на Kubernetes на виртуални машини в Yandex.Cloud (но не готов управляем клъстер) и искат да използват (поръчат) дискове през CSI.

Реализация

Основни възможности

В момента драйверът поддържа следните функции:

  • Поръчка на дискове във всички зони на клъстера в съответствие с топологията на наличните в клъстера възли;
  • Изтриване на вече поръчани дискове;
  • Offline увеличаване на дискове (Yandex.Cloud не поддържа увеличаване на дискове, които са монтирани на виртуална машина). Как точно се преработи драйверът, за да се извърши увеличаването по възможно най-безболезнен начин, вижте по-долу.

В бъдеще е планирано да се реализира поддръжка за създаване и изтриване на моментни снимки на дискове.

Основната трудност и нейното преодоляване

Липсата на възможност в API на Yandex.Cloud за увеличаване на дискове в реално време — ограничение, което усложнява операцията на увеличаване за PV (Persistent Volume): при такава ситуация е необходимо pod приложението, което използва диска, да бъде спряно, а това може да доведе до престой на приложението.

Според спецификация CSI, ако CSI-контролерът съобщава, че може да изпълнява увеличаване на дискове само „в offline“ (VolumeExpansion.OFFLINE), то процесът на увеличаване на диска трябва да протича така:

Ако плъгинът има само VolumeExpansion.OFFLINE възможност за увеличаване и обемът в момента е публикуван или наличен на възел, то ControllerExpandVolume ТРЯБВА да бъде извикан САМО след:

  • Плъгинът има контролер PUBLISH_UNPUBLISH_VOLUME възможност и ControllerUnpublishVolume е бил успешно извикан.

ИЛИ

  • Ако плъгинът НЯМА контролер PUBLISH_UNPUBLISH_VOLUME възможност, плъгинът има възел STAGE_UNSTAGE_VOLUME възможност и NodeUnstageVolume е бил успешно завършен.

ИЛИ

  • Ако плъгинът НЯМА контролер PUBLISH_UNPUBLISH_VOLUME възможност, нито възел STAGE_UNSTAGE_VOLUME възможност и NodeUnpublishVolume е бил успешно завършен.

По същество това означава, че трябва да се отдели дискът от виртуалната машина, преди да може да бъде увеличен.

Въпреки това, за съжаление, реализация спецификациите на CSI чрез sidecar не отговарят на тези изисквания:

  • В sidecar контейнера csi-attacher, който трябва да отговаря за необходимото време между монтиранията, при offline-резайз просто не е реализиран този функционал. Дискусията по този въпрос беше иницирана тук.
  • Какво всъщност е sidecar контейнер в този контекст? Самият CSI плъгин не взаимодействат с Kubernetes API, а просто отговаря на gRPC повиквания, изпратени от sidecar контейнери. Последните се разработват общността на Kubernetes.

В нашия случай (CSI плъгин) операцията за увеличаване на диска изглежда по следния начин:

  1. Получаваме gRPC повикване ControllerExpandVolume;
  2. Опитваме се да увеличим диска в API, но получаваме грешка за невъзможност за изпълнение на операцията, тъй като диска е монтиран;
  3. Записваме идентификатора на диска в карта, съдържаща дискове, за които е необходимо да се извърши операцията за увеличаване. По-късно за краткост ще наричаме тази карта volumeResizeRequired;
  4. Ръчно изтриваме pod, който използва диска. Kubernetes ще го рестартира. За да предотвратим диска да бъде монтиран (ControllerPublishVolume) преди приключване на операцията по увеличаване при опит за монтиране, проверяваме дали този диск все още е в volumeResizeRequired и връщаме грешка;
  5. CSI драйвера се опитва да повтори операцията по resize. Ако операцията е успешна, изтриваме диска от volumeResizeRequired;
  6. Тъй като идентификаторът на диска отсъства в volumeResizeRequired, ControllerPublishVolume преминава успешно, дискът се монтира, pod-ът се стартира.

Всё изглежда доста просто, но както винаги има подводни камъни. Увеличаването на дисковете отговаря на external-resizer, който в случай на грешка при изпълнението на операцията използва опашка с експоненциално увеличаване на времето за изчакване до 1000 секунди:

func DefaultControllerRateLimiter() RateLimiter {
  return NewMaxOfRateLimiter(
  NewItemExponentialFailureRateLimiter(5*time.Millisecond, 1000*time.Second),
  // 10 qps, 100 bucket size.  Това е само за скорост на повторение и е само общ фактор (не на елемент)
  &BucketRateLimiter{Limiter: rate.NewLimiter(rate.Limit(10), 100)},
  )
}

Това може периодично да доведе до това, че операцията по увеличаване на диска се проточва над 15+ минути и по този начин става недостъпен съответния pod.

Единственият вариант, който достатъчно лесно и безболезнено ни позволи да намалим потенциалното време на простои, беше използването на наша версия на external-resizer с максимално ограничение на времето за изчакване от 5 секунди:

workqueue.NewItemExponentialFailureRateLimiter(5*time.Millisecond, 5*time.Second)

Не преценихме за необходимо да инициираме спешна дискусия и да патчим external-resizer, защото offline resize на дисковете — е атисъм, който скоро ще изчезне при всички облачни доставчици.

Как да започнем да използваме?

Драйверът се поддържа в Kubernetes версия 1.15 и по-нови. За работата на драйвера трябва да се изпълняват следните изисквания:

  • Знаме --allow-privileged установен на стойност true за API-сървъра и kubelet;
  • Активирани --feature-gates=VolumeSnapshotDataSource=true,KubeletPluginsWatcher=true,CSINodeInfo=true,CSIDriverRegistry=true за API-сървъра и kubelet;
  • Разпространение на монтирането (mount propagation) трябва да бъде включено в кластера. При използване на Docker, демонът трябва да бъде конфигуриран така, че да са разрешени споделени обекти за монтиране (shared mounts).

Всички необходими стъпки за самата инсталация са описани в README. Инсталацията представлява създаване на обекти в Kubernetes от манифести.

За да работи драйвера, ви е необходимо следното:

  • Да посочите в манифеста идентификатора на директорията (folder-id) на Яндекс.Облака (вж. документацията);
  • За взаимодействие с API на Яндекс.Облака в CSI-драйвера се използва сервисен акаунт. В манифеста Secret трябва да предадете авторизирани ключове от сервисния акаунт. В документацията е описано., как да създадете сервисен акаунт и да получите ключовете.

Общо взето — опитайте, а ние ще се радваме на обратна връзка и нови проблеми, ако се сблъскате с някакви трудности!

Допълнителна поддръжка

В заключение, бихме искали да подчертаем, че този CSI-драйвер не е създаден от голямо желание за забавление с разработката на приложения на Go, а поради остра необходимост вътре в компанията. Няма да смятаме, че поддържането на собствена реализация е целесъобразно, така че, ако Яндекс прояви интерес и реши да продължи поддръжката на драйвера, с удоволствие предадем репозитория на тях.

Освен това, сигурно Яндекс в управляваните клъстери Kubernetes има собствена реализация на CSI-драйвера, която може да бъде пусната като Open Source. Тази възможност изглежда благоприятна за нас — общността ще може да се възползва от тестван драйвер от доставчика на услуги, а не от трета страна.

P.S.

Прочетете също в нашия блог:

Източник: habr.com

Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри 🔥 Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри | ProHoster