Поиск оптимального хранилища — это довольно сложный процесс, у всего есть плюсы и минусы. Разумеется, лидером в данной категории является 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.
oller
сколько не читал обзоров, форумов, статей:
Все сводилось к тому что гластер легко ломается и сложно в итоге делается.
Надежнее ceph, но сильно дороже, но и развалить сложнее
А в энтерпрайз на днях пришел vsan с интеграцией с kubernetes и vmware.
divanikus
Сколько gluster ни собирал, ломаться там особо не чему, потому что он просто файлики из локальной папочки по сети отдает, кроме distributed транслятора конечно. А вот производительность всегда была и остается говном, хоть какой простой сетап не запиливай.
Ceph пробовал последний раз в далеком 2013-м, тогда он делал kernel panic и убивал машину. Думаю с тех пор все сильно лучше стало.