Наш досвід розробки 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): адже в такому разі необхідно, щоб під додатки, що використовує диск, було зупинено, а це може викликати простий програми.

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

If the plugin has only VolumeExpansion.OFFLINE expansion capability and volume is currently published or available on a node then ControllerExpandVolume MUST called ONLY after either:

  • The plugin has controller PUBLISH_UNPUBLISH_VOLUME здатність і ControllerUnpublishVolume has been invoked успішно.

ІНАКШЕ

  • Plugin does NOT have controller PUBLISH_UNPUBLISH_VOLUME capability, the plugin has node STAGE_UNSTAGE_VOLUME capability, and NodeUnstageVolume було успішно завершено.

ІНАКШЕ

  • Plugin does NOT have 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 проходить успішно, диск монтується, під запускається.

Все виглядає досить просто, але, як завжди, є підводні камені. Збільшенням дисків займається 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

Додати коментар або відгук