Наш вопыт распрацоўкі CSI-драйвера ў Kubernetes для Яндэкс.Аблокі

Наш вопыт распрацоўкі CSI-драйвера ў Kubernetes для Яндэкс.Аблокі

Рады абвясціць, што кампанія "Флант" папаўняе свой уклад у Open Source-інструменты для Kubernetes, выпусціўшы альфа-версію драйвера CSI (Container Storage Interface) для Яндэкс.Аблокі.

Але перад тым, як перайсці да дэталяў рэалізацыі, адкажам на пытанне, навошта гэта ўвогуле трэба, калі ў Яндэкса ўжо ёсць паслуга Managed Service for Kubernetes.

Увядзенне

Нашто гэта?

Усярэдзіне нашай кампаніі, яшчэ з самага пачатку эксплуатацыі Kubernetes у production (г.зн. ужо некалькі гадоў), развіваецца ўласная прылада (deckhouse), які, дарэчы, мы таксама плануем у хуткім часе зрабіць даступным як Open Source-праект. З яго дапамогай мы аднастайна канфігуруем і наладжваем усе свае кластары, а ў сапраўдны момант іх ужо больш за 100, прычым на самых розных канфігурацыях жалеза і ва ўсіх даступных хмарных сэрвісах.

Кластары, у якіх выкарыстоўваецца deckhouse, маюць у сабе ўсе неабходныя для працы кампаненты: балансавальнікі, маніторынг са зручнымі графікамі, метрыкамі і алертамі, аўтэнтыфікацыю карыстачоў праз вонкавых правайдэраў для доступу да ўсіх dashboard'ам і гэтак далей. Такі «прапампаваны» кластар няма сэнсу ставіць у managed-рашэнне, бо часцяком гэта альбо немагчыма, альбо прывядзе да неабходнасці адключаць палову кампанентаў.

NB: Гэта наш вопыт, і ён даволі спецыфічны. Мы ні ў якім разе не сцвярджаем, што ўсім варта самастойна займацца разгортваннем кластараў Kubernetes замест таго, каб карыстацца гатовымі рашэннямі. Дарэчы, рэальнага досведу эксплуатацыі Kubernetes ад Яндэкса ў нас няма і даваць якую-небудзь адзнаку гэтаму сэрвісу ў гэтым артыкуле мы не будзем.

Што гэта і для каго?

Такім чынам, мы ўжо расказвалі пра сучасны падыход да сховішчаў у Kubernetes: як уладкованы CSI и як супольнасць прыйшло да такога падыходу.

У цяперашні час шматлікія буйныя пастаўшчыкі хмарных паслуг распрацавалі драйверы для выкарыстання сваіх "хмарных" дыскаў у якасці Persistent Volume у Kubernetes. Калі ж такога драйвера ў пастаўшчыка няма, але пры гэтым усе неабходныя функцыі падаюцца праз API, тое нішто не мяшае рэалізаваць драйвер уласнымі сіламі. Так і атрымалася ў нас з Яндэкс.Аблокам.

За аснову для распрацоўкі мы ўзялі CSI-драйвер для аблокі DigitalOcean і пару ідэй з драйвера для GCP, так як узаемадзеянне з API гэтых аблокаў (Google і Яндэкс) мае шэраг падабенстваў. У прыватнасці, API і ў GCP, І ў Яндэкс вяртаюць аб'ект Operation для адсочвання статусу працяглых аперацый (напрыклад, стварэння новага дыска). Для ўзаемадзеяння з API Яндэкс.Аблокі выкарыстоўваецца Yandex.Cloud Go SDK.

Вынік праведзенай працы апублікаваны на GitHub і можа спатрэбіцца тым, хто па нейкім чынніку выкарыстае ўласную ўсталёўку Kubernetes на віртуальных машынах Яндэкс.Аблокі (але не гатовы managed-кластар) і жадаў бы выкарыстаць (заказваць) кружэлкі праз CSI.

Рэалізацыя

асноўныя магчымасці

На дадзены момант драйвер падтрымлівае наступныя функцыі:

  • Заказ дыскаў ва ўсіх зонах кластара паводле тапалогіі наяўных у кластары вузлоў;
  • Выдаленне замоўленых раней дыскаў;
  • Offline resize для дыскаў (Яндэкс.Хмара не падтрымлівае павелічэнне дыскаў, якія прымантаваны да віртуальнай машыны). Пра тое, як прыйшлося дапрацоўваць драйвер, каб максімальна бязбольна выконваць resize, гл. ніжэй.

У будучыні плануецца рэалізаваць падтрымку стварэння і выдаленні снапшотаў дыскаў.

Галоўная складанасць і яе пераадоленне

Адсутнасць у API Яндэкс.Аблокі магчымасці павялічваць дыскі ў рэальным часе - абмежаванне, якое ўскладняе аперацыю resize'а для PV (Persistent Volume): бо ў такім выпадку неабходна, каб pod прыкладанні, які выкарыстоўвае дыск, быў спынены, а гэта можа выклікаць просты прыкладанні.

Згодна з спецыфікацыі CSI, калі CSI-кантролер паведамляе аб тым, што ўмее рабіць resize дыскаў толькі "у offline" (VolumeExpansion.OFFLINE), то працэс павелічэння дыска павінен праходзіць так:

If the plugin мае толькі VolumeExpansion.OFFLINE expansion capability and volume currently published or available on a node then ControllerExpandVolume MUST called ONLY after either:

  • The plugin мае controller PUBLISH_UNPUBLISH_VOLUME capability and ControllerUnpublishVolume has been invoked successfully.

OR ELSE

  • The plugin does NOT мае controller PUBLISH_UNPUBLISH_VOLUME capability, the plugin мае node STAGE_UNSTAGE_VOLUME capability, and NodeUnstageVolume has been completed successfully.

OR ELSE

  • The plugin does NOT мае controller PUBLISH_UNPUBLISH_VOLUME capability, nor node STAGE_UNSTAGE_VOLUME capability, and NodeUnpublishVolume паспяхова завершана.

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

Аднак, на жаль, рэалізацыя спецыфікацыі CSI праз sidecar'ы не адпавядае гэтым патрабаванням:

  • У sidecar-кантэйнеры csi-attacher, які і павінен адказваць за наяўнасць патрэбнага прамежку паміж мантаваннямі, пры offline-рэсайзе папросту не рэалізаваны гэты функцыянал. Дыскусію аб гэтым ініцыявалі тут.
  • Што ўвогуле такое sidecar-кантэйнер у дадзеным кантэксце? Сам CSI-убудова не займаецца ўзаемадзеяннем з Kubernetes API, а толькі рэагуе на gRPC-выклікі, якія пасылаюць яму sidecar-кантэйнеры. Апошнія распрацоўваюцца супольнасцю Kubernetes.

У нашым выпадку (CSI-убудова) аперацыя павелічэння дыска выглядае наступным чынам:

  1. Атрымліваем gRPC-выклік ControllerExpandVolume;
  2. Спрабуем павялічыць дыск у API, але атрымліваем памылку аб немагчымасці выканання аперацыі, бо дыск прымантаваны;
  3. Захоўваем ідэнтыфікатар дыска ў map, які змяшчае дыскі, для якіх неабходна выканаць аперацыю павелічэння. Далей для сцісласці будзем зваць гэты map як 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.  This is only for retry speed and its only the overall factor (not per item)
  &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 неабходна перадаць аўтарызаваныя ключы ад сэрвіснага акаўнта. У дакументацыі апісана, як стварыць сэрвісны рахунак і атрымаць ключы.

У агульным - паспрабуйце, а мы будзем рады зваротнай сувязі і новым issues, калі сутыкнецеся з нейкімі праблемамі!

Далейшая падтрымка

У якасці выніку нам хацелася б адзначыць, што гэты CSI-драйвер мы рэалізоўвалі не ад вялікага жадання пацешыцца з напісаннем прыкладанняў на Go, а з прычыны вострай неабходнасці ўнутры кампаніі. Падтрымліваць сваю ўласную рэалізацыю нам не здаецца мэтазгодным, таму, калі Яндэкс праявіць цікавасць і вырашыць прадоўжыць падтрымку драйвера, то мы з задавальненнем перададзім рэпазітар у іх распараджэнне.

Акрамя таго, мусіць, у Яндэкса ў managed-кластары Kubernetes ёсць уласная рэалізацыя CSI-драйвера, якую можна выпусціць у Open Source. Такі варыянт развіцця для нас таксама бачыцца спрыяльным - супольнасць зможа карыстацца правераным драйверам ад пастаўшчыка паслуг, а не ад іншай кампаніі.

PS

Чытайце таксама ў нашым блогу:

Крыніца: habr.com

Дадаць каментар