Kubernetes.Хранилища данных (Persistent Volumes).NFS

Thank you for reading this post, don't forget to subscribe!

Посто­ян­ные тома (Persistent Volumes, PV) — это еди­ни­цы хра­не­ния, под­го­тов­лен­ные адми­ном. Они не зави­сят от подов и их ско­ро­теч­ной жизни.

Persistent Volume Claim (PVC) — это запро­сы на хра­ни­ли­ще, то есть PV. С PVC мож­но при­вя­зать хра­ни­ли­ще к ноде, и эта нода будет его использовать.

С хра­ни­ли­щем мож­но рабо­тать ста­ти­че­ски или динамически.

При ста­ти­че­ском под­хо­де админ зара­нее, до запро­сов, гото­вит PV, кото­рые пред­по­ло­жи­тель­но пона­до­бят­ся подам, и эти PV вруч­ную при­вя­за­ны к кон­крет­ным подам с помо­щью явных PVC.

На прак­ти­ке спе­ци­аль­но опре­де­лен­ные PV несов­ме­сти­мы с пере­но­си­мой струк­ту­рой Kubernetes — хра­ни­ли­ще зави­сит от сре­ды, вро­де AWS EBS или посто­ян­но­го дис­ка GCE. Для при­вяз­ки вруч­ную нуж­но ука­зать на кон­крет­ное хра­ни­ли­ще в фай­ле YAML.

Ста­ти­че­ский под­ход вооб­ще про­ти­во­ре­чит фило­со­фии Kubernetes: ЦП и память не выде­ля­ют­ся зара­нее и не при­вя­зы­ва­ют­ся к подам или кон­тей­не­рам. Они выда­ют­ся динамически.

Для дина­ми­че­ской под­го­тов­ки мы исполь­зу­ем клас­сы хра­ни­ли­ща. Адми­ни­стра­то­ру кла­сте­ра не нуж­но зара­нее созда­вать PV. Он созда­ет несколь­ко про­фи­лей хра­ни­ли­ща, напо­до­бие шаб­ло­нов. Когда раз­ра­бот­чик дела­ет запрос PVC, в момент запро­са один из этих шаб­ло­нов созда­ет­ся и при­вя­зы­ва­ет­ся к поду.

Вот так, в самых общих чер­тах, Kubernetes рабо­та­ет с внеш­ним хранилищем.

Работа с дисками в Kubernetes

Рабо­та с дис­ко­вы­ми тома­ми в Kubernetes про­хо­дит по сле­ду­ю­щей схеме:

  1. Вы опи­сы­ва­е­те типы фай­ло­вых хра­ни­лищ с помо­щью Storage Classes и Persistent Volumes. Они могут быть совер­шен­но раз­ны­ми от локаль­ных дис­ков до внеш­них кла­стер­ных систем и дис­ко­вых полок.
  2. Для под­клю­че­ния дис­ка к поду вы созда­е­те Persistent Volume Claim, в кото­ром опи­сы­ва­е­те потреб­но­сти пода в досту­пе к хра­ни­ли­щу — объ­ем, тип и т.д. На осно­ве это­го запро­са исполь­зу­ют­ся либо гото­вые PV, либо созда­ют­ся под кон­крет­ный запрос авто­ма­ти­че­ски с помо­щью PV Provisioners.
  3. В опи­са­нии пода добав­ля­е­те инфор­ма­цию о Persistent Volume Claim, кото­рый он будет исполь­зо­вать в сво­ей работе.

Persistent Volumes

Услов­но Persistent Volumes мож­но счи­тать ана­ло­гом нод в самом кла­сте­ре Kubernetes. Допу­стим, у вас есть несколь­ко раз­ных хра­ни­лищ. К при­ме­ру, одно быст­рое на SSD, а дру­гое мед­лен­ное на HDD. Вы може­те создать 2 Persistent Volumes в соот­вет­ствии с этим, а затем выде­лять подам место в этих томах. Кубер­не­тис под­дер­жи­ва­ет огром­ное коли­че­ство под­клю­ча­е­мых томов с помо­щью плагинов.

Вот спи­сок наи­бо­лее популярных:

  • NFS
  • iSCSI
  • RBD
  • CephFS
  • Glusterfs
  • FC (Fibre Channel)

Пол­ный спи­сок мож­но посмот­реть в доку­мен­та­ции.

Persistent Volume Claim

PersistentVolumeClaim (PVC) есть не что иное как запрос к Persistent Volumes на хра­не­ние от поль­зо­ва­те­ля. Это ана­лог созда­ния Pod на ноде. Поды могут запра­ши­вать опре­де­лен­ные ресур­сы ноды, то же самое дела­ет и PVC. Основ­ные пара­мет­ры запроса:

  • объ­ем pvc
  • тип досту­па

Типы досту­па у PVC могут быть следующие:

  • ReadWriteOnce – том может быть смон­ти­ро­ван на чте­ние и запись к одно­му поду.
  • ReadOnlyMany – том может быть смон­ти­ро­ван на мно­го подов в режи­ме толь­ко чтения.
  • ReadWriteMany – том может быть смон­ти­ро­ван к мно­же­ству подов в режи­ме чте­ния и записи.

Огра­ни­че­ние на тип досту­па может нала­гать­ся типом само­го хра­ни­ли­ща. К при­ме­ру, хра­ни­ли­ще RBD или iSCSI не под­дер­жи­ва­ют доступ в режи­ме ReadWriteMany.

Один PV может исполь­зо­вать­ся толь­ко одним PVС. К при­ме­ру, если у вас есть 3 PV по 50, 100 и 150 гб. При­хо­дят 3 PVC каж­дый по 50 гб. Пер­во­му будет отда­но PV на 50 гб, вто­ро­му на 100 гб, тре­тье­му на 150 гб, несмот­ря на то, что вто­ро­му и тре­тье­му было бы доста­точ­но и 50 гб. Но если PV на 50 гб нет, то будет отда­но на 100 или 150, так как они тоже удо­вле­тво­ря­ют запро­су. И боль­ше никто с PV на 150 гб рабо­тать не смо­жет, несмот­ря на то, что там еще есть сво­бод­ное место.

Из-за это­го нюан­са, нуж­но вни­ма­тель­но сле­дить за доступ­ны­ми тома­ми и запро­са­ми к ним. В основ­ном это дела­ет­ся не вруч­ную, а авто­ма­ти­че­ски с помо­щью PV Provisioners. В момент запро­са pvc через api кла­сте­ра авто­ма­ти­че­ски фор­ми­ру­ет­ся запрос к storage provider. На осно­ве это­го запро­са хра­ни­ли­ще созда­ет необ­хо­ди­мый PV и он под­клю­ча­ет­ся к поду в соот­вет­ствии с запросом.

Storage Classes

StorageClass поз­во­ля­ет опи­сать клас­сы хра­не­ния, кото­рые пред­ла­га­ют хра­ни­ли­ща. Напри­мер, они могут отли­чать­ся по ско­ро­сти, по поли­ти­кам бэка­па, либо каки­ми-то еще про­из­воль­ны­ми поли­ти­ка­ми. Каж­дый StorageClass содер­жит поля provisioner, parameters и reclaimPolicy, кото­рые исполь­зу­ют­ся, что­бы дина­ми­че­ски созда­вать PersistentVolume.

Мож­но создать дефолт­ный StorageClass для тех PVC, кото­рые его вооб­ще не ука­зы­ва­ют. Так же storage class хра­нит пара­мет­ры под­клю­че­ния к реаль­но­му хра­ни­ли­щу. PVC исполь­зу­ют эти пара­мет­ры для под­клю­че­ния хра­ни­ли­ща к подам.

Важ­ный нюанс. PVC зави­сим от namespace. Если у вас SC будет с сек­ре­том, то этот сек­рет дол­жен быть в том же namespace, что и PVC, с кото­ро­го будет запрос на подключение.

Работа с PV в кластере

Про­ве­рим, есть ли у нас какие-то PV в кластере.

[root@minikub ~]# kubectl get pv
No resources found.

Ниче­го нет. Попро­бу­ем создать и при­ме­нить какой-нибудь PVC и про­ве­рим, что про­изой­дет. Для при­ме­ра созда­ем pvc.yaml сле­ду­ю­ще­го содержания.

[root@minikub my-project]# cat pvc.yaml

[codesyntax lang="php"]

[/codesyntax]

Про­сим выде­лить нам 1 гб про­стран­ства с типом досту­па ReadWriteMany. При­ме­ня­ем yaml.

Ждем немно­го и про­ве­ря­ем ста­тус pvc.

NFS хранилище в качестве Persistence Volume

Для про­сто­ты я взял в каче­стве хра­ни­ли­ща PV nfs сер­вер. Для него не суще­ству­ет встро­ен­но­го Provisioner, как к при­ме­ру, для Ceph

у нас уста­нов­лен nfs сервер:
192.168.1.82 с дирек­то­ри­ей /nfs
и кли­ент, наш миникуб
192.168.1.120 с дирек­то­ри­ей /nfs-client

[root@minikub my-project]# df -h | grep /nfs-client
192.168.1.82:/nfs 20G 44M 19G 1% /nfs-client

на кли­ен­те не забы­ва­ем поста­вить все необ­хо­ди­мые пакеты:

Созда­ем yaml файл pv-nfs.yaml с опи­са­ни­ем Persistence Volume на осно­ве NFS.

[root@minikub my-project]# vim pv-nfs.yaml

[codesyntax lang="php"]

[/codesyntax]

Reclaim Policy

persistentVolumeReclaimPolicy — что будет про­ис­хо­дить с pv после уда­ле­ния pvc. Могут быть 3 варианта:

  1. Retain — pv уда­лен не будет.
  2. Recycle — pv будет очищен.
  3. Delete — pv будет удален.

Так как у нас нет Provisioner для nfs, уда­лять авто­ма­ти­че­ски pv не полу­чит­ся. Так что у нас толь­ко 2 вари­ан­та — либо остав­лять дан­ные (retain), либо очи­щать том (recycle).

Добав­ля­ем опи­сан­ный pv в кла­стер kubernetes.

[root@minikub my-project]# kubectl apply -f pv-nfs.yaml
persistentvolume/my-nfs-share created

Про­ве­ря­ем спи­сок pv и pvc

Мы про­си­ли в pvc толь­ко 1 Гб хра­ни­ли­ща, но в pv было толь­ко хра­ни­ли­ще с 10 Гб и оно было выда­но. Как я и гово­рил рань­ше. Так что в слу­чае руч­но­го созда­ния PV и PVC нуж­но самим сле­дить за раз­ме­ром PV.

Подключаем хранилище к поду

Теперь под­клю­чим наш PVC в виде volume к како­му-нибудь поду. Опи­шем его в кон­фи­ге pod-with-nfs.yaml

[codesyntax lang="php"]

[/codesyntax]

При­ме­ня­ем.
# kubectl apply -f pod-with-nfs.yaml

Затем про­ве­ряй­те ста­тус запу­щен­но­го пода.

Зай­дем в его кон­соль и посмот­рим, под­мон­ти­ро­ва­лась ли nfs шара.

# kubectl exec -it pod-with-nfs sh

набрав
mount | grep nfs
мы уви­дим наш под­мон­ти­ро­ван­ный раздел

Попро­бу­ем теперь что-то запи­сать в шару. Сно­ва захо­дим на под и созда­ем тек­сто­вый файл.

# kubectl exec -it pod-with-nfs sh
# echo "test text" >> /mnt/nfs/test.txt

Теперь запу­стим еще один под и под­клю­чим ему этот же pvc. Для это­го про­сто немно­го изме­ним преды­ду­щий под, обо­звав его pod-with-nfs2.

[codesyntax lang="php"]

[/codesyntax]

Запус­ка­ем под и захо­дим в него.
# kubectl apply -f pod-with-nfs2.yaml
# kubectl exec -it pod-with-nfs2 sh
# cat /mnt/nfs/test.txt
test text

Все в поряд­ке, файл на месте. С внеш­ни­ми хра­ни­ли­щем в Kubernetes закончили.

Local Persistent Volume — хранилище Kubernetes на локальных дисках

Локаль­ные тома, как и nfs, не име­ют встро­ен­но­го про­ви­зи­о­не­ра, так что наре­зать их мож­но толь­ко вруч­ную, созда­вая PV. Есть внеш­ний provisioner — https://github.com/kubernetes-sigs/sig-storage-local-static-provisioner, но лич­но я его не проверял.

Созда­ем SC sc-local.yaml.

[codesyntax lang="php"]

[/codesyntax]

Созда­ем вруч­ную PV pv-local-node-1.yaml, кото­рый будет рас­по­ла­гать­ся на kub-node-1 в /mnt/local-storage. Эту дирек­то­рию необ­хо­ди­мо вруч­ную создать на сервере.

[codesyntax lang="php"]

[/codesyntax]

Созда­ем PVC pvc-local.yaml для запро­са сто­рей­джа, кото­рый пере­да­дим поду.

[codesyntax lang="php"]

[/codesyntax]

И в завер­ше­нии созда­дим тесто­вый POD pod-with-pvc-local.yaml для про­вер­ки рабо­ты local storage.

[codesyntax lang="php"]

[/codesyntax]

При­ме­ня­ем все выше­пе­ре­чис­лен­ное в стро­го опре­де­лен­ном порядке:

  1. SC
  2. PV
  3. PVC
  4. POD

После это­го посмот­ри­те ста­тус всех запу­щен­ных абстракций.

Про­ве­рим, что Local Persistent Volume пра­виль­но рабо­та­ет. Зай­дем в под и созда­дим тесто­вый файл.

# kubectl exec -it pod-with-pvc-local sh
# echo "local srorage test" >> /mnt/local.txt

Теперь идем на сер­вер kub-node-1 и про­ве­ря­ем файл.

Все в поряд­ке. Файл создан.

Если у вас воз­ник­ли какие-то про­бле­мы с POD, то в PVC будет ошибка:

И в это же вре­мя в поде:

Воз­ник­ли ошиб­ки из-за того, что в опи­са­нии PV я ошиб­ся в назва­нии сер­ве­ра, где будет досту­пен local storage. В ито­ге pod запус­кал­ся, про­ве­рял pvc, а pvc смот­рел на pv и видел, что адрес ноды с pv не соот­вет­ству­ет име­ни ноды, где будет запу­щен pod. В ито­ге все висе­ло в ожи­да­нии раз­ре­ше­ния этих несоответствий.