Як працюють volumes та storage в Kubernetes

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

Всім привіт! Мене звати Олександр Рихальський, працюю Senior DevOps Engineer у київському центрі розробки SoftServe. У своїй повсякденній роботі дуже часто доводиться розв’язувати інженерні задачі, пов’язані із підтримкою, розвитком й оптимізацією наявних та впровадженням нових хмарних сервісів для замовників із різних куточків земної кулі.

За моїми спостереженнями Kubernetes (k8s) використовується на 8 з 9 проєктах. Навички роботи з ним є обов’язковими у світі DevOps/SRE. K8s — складна система для керування та автоматизації роботи із контейнеризованими застосунками, й охопити її всю за одну публікацію неможливо. Тому в цій статті я хотів би сфокусуватися на тому, як працювати зі storage в k8s, що може бути корисним інженерам із різних IT-спеціальностей.

Нижче зупинюсь на основних складниках системи керування постійними (persistent) та тимчасовими (ephemeral) даними в k8s, як-то диски (volumes) та їх типи (nfs, CSI, EBS/GCE провайдери), StorageClass. А також на тому, яким чином перелічені вище елементи працюють разом. Наприкінці статті поділюсь кількома випадками з практики, що якраз стосуються теми.

Volumes та storage в Kubernetes: theoretical minimum

Volumes та storage в Kubernetes відіграють ключову роль в керуванні постійними (persistent) та тимчасовими або ефемерними (ephemeral) даними для контейнеризованих застосунків. За замовчуванням контейнери в Kubernetes є «stateless» та ephemeral, і будь-які дані, що зберігаються в контейнері, будуть втрачені у випадку його видалення.

Іншим викликом для сторейджу в Kubernetes є випадок, коли кілька контейнерів в рамках одного поду (Pod) повинні мати доступ до одних і тих же файлів. Для розв’язання таких задач в k8s якраз і використовуються persistent volumes та інші абстракції системи сторейджу (storage abstractions).

Основні типи Kubernetes Volumes

За своєю сутністю k8s volume — це певна директорія, можливо, із даними, до якої мають доступ контейнери в поді. Kubernetes підтримує такі основні типи volumes: Ephemeral Volumes та Persistent Volumes.

Ephemeral Volumes існують доти, доки існує под, і разом із видаленням поду видаляється й volume з усіма даними. На противагу Persistent Volumes можуть існувати незалежно від життєвого циклу поду. Спільною рисою даних типів k8s Volumes є те, що дані зберігаються у випадку рестарту поду.

Ephemeral Volumes. Цей тип k8s volumes використовується для розв’язання таких практичних задач:

  • Застосунки (applications), які потребують додаткового місця для сторейджу, але для них немає значення чи зберігаються дані після перезапуску подів — або в разі їх видалення і повторного створення. Прикладом можуть бути сервіси, що кешують (caching services). У таких сервісів основне навантаження, як правило, припадає на оперативну пам’ять, обсяг якої не є нескінченним. З метою її звільнення такі застосунки можуть частину даних переносити в повільніший сторейдж.
  • Застосунки, які потребують присутності на файловій системі read-only даних на кшталт конфігураційних файлів або secret keys.

До основних видів Ephemeral Volumes належать:

  • emptyDir — пуста директорія, що залишається такою при запуску поду, а всі необхідні дані беруться локально із k8s ноди (k8s node). Зазвичай це кореневий каталог або RAM.
  • configMap, secret — дають змогу «інжектувати» різні типи Kubernetes даних (ConfigMap, Secret) в под.
  • CSI ephemeral volumes — подібні до викладених вище типів volumes, але для своєї роботи вимагають встановлення спеціальних CSI-драйверів.
  • generic ephemeral volumes — тип Volumes, що може бути створений за допомогою тих же storage-драйверів, які також підтримують створення persistent volumes

Persistent Volumes. Для застосунків, що потребують збереження даних незалежно від життєвого циклу поду (наприклад, бази даних або інші «stateful» сервіси), kubernetes використовує Persistent Volumes (PV) та Persistent Volume Claims (PVC).

PersistentVolume (PV) — є елементом storage в кластері, який створюється адміністратором або динамічно, або за допомогою так званих Storage Classes. Життєвий цикл PV не залежить від будь-якого поду, що його використовує.

PersistentVolumeClaim (PVC) — цей k8s-об’єкт можна розглядати як запит від користувача на створення storage. Якщо провести паралель із подом, то под використовує наявні ресурси k8s ноди, а PVC використовує ресурси PV. Як і у випадку із подом, ми можемо вказати, скільки ресурсів (CPU та memory) він може споживати. У випадку PVC ми вказуємо розмір необхідного дискового простору та режим доступу (ReadWriteOnce, ReadOnlyMany, ReadWriteMany, див. AccessModes).

PersistentVolumeClaims дають можливість використовувати лише абстрактні ресурси. Але для розв’язання тих чи інших задач може виникнути необхідність мати PersistentVolumes із різними параметрами продуктивності (IOPS, пропускна здатність тощо). Для того, щоб надати користувачу можливість вибирати тип PV без розкриття технічних деталей імплементації сторейджу, існує окремий тип k8s-ресурсу — StorageClass.

StorageClass є механізмом, що дає змогу адміністраторам описати класи або різновиди storage, які пропонуються. Різні класи storage можуть відповідати різним рівням QoS, політикам резервного копіювання, storage backend (NFS, EBS, gceDisk тощо). Кожен StorageClass-об’єкт містить такі поля, як provisioner, parameters та reclaimPolicy, що використовуються при динамічному створенні PersistentVolume, який належить до даного класу відповідно до PersistentVolumeClaim (PVC) специфікації.

Lifecycle of a volume and claim, або Як це все працює разом

Як було сказано вище, PV є ресурсом в k8s-кластері, а PVC — запит на ці ресурси. Взаємодію між PV та PVC можна описати за допомогою представленої нижче схеми Kubernetes cookbook, згідно з якою процес виділення Storage можна розділити на такі етапи.

  1. Provisioning. PVs можуть створюватися статично або динамічно. При статичному створенні PV всі операції здійснюються адміністратором k8s-кластера. Динамічне створення PV здійснюється за допомогою StorageClasses: у PVC вказується, який storage class використовувати, але задані StorageClasses мають бути встановленими в k8s-кластері заздалегідь.
  2. Binding. На цьому етапі користувач створює PVC із заданими об’ємом storage та режимом доступу. Після цього Kubernetes control plane до кожного новоствореного PVC знаходить відповідний PV і зв’язує (bind) їх.
  3. Using. Kubernetes-поди використовують PVC як volume. При цьому кластер відстежує PVC та монтує відповідний volume до вказаного поду.

На практиці все вказане вище може виглядати наступним чином. В специфікації поду в секції «volumes» користувач вказує назву PVC під параметром persistentVolumeClaim. Точка монтування даного volume вказується окремо для кожного контейнеру в поді.

Наприклад:

apiVersion: v1
kind: Pod
metadata:
 name: my-pod
spec:
 containers:
 - name: my-container
   image: my-image
   volumeMounts:
   - mountPath: "/data"
     name: my-volume
 volumes:
 - name: my-volume
   persistentVolumeClaim:
     claimName: my-pvc

Де PVC з назвою «my-pvc» використовується як persistent storage для поду «my-pod», точкою монтування в контейнері є директорія /data.

У випадку використання StorageClass маніфест для PVC може виглядати так:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
 name: my-pvc
spec:
 storageClassName: fast
 accessModes:
   - ReadWriteOnce
 resources:
   requests:
     storage: 10Gi

В цьому прикладі ми за допомогою PVC створюємо запит на виділення 10Gi дискового простору, використовуючи StorageClass «fast».

Приклад із власного досвіду

Кейс 1: обмеження кількості PV, які можна примонтувати до одного k8s ноду в Cloud

Героями цієї історії є AWS Cloud та EKS (Amazon Elastic Kubernetes Service). Клієнт мав багато k8s-кластерів різного розміру від 30 до 200 нод кожен залежно від енву. Основним типом інстансів був С5, R5, k8s-версія на той момент була 1.23. Команд розробників та QA-інженерів також працювало немало і щоб кожна із них могла працювати над своїми задачами, не впливаючи на роботу інших, їм пропонувалось створювати для роботи повну копію їхнього продукту. Продукт являв собою цілу екосистему, що складалась із 30+ мікросервісів, і з них 12 потребували persistent storage (PV+PVC). Бекендом до сторейджу виступав EBS CSI Driver.

До певного моменту все працювало чудово, але в один прекрасний спринт розробники почали скаржитися на те, що «падають» CI/CD джоби, які створюють відповідні енви й багато сервісів не є доступними. У процесі аналізу було виявлено, що відповідні поди «застрягали» на стадії створення в очікуванні аттачменту відповідного PV. З погляду кластера спостерігалось багато подів, що застрягли в стані «Pending».

При цьому describe pod давав наступні записи в секції івентів:

FailedScheduling ….. pod has unbound immediate PersistentVolumeClaims

Далі настав час читати документацію і виявилося, що є два типи лімітів за кількістю дисків, які можна приєднати до одного інстансу:

1. Ліміти Cloud-провайдера.

2. Ліміти Kubernetes. Зараз про цю проблему є окрема сторінка в документації, але на той момент довелось інформацію збирати по частинах. В моєму випадку в Kubernetes scheduler були встановлено ліміти для Amazon Elastic Block Store (EBS) за замовчуванням в 39 volumes per Node. Але згідно лімітів самого AWS для інстансів типу C5, R5 можна було приєднати лише 25 EBS дисків. Тобто в умовах «низького» навантаження на k8s-кластер все працювало нормально, але коли багато команд розробників майже в один і той же проміжок часу (часто при підготовці до релізу) почали створювати собі енви, існувала висока ймовірність «впертись» в обмеження.

Які можна запропонувати стратегії виходу із цієї ситуації:

  1. Збільшення кількості інстансів у кластері.
  2. Модернізація безпосередньо мікросервісів із метою зменшення кількості необхідних PV+PVC. В моєму випадку це можна було зробити, бо енв мав кілька інстансів Elasticsearch та Postgresql, для яких була технічна можливість використовувати один Deployment/StatefulSet замість 2-3.
  3. Міграція із persistent volume на NFS. Тобто, міграція з EBS на EFS.

Що ж до конкретно мого кейсу, то як швидкий фікс брали опцію 1 — тимчасове збільшення кількості інстансів у кластері, щоб розблокувати команди розробників і виграти трохи часу. А далі — опція 3, де була виконана міграція з EBS на EFS.

Кейс 2: EFS throughput

Другий кейс є продовженням першого і пов’язаний з особливостями роботи з EFS (NFS у виконанні AWS), а точніше — із її пропускною здатністю. Згідно з документацією, EFS має кілька типів налаштувань пропускної здатності (throughput):

  • Elastic throughput — чудовий вибір, коли вимоги до пропускної здатності важко передбачити через те, що ваші сервіси можуть мати несподівані «спайки» — короткочасне і несподіване зростання кількості ресурсів, які споживає аппка або сервіс (більше тут: Elastic throughput).
  • Provisioned throughput — цей режим найкраще підходить до випадків, коли точно відомо, яка пропускна здатність необхідна для EFS (наприклад, 75 Мбіт/с) і дозволяє статично задати саме бажані значення (більше тут: Provisioned throughput).
  • Bursting throughput — цей режим має сенс встановлювати якщо ми хочемо, щоб пропускна здатність збільшувалась пропорційно збільшенню об’єму даних, що зберігається в EFS (більше тут: Bursting throughput).

Від самого початку міграції k8s storage на EFS із погляду економії коштів був встановлений режим Bursting throughput, бо залежно від енву мали від 100-500 Гб даних. Знову ж таки, у випадку низького навантаження на кластер все працювало як треба. Але при інтенсивному створенні нових енвів розробниками запускалось відносно багато операцій ініціалізації баз даних і дуже швидко досягалися ліміти пропускної здатності EFS + burst, що можна було чітко побачити на графіках в CloudWatch. наступним логічним кроком стало встановлення Provisioned throughput залежно від енву і це остаточно розв’язало проблему із k8s storage для нашого клієнта.

Висновок

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

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

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

Вітаю, дякую за статтю. Приємно бачити детальні технічні статті.
Маю декілька питань/ зауважень ( прошу одразу зауважити мій початковий рівень з к8с):
1) В статті Ви зауважуєте, що для кожного контейнеру треба вказувати окремо цей PVC — було б гарно якби і в прикладах це було продемонстроване, типу my_container_1 та my_container_2

2) Посилання на інші ресурси, особливо книжки, завжди вітаються, проте я думаю в самій книзі є пряма заборона на використання матеріалів без попереднього дозволу, навряд чи такий є для схеми. Що скаже поважна редакція?

Вітаю!
1) Приклад для кількох контейнерів спробую додати;
2) Copyright для книжки: «No parts of this book may be reproduced in any manner whatsoever without written permission except in the case of brief quotations embodied in critical articles and reviews». В даному випадку, малюнок подано як він є, без змін чи редагування та надано посилання на першоджерело — це є цитування. Якби взагалі не допускалось використання матеріалів книжок, статей та ін літератури без письмової згоди, то, банально, дипломну роботу було би вкрай важко написати, бо замахаєшся десяткам редакцій писати запити щодо дозволу використання матеріалів, якщо треба вставити якусь схему чи формулу із підручника або книжки. Якщо казати про наукові публікації, то там дозволяється, в залежності від редакції, до 22 % (проте зараз є тренд зменшення цього відсотка до рівня 11-17% ) роботи було «плагіатом» — тобто містило передрук матеріалів оригінальних робіт слово-в-слово (окремі формулювання, речення тощо).

По перше можна заюзати адони які посередник між різними сторедж адонами, в PV / PVC storage class вказуєш не ebs-sc а посередника. А сам посередник вже створює реальні стореджи юзаючи різні драйвера типу aws ebs csi , s3 efs. Це дозволяє не думати куди приатачиться ebs і з якого акаунта і регіона і az цей еbs. Бо якщо напряму юзати ebs драйвер то там купа обмеженнь по az , по кількості атачів.

Також рішення цієї задачі може бути і в тимчасових нодах створених autoscaler. Якщо сервіс не юзається постійно створюй ноди тимчасово під поди. Є адони які можуть запускати ноди по шедулу наприклад з 9 ранку до 18 вечора лише. Це дозволяє не юзати дорогу ноду с купою подів на ній 24/7 , а запускати дешеві ноди під конкретні поди по лейблу , по потрібних умовах.

rook / longhorn краще б вже розглянули — то має більш конкретне застосування для ентерпрайсу з закритими кластерами.

Дякую за відгук, розглянемо можливість освітити rook/longhorn, якщо цей кейс є актуальним для спільноти

Було б класно прочитати статтю ще й про цей кейс. Вже з нетерпінням чекаю 😊

Чомусь також очікував побачити Ceph, як банальний приклад стореджа для ентерпрайзу (з усима плюсами і мінусами), а під капотом виявилась типова «стаття» на доу.

Ви мали на увазі:

(з усіма плюсами і мінусами)

Відверто кажучи, я би не сказав, що Ceph — банальний сторадж. Банальний — то щось типу GlusterFS))

ну хоч би не полінився й показав би як створити pv + pvc вручну

пройшовся б по accessmodes, про rwop

читав би оце якийсь junior cto, то ніфіга не навчився б

Дякую за коментар! Врахую у наступних випусках. Посилання на офіційну документацію, де описані accessmodes, у тексті наведені. Якщо не лінуватися, то можна перейти і ознайомитися

Якщо не лінуватися

якщо не лінуватися то можна й писати про базові речі в статті «як працює»

а то якось вийшла стаття «як працює» де аж один нещасний автоматний pvc на кілька тищ знаків

У даній статті на меті було освітити теоретичний мінімум, а не освітити всі аспекти сторейджу. Далеко не кожному треба високий рівень деталізації + в контексті DevOps практик та автоматизації рутинних процесів мені важко уявити щоб хтось десь створював pv+pvc вручну (або це якийсь дуже рідкісний та особливий випадок). Де треба, наведено посилання на першоджерела та офіційну документацію. Якщо є бачення як покращити інформаційне наповнення, то можна завжди написати свою статтю. Тема сторейджу в k8s редакцією ДОУ була вказана я така, що є актуальною, і тут можна написати не одну дисертацію.

Ну, якщо писати так, щоб потім всіх зацікавлених гнати читати оф документацію, та виправдовувати це тим, що «доу визначили тему актуальною», то тоді краще вже нічого не писати.

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