
Поиск оптимального хранилища — это довольно сложный процесс, у всего есть плюсы и минусы. Разумеется, лидером в данной категории является CEPH, но это довольно сложная система, хотя и с очень богатым функционалом. Для нас такая система избыточна, учитывая то, что нам нужно было простое реплицируемое хранилище в режиме master-master на пару терабайт. Изучив много материала, было принято решение протестировать наиболее модный на рынке продукт для интересующей нас схемы. В связи с тем, что готового решения подобного плана найдено не было, хочется поделиться своими наработками по данной теме и описать проблемы, с которыми мы столкнулись в процессе развертывания.
Цели
Что мы ждали от нового хранилища:
- Возможность работы с четным числом нод для репликации.
- Простая установка, настройка, поддержка
- Система должна быть взрослой, проверенной временем и пользователями
- Возможность расширения места на хранилище без простоя системы
- Хранилище должно быть совместимо с Kubernetes
- Должен быть автоматический Failover при падениях одного из узлов
Именно по последнему пункту у нас и возникло много вопросов.
Развертывание
Для развертывания было создано две виртуалки на CentOs 8. К каждой подключено по дополнительному диску с СХД.
Предварительная подготовка
Для GlusterFS нужно выделить отдельный диск с XFS, чтобы он никак не влиял на систему.
Выделяем партицию:
$ fdisk /dev/sdb Command (m for help): n Partition type p primary (0 primary, 0 extended, 4 free) e extended (container for logical partitions) Select (default p): p Partition number (1-4, default 1): 1 First sector (2048-16777215, default 2048): Last sector, +sectors or +size{K,M,G,T,P} (2048-16777215, default 16777215): Created a new partition 1 of type ‘Linux’ and of size 8 GiB. Command (m for help): w The partition table has been altered. Calling ioctl() to re-read partition table. Syncing disks.
Форматируем в XFS и монтируем:
$ mkfs.xfs /dev/sdb1 $ mkdir /gluster $ mount /dev/sdb1 /gluster
И в довершении закидываем запись в /etc/fstab для автоматического монтирования директории при запуске системы:
/dev/sdb1 /gluster xfs defaults 0 0
Установка
По поводу установки написано много статей, в связи с этим сильно углубляться в процесс не будем, просто рассмотрим то, на что стоит обратить внимание.
На обеих нодах ставим и запускаем glusterfs последней версии:
$ wget -P /etc/yum.repos.d https://download.gluster.org/pub/gluster/glusterfs/LATEST/CentOS/glusterfs-rhel8.repo $ yum -y install yum-utils $ yum-config-manager --enable PowerTools $ yum install -y glusterfs-server $ systemctl start glusterd
Дальше надо сказать гластеру где его соседняя нода. Делается только с одной ноды. Важный момент: если у вас доменная сеть, то тут обязательно указывать имя сервера с доменом, иначе в будущем придется все переделывать.
$ gluster peer probe gluster-02.example.com
Если прошло успешно, то проверяем связь командой с обоих серверов:
$ gluster peer status Number of Peers: 1 Hostname: gluster-02.example.com Uuid: a6de3b23-ee31-4394-8bff-0bd97bd54f46 State: Peer in Cluster (Connected) Other names: 10.10.6.72
Теперь можно создать Volume в который мы будем писать.
gluster volume create main replica 2 gluster-01.example.com:/gluster/main gluster-02.example.com:/gluster/main force
Где:
- main — название Volume
- replica — тип Volume (подробнее можно прочитать в официальной документации)
- 2 — количество реплик
Запускаем Volume и проверяем его работоспособность:
gluster volume start main gluster volume status main
Для реплицируемых Volume рекомендуется установить следующие параметры:
$ gluster volume set main network.ping-timeout 5 $ gluster volume set main cluster.quorum-type fixed $ gluster volume set main cluster.quorum-count 1 $ gluster volume set main performance.quick-read on
Этими простыми действиями мы собрали кластер GlusterFS. Осталось подключиться к нему и проверить работоспособность. На клиентской машине установлена Ubuntu, для монтирования нужно установить клиент:
$ add-apt-repository ppa:gluster/glusterfs-7 $ apt install glusterfs-client $ mkdir /gluster $ mount.glusterfs gluster-01.example.com:/main /gluster
Gluster при подключении к одной из нод, отдает адреса всех нод и автоматически подключается ко всем. Если клиент уже подключился, то отказ одной из нод не приведет к остановке работы. Но вот если будет недоступен первый узел, подключиться в случае разрыва сессии уже не получится. Для этого при монтировании можно передать параметр backupvolfile с указанием второй ноды.
mount.glusterfs gluster-01.example.com:/main /gluster -o backupvolfile-server=gluster-02.example.com
Важный момент: gluster синхронизирует файлы между нодами только если их изменение было через монтированный volume. Если делать изменения напрямую на нодах, будет рассинхрон файлов.
Подключение к Kubernetes
На этом этапе и начались вопросы: «А как это подключить?». И здесь есть несколько вариантов. Рассмотрим их.
Heketi
Самый популярный и рекомендуемый — использовать внешний сервис: heketi. heketi — это прослойка между kubernetes и gluster, которая позволяет управлять хранилищем через http и работать с ним. Но heketi и будет той самой единой точкой отказа, т.к. сервис не кластеризуется. Второй инстанс данного сервиса не сможет самостоятельно работать, т.к. любые изменения хранятся в локальной БД. Запуск в kubernetes данного сервиса также не подходит, т.к. ему нужен статичный диск, на котором будет хранится его БД. В связи с этим данный вариант оказался самым не подходящим.
Endpoint в Kubernetes
Если у вас Kubernetes на системах с пакетными менеджерами, то это очень удобный вариант. Смысл заключается в том, что для всех серверов GlusteFS в Kubernetes создается общий Endpoint. На этот Endpoint вешается сервис и к этому сервису мы уже будем монтироваться. Для работы данного варианта необходимо на каждой ноде Kubernetes установить glusterfs-client и убедиться в возможности монтирования. В Kubernetes деплоим следующий конфиг:
apiVersion: v1 kind: Endpoints metadata: name: glusterfs-cluster subsets: - addresses: #Тут указываем ip наших серверов - ip: 10.10.6.71 ports: #Тут просто можно оставить 1, порт роли не играет - port: 1 - addresses: - ip: 10.10.6.72 ports: - port: 1 --- apiVersion: v1 kind: Service metadata: name: glusterfs-cluster spec: ports: - port: 1
Теперь мы можем создать простой тестовый деплоймент и проверить как происходит монтирование. Ниже пример простого тестового деплоймента:
apiVersion: apps/v1 kind: Deployment metadata: name: gluster-test spec: replicas: 1 selector: matchLabels: app: gluster-test template: metadata: labels: app: gluster-test spec: volumes: - name: gluster glusterfs: endpoints: glusterfs-cluster path: main containers: - name: gluster-test image: nginx volumeMounts: - name: gluster mountPath: /gluster
Данный вариант нам не подошел, потому что у нас на всех нодах Kubernetes стоит container-linux. Пакетного менеджера там нет, поэтому установить gluster-client для монтирования не удалось. В связи с этим был найден третий вариант, который и было решено использовать.
GlusterFS + NFS + keepalived
GlusterFS до недавнего времени предлагал собственный NFS сервер, но теперь для NFS используется внешний сервис nfs-ganesha. Об этом написано довольно немного, в связи с этим разбе��ем как это настроить.
Репозиторий нужно прописать вручную. Для этого в файле /etc/yum.repos.d/nfs-ganesha.repo вносим:
[nfs-ganesha] name=nfs-ganesha baseurl=https://download.nfs-ganesha.org/2.8/2.8.0/RHEL/el-8/$basearch/ enabled=1 gpgcheck=1 [nfs-ganesha-noarch] name=nfs-ganesha-noarch baseurl=https://download.nfs-ganesha.org/2.8/2.8.0/RHEL/el-8/noarch/ enabled=1 gpgcheck=1
И устанавливаем:
yum -y install nfs-ganesha-gluster --nogpgcheck
После установки проводим базовую конфигурацию в файле /etc/ganesha/ganesha.conf.
# create new NFS_CORE_PARAM { # possible to mount with NFSv3 to NFSv4 Pseudo path mount_path_pseudo = true; # NFS protocol Protocols = 3,4; } EXPORT_DEFAULTS { # default access mode Access_Type = RW; } EXPORT { # uniq ID Export_Id = 101; # mount path of Gluster Volume Path = "/gluster/main"; FSAL { # any name name = GLUSTER; # hostname or IP address of this Node hostname="gluster-01.example.com"; # Gluster volume name volume="main"; } # config for root Squash Squash="No_root_squash"; # NFSv4 Pseudo path Pseudo="/main"; # allowed security options SecType = "sys"; } LOG { # default log level Default_Log_Level = WARN; }
Нужно стартовать сервис, включить nfs для нашего volume и проверить то, что он включился.
$ systemctl start nfs-ganesha $ systemctl enable nfs-ganesha $ gluster volume set main nfs.disable off $ gluster volume status main
В результате статус должен показать что запустился nfs сервер для нашего volume. Нужно сделать mount и проверить.
mkdir /gluster-nfs mount.nfs gluster-01.example.com:/main /gluster-nfs
Но данный вариант не отказоустойчив, поэтому нужно сделать VIP адрес, который будет ездить между двумя нашими нодами и помогать переключать трафик в случае падения одной из нод.
Установка keepalived в CentOs производится сразу через пакетный менеджер.
$ yum install -y keepalived
Производим конфигурацию сервиса в файле /etc/keepalived/keepalived.conf:
global_defs { notification_email { admin@example.com } notification_email_from alarm@example.com smtp_server mail.example.com smtp_connect_timeout 30 vrrp_garp_interval 10 vrrp_garp_master_refresh 30 } #Cкрипт для проверки того, что гластер запущен. Если проверка не пройдет, VIP переедет. vrrp_script chk_gluster { script "pgrep glusterd" interval 2 } vrrp_instance gluster { interface ens192 state MASTER #Для второй ноды тут будет BACKUP priority 200 #Для второй ноды тут будет число меньше, например 100 virtual_router_id 1 virtual_ipaddress { 10.10.6.70/24 } unicast_peer { 10.10.6.72 #на второй ноде тут надо указать будет адрес первой } track_script { chk_gluster } }
Теперь мы можем запустить сервис и проверить что VIP на ноде появился:
$ systemctl start keepalived $ systemctl enable keepalived $ ip addr 1: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 00:50:56:97:55:eb brd ff:ff:ff:ff:ff:ff inet 10.10.6.72/24 brd 10.10.6.255 scope global noprefixroute ens192 valid_lft forever preferred_lft forever inet 10.10.6.70/24 scope global secondary ens192 valid_lft forever preferred_lft forever
Если у нас все заработало, то осталось внести PersistentVolume в Kubernetes и создать тестовый сервис для проверки работы.
--- apiVersion: v1 kind: PersistentVolume metadata: name: gluster-nfs spec: capacity: storage: 10Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain nfs: server: 10.10.6.70 path: /main --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: gluster-nfs spec: accessModes: - ReadWriteMany resources: requests: storage: 10Gi volumeName: "gluster-nfs" --- apiVersion: apps/v1 kind: Deployment metadata: name: gluster-test labels: app: gluster-test spec: replicas: 1 selector: matchLabels: app: gluster-test template: metadata: labels: app: gluster-test spec: volumes: - name: gluster persistentVolumeClaim: claimName: gluster-nfs containers: - name: gluster-test image: nginx volumeMounts: - name: gluster mountPath: /gluster
При данной конфигурации, в случае падения основной ноды, будет простой около минуты, пока mount не отвалится по таймауту и не переключится. Простой в минуту для данного хранилища допустим при том, что это не штатная ситуация и встречаться мы с ней будем редко, но в таком случае система автоматически переключится и продолжит работу, а мы сможем решать возникшую проблему и проводить восстановление не беспокоясь о простое.
Итоги
В данной статье мы рассмотрели 3 возможных варианта подключения GlusterFS к Kubernetes, в нашем варианте возможно добавить provisioner в Kubernetes, но он пока нам не нужен. Осталось добавить результаты тестов производительности между NFS и Gluster на одних и тех же нодах.
Файлы по 1Mb:
sync; dd if=/dev/zero of=tempfile bs=1M count=1024; sync Gluster: 1073741824 bytes (1.1 GB, 1.0 GiB) copied, 2.63496 s, 407 MB/s NFS: 1073741824 bytes (1.1 GB, 1.0 GiB) copied, 5.4527 s, 197 MB/s
Файлы по 1Kb:
sync; dd if=/dev/zero of=tempfile bs=1K count=1048576; sync Gluster: 1073741824 bytes (1.1 GB, 1.0 GiB) copied, 70.0508 s, 15.3 MB/s NFS: 1073741824 bytes (1.1 GB, 1.0 GiB) copied, 6.95208 s, 154 MB/s
NFS работает одинаково при любых размерах файлов, разница в скорости особенно не ощущается, в отличии от GlusterFS, которые при маленьких файлах деградирует очень сильно. Но при этом, при больших размерах файлов NFS показывает производительность в 2-3 раза ниже, чем Gluster.
