Узнайте о преимуществах использования GlusterFS и о том, как это может помочь в создании масштабируемой распределенной файловой системы.

Вступление

Всем, кто сталкивался с платформами оркестровки контейнеров, чаще всего с Kubernetes, хорошо известно, что управление хранилищем может быть настоящей проблемой. Не из-за сложности или количества базовых компонентов, а из-за динамического характера архитектуры такой платформы.

Работая внутри распределенного кластера, Kubernetes может манипулировать контейнерами и модулями всевозможными способами - создавать их, уничтожать, реплицировать, автомасштабировать и т. Д. И как сопровождающий, вы часто не знаете, на какой физической (или виртуальной) машине они будут быть размещен на. Следовательно, для доступа к постоянному хранилищу необходимо использовать какие-то внешние службы предоставления данных.

В Kubernetes есть концепция постоянного тома, которая может быть реализована множеством способов. Сейчас одним из самых простых и часто используемых, конечно же, является NFS. Все, что вам нужно, это установить Helm chart и использовать созданный StorageClass в любом объекте. Но, несмотря на то, что такая практика настолько проста и легка для начала, к сожалению, она не так эффективна и гибка, как иногда может потребоваться.

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

При поиске распределенной или кластерной файловой системы выбор в конечном итоге сводится к нескольким вариантам. Сегодня, конечно же, выделяются GlusterFS и CephFS. Однако я не рекомендую делать выбор, основываясь только на этой статье. Проведите собственное исследование. Потому что кроме этих, доступно множество других опций, таких как GFS 2, Lustre, MinIO , MooseFS и другие. Я также рекомендую попробовать Rook, который на самом деле не является DFS, а скорее облачным оркестратором хранилища, предназначенным для расширения возможностей управления хранилищем Kubernetes.

И GlusterFS, и CephFS представляют собой системы программно-определяемого хранилища с открытым исходным кодом (SDN), которые обеспечивают распределенное, высокомасштабируемое и высокодоступное блочное и объектное хранилище соответственно. Оба имеют определенные преимущества перед собственными решениями. Настоящая разница между ними начинает проявляться по мере увеличения размера хранилища. Gluster хорошо работает на более высоких масштабах, которые могут увеличиваться с терабайт до петабайт за короткое время. С другой стороны, Ceph, благодаря своему формату хранения, обеспечивает более эффективное краткосрочное хранилище, к которому пользователи обращаются чаще.

Углубляясь в архитектуру

Решение файловой системы Gluster на более высоком уровне абстракции состоит из трех основных компонентов:

  • Серверы Gluster, установленные в качестве блоков хранения на узлах K8s с помощью DaemonSet
  • Клиенты Gluster устанавливаются непосредственно на физические машины и подключаются к серверам через такие протоколы, как TCP / IP и напрямую через сокеты.
  • Management API для абстрагирования от низкоуровневых манипуляций с томами

Справедливо сказать, что последний (API) не обязательно нужен, поскольку вы можете создавать выделенные конечные точки и использовать их для ручного создания постоянных томов самостоятельно.

Однако я думаю, что это еще больше усложнит процесс его использования. В отличие от этого API управления предоставляет автоматизированный интерфейс для создания, изменения размера и удаления PV с помощью K8s StorageClass. Таким образом, вам действительно нужно позаботиться о развертывании и настройке только один раз. Если вы спросите меня, я скажу, что это гораздо более привлекательный и элегантный способ.

Вот почему в этом руководстве мы будем использовать Heketi - фреймворк управления томами на основе RESTful, написанный в основном на Go, что упрощает использование GlusterFS.

Инструкция

Прежде чем мы начнем, стоит упомянуть, что я напишу об этом так, чтобы это было более конкретно для Kubernetes, управляемого DigitalOcean, поскольку это мой предпочтительный поставщик облачной инфраструктуры. Конечно, будут части, общие для всех провайдеров - на самом деле, большинство из них. Так что, если вы используете что-то вроде GKE или Amazon EKS, вы можете остаться здесь.
Хорошо, если все улажено, давайте, наконец, приступим к делу!

Доступ к наземной операционной системе узла

Чтобы сначала развернуть GlusterFS, вам необходимо создать раздел на диске, поскольку он низкоуровневый, Gluster не может работать с уже существующей файловой системой и требует полностью необработанного дискового устройства или его раздела.

Узлы DigitalOcean K8s работают поверх операционной системы Debian 10 (Buster), и, прежде всего, вам необходимо получить к ней доступ. Поскольку внутренние узлы K8s также являются каплями DO (кодовое название управляемого VPS), вы можете сделать это на специальной вкладке в консоли управления каплями.

Войдя в систему, приготовьтесь скопировать и вставить (или ввести вручную, если вы действительно хотите) множество команд, поскольку иногда это может быть довольно неприятно, учитывая чрезвычайное удобство веб-консоли DO.

Создание нового раздела на диске

Мы будем использовать загрузочную живую систему GRML для сжатия существующего раздела диска. Устанавливаем через apt-get утилиту:

$ apt-get install grml-rescueboot

И загрузите загрузочный образ из официального источника в загрузочный каталог. Я использовал последнюю доступную на тот момент версию. Затем настройте его:

$ cd /boot/grml
$ wget http://download.grml.org/grml64-small_2020.06.iso
$ update-grub

Перед перезагрузкой системы я рекомендую проверить доступные дисковые устройства с помощью утилиты lsblk. Ваш результат, вероятно, будет таким же, но его все же стоит проверить:

$ lsblk
NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
vda    254:0    0    80G  0 disk 
├─vda1 254:1    0  79.9G  0 part /
└─vda2 254:2    0     2M  0 part 
vdb    254:16   0   446K  0 disk

Это означает, что у вас есть одно основное дисковое устройство (vda ), которое уже разделено на два раздела. Конечно, вас интересует более крупный (vda1), потому что размера vda2 достаточно только для хранения BIOS. На следующих шагах вы сначала уменьшите vda1, чтобы безопасно вырезать новый раздел на vda устройстве.

Получив карту разделов, перезагрузите систему, набрав reboot. Во время загрузки быстро выберите эти параметры:

  • Grml Rescue System
  • grml64-small — advance options
  • grml64-small — copy Grml to RAM

После этого вы попадете в консоль Grml, где сможете изменить раскладку клавиатуры. Если вам подходит США по умолчанию, просто нажмите Return .

Используйте resize2fs, чтобы сжать существующую файловую систему:

$ fsck.ext4 -f /dev/vda1
$ resize2fs -M /dev/vda1

Затем используйте parted, чтобы изменить его размер и создать новый:

$ parted /dev/vda
$ resizepart 1 30G # shrink vda1 to 30 Gb
$ mkpart gluster 30001 100% # create vda3 where vda1 ends
$ set 3 lvm on # optionally setup logical volumes
$ print # verify results
$ quit

В моем случае я создаю новый раздел размером 50 ГБ, поэтому я изменяю размер существующей части до 30 ГБ. Если вы хотите чего-то другого, посчитайте сами. Вы всегда можете еще раз проверить ситуацию с помощью утилиты fdisk:

$ fdisk -l /dev/vda
Device      Start    End        Sectors   Size  Type       
/dev/vda1   6144     62920671   62914528  50G   Linux filesystem       /dev/vda2   2048     6143       4096      2M    BIOS boot       
/dev/vda3   62920672 1667770111 104857546 30G   Linux filesystem

По завершении перезагрузите систему еще раз, набрав reboot .

Связывание устройства хранения с помощью символической ссылки

Следующим важным шагом является установка символической ссылки (symlink) для идентификации вновь созданного раздела как устройства хранения GlusterFS. Не рекомендуется использовать имена устройств, такие как /dev/vda3, поскольку имя может измениться между перезагрузками системы. Для этого у вас есть возможность использовать сгенерированные системой символические ссылки или вручную создавать свои собственные.

Чтобы получить уже существующие символические ссылки, просто используйте:

$ ls -altr /dev/disk/* | grep <device>

Для /dev/vda3 это будет /dev/disk/by-path/pci-0000:00:06.0-part3.

Ручной способ немного сложнее, но вы можете указать любое собственное имя. В моем случае это /dev/disk/gluster-disk. Для этого вам нужно сначала получить некоторые атрибуты устройства с помощью утилиты udevadm, указав исходное устройство:

$ udevadm info --root --name=/dev/vda3
P: /devices/pci0000:00/0000:00:06.0/virtio3/block/vda/vda3
N: vda3
E: DEVNAME=/dev/vda3
E: DEVPATH=/devices/pci0000:00/0000:00:06.0/virtio3/block/vda/vda3
E: DEVTYPE=partition
E: MAJOR=254
E: MINOR=3
E: SUBSYSTEM=block
E: TAGS=:systemd:
E: USEC_INITIALIZED=1942797
...

Выделенные атрибуты необходимы для формирования правила символической ссылки, например:

ENV{DEVTYPE}=="partition", ENV{SUBSYSTEM}=="block", ENV{DEVPATH}=="/devices/pci0000:00/0000:00:06.0/virtio3/block/vda/vda3" SYMLINK+="disk/gluster-disk"

Что потом нужно записать в файл пользовательских правил с помощью утилит nano или vi:

$ nano /lib/udev/rules.d/10-custom-icp.rules

Наконец, перезагрузите udev правила, чтобы создать символические ссылки и проверить результаты:

$ udevadm control --reload-rules
$ udevadm trigger --type=devices --action=change
$ ls -ltr /dev/disk/gluster-*
lrwxrwxrwx 1 root root 7 Oct 21 16:20 /dev/disk/gluster-disk -> ../vda3

Настройка клиента Gluster

Наконец, вы можете установить самую важную часть всей этой шарады, которой, конечно же, является Gluster Native Client. Сразу после настройки модуля ядра dm_thin_pool. Здесь нечего раздражать, потому что на самом деле это довольно просто:

$ modprobe dm_thin_pool
$ echo dm_thin_pool | tee -a /etc/modules
dm_thin_pool # verify output

Теперь мы можем приступить к установке клиента. Но прежде чем вы продолжите, я должен предупредить вас, что настоятельно рекомендуется, чтобы версия Gluster Client была как можно ближе к версии сервера. На момент публикации этой статьи последняя доступная контейнерная версия для сервера - 7.1. Итак, я продолжу настраивать клиента с точно такой же версией.

Сначала добавьте ключ GPG в apt:

$ wget -O - https://download.gluster.org/pub/gluster/glusterfs/7/rsa.pub | apt-key add - # install certificate

Затем добавьте источник по мере необходимости:

$ echo deb [arch=amd64] https://download.gluster.org/pub/gluster/glusterfs/7/7.1/Debian/buster/amd64/apt buster main > /etc/apt/sources.list.d/gluster.list

Обновите список пакетов и установите glusterfs-client:

$ apt-get update
$ apt-get install glusterfs-client
$ /usr/sbin/glusterfs --version
glusterfs 7.1

Результаты последней команды будут проверены.

Вот и все! Теперь вы можете закрыть эту неудобную консоль и, наконец, вернуться к своему потрясающему терминалу. Это, конечно, если для желаемой инфраструктуры достаточно одного узла хранения, но это сомнительно. В противном случае вы должны делать то же самое снова для каждого другого узла, пока вы не будете удовлетворены или не устанете от этого. Я получил и то, и другое после того, как проделал это три раза.

Развертывание Gluster Server и Heketi API

На этом самая сложная часть окончена. Осталось только настроить и развернуть два последних компонента - Gluster Server и Heketi API. Ясно, что после всей работы, которую вы проделали на предыдущих этапах, вы заслуживаете несложного и простого способа довести дело до конца. А что может быть проще, чем использовать заранее настроенный Helm график?

Для этого есть даже несколько вариантов:

Хотя это может показаться очевидным выбором, стоит также отметить, что второе решение на самом деле намного проще как для понимания, так и для развертывания. Итак, я еще раз настоятельно рекомендую провести собственное исследование. Как вариант, вы также можете попробовать мое собственное комбинированное решение, которое я сделал в рамках моего проекта Kicksware с открытым исходным кодом.

Общим для всех этих решений является то, что все они используют Heketi в качестве API управления, поэтому для всех них необходимо использовать предопределенную конфигурацию топологии узлов хранения. И хотя это может показаться немного утомительным, на самом деле этот процесс сводится к выполнению одной команды и записи нескольких IP-адресов. Вот как это выглядит:

По сути, все, что вам нужно, - это внутренний IP-адрес каждого узла, имя и имя устройства хранения, которое вы создали на последнем шаге. Если вы точно следовали инструкции, то имя устройства будет /dev/disk/gluster-disk.

Чтобы получить оставшиеся части головоломки, просто введите эту команду:

$ kubectl get nodes -o wide
NAME                  STATUS   AGE   VERSION   INTERNAL-IP kicksware-k8s-3pkt4   Ready    38h   v1.18.8   10.114.0.5    kicksware-k8s-3pkt8   Ready    38h   v1.18.8   10.114.0.3    kicksware-k8s-3pktw   Ready    38h   v1.18.8   10.114.0.4

Имя узла node.hostname.manage, а внутренний IP-адрес соответствует node.hostname.storage. Просто поместите это в values.yaml файл и используйте его при развертывании выбранной диаграммы управления.

Кроме того, в зависимости от выбранного метода (кроме метода IBM) вам может потребоваться вручную ввести IP-адрес кластера службы Heketi в конфигурацию класса хранилища и диаграмму обновления:

$ kubectl get services -lrelease=<RELEASE_NAME>
NAME             TYPE        CLUSTER-IP       PORT(S)    AGE
gluster-heketi   ClusterIP   10.245.189.193   8080/TCP   1m

Когда у вас есть класс хранилища, просто вставляйте его в каждый PersistentVolumeClaim всякий раз, когда вам нужно выделить хранилище, например:

Разве я не говорил вам, что в конце концов это будет кусок пирога! И вот наконец оно. Отличная работа!

Еще одна вещь, если вы используете Kubernetes на основе RBAC, убедитесь, что для приложения Heketi предоставлена ​​правильная привязка ролей. В противном случае он не сможет загрузить топологию, и вы получите это странное сообщение об ошибке:

Unable to create node: New Node doesn't have glusterd running

И хотя это может быть вызвано какой-то проблемой с установкой Gluster Client, вполне вероятно, что у Heketi просто недостаточно прав для доступа к собственным API-интерфейсам K8s к узлам и модулям.

Последние мысли

Из-за своей очень динамичной, почти эфемерной природы парадигма управления хранилищем Kubernetes иногда может сбивать с толку и даже подавлять. Большинство развернутых сервисов по умолчанию не имеют состояния. Однако в реальных производственных сценариях часто требуется, чтобы они были с отслеживанием состояния, то есть имели какое-то внешнее хранилище. Для этого Kubernetes изначально предлагает несколько решений: эфемерные параметры, постоянные тома, заявки на постоянные тома, классы хранилища или StatefulSets.

Кроме того, предоставление хранилища различается в зависимости от статического и динамического способов выполнения. Статический требует от администраторов заранее продумать и настроить PV, что противоречит мировоззрению Kubernetes с точки зрения обработки ресурсов (ЦП, памяти и хранилища) как динамически предоставляемых. Таким образом, динамический способ, который считается более правильным и элегантным, заключается в делегировании ответственности за управление хранилищем механизму K8s, и это делается через StorageClasses.

StorageClass абстрагирует сложный процесс управления распределенным хранилищем, делая его таким же простым, как написание нескольких строк в конфигурации YAML. Однако парадигма StorageClass сначала должна быть реализована с использованием какой-либо внешней службы. И хотя с некоторыми из них также легко начать работу, есть варианты, которые требуют предварительного прохождения сложной процедуры развертывания.

Эта статья была посвящена раскрытию деталей и подводных камней развертывания GlusterFS с предоставлением полного набора инструкций о том, как это сделать с предполагаемой спецификацией в управляемой среде Kubernetes.

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

Однако как разработчик я очень горжусь нашим сообществом за то, что оно так полезно и поддерживает, за которым может последовать любой. Итак, я стараюсь внести свой вклад в это движение.

Благодарю за внимание.
Удачного DevOpsing!