среда, 10 октября 2018 г.

Бездисковые станции на слоистой файловой системе OverlayFS

Идея реализации бездисковых станций на базе OverlayFS была не моя, а моих коллег по работе.
Вероятно, уже многие рассматривали варианты как сделать бездисковые станции на базе слоситых файловых систем. Например на базе AuFS.
Но это было не просто  - т.к. много работы по установке и настройке модулей ядра, которых нет по умолчанию.
А зачем вообще такие бездисковые станции? спросите вы.

Область применения:


Сразу скажу это решение для Linux систем.
А с другими я особо и не работаю :))

1. Самое понятное - Класс в учебном заведении.
на всех машинах должны стоять одинаковые системы, они должны обновляться одинаково. На них должны стоять одинаковые наборы программ с одинаковыми настройками и связями между собой и с принтерами например.
Ну а наработанные данные пользователя должны тоже сохраниться и они будут лежать в отдельных папках на сервере.
2. Разные операционисты в банках, в торговых заведениях. Если например все они должны работать в общем пространстве программ и все программы должны быть настроены одинаково на всех машинах.
3. Различные пультовые и компьютеры управления какими-нибудь системами.
Суть в том что в разных местах сложных больших по площади установок могут стоять бездисковые машины для контроля и управления этой установкой и при запуске эти машины будут работать совершенно одинаково и показывать одни и теже приложения которые настраиваются один раз для всех машин.

Ну и можно еще много всего перечислять.

Идея:


Есть один большой и мощный NFS сервер, который для каждого клиента (бездисковой машины) имеет свой ресурс выдачи.
Этот ресурс формируется из двух слоев, можно и больше если в этом есть производственная необходимость (позже расскажу какая может быть потребность).
Нижний или Базовый слой содержит всю основную систему со всеми пакетами программ и настройками драйверов для внешних и устройств. В этом плане все клиентские машины должны быть унифицированы и иметь одинаковые подключенные внешние и внутренние устройства. Ну или придется устанавливать драйвера для всех имеющихся на клиентах устройств.
На верхнем слое будет лежать только то, что наработал пользователь. Здесь имеется ввиду логи его действий, временные и рабочие каталоги или настройки программ для этой машины.
А вот данные пользователя и его домашний каталог можно держать совершенно в отдельном каталоге. Это можно было делать и без использования слоистой файловой системы.
В итоге мы получаем :
1. настроенная  система в одном экземпляре
2. пользовательские папки  - на каждого пользователя по одной папке, независимо от количества бездисковых машин. На всех них он может работать с одинаковыми программами и со своими данными
3. в верхних слоях для каждой машины лежат временные папки и папки логов. Эти папки кстати можно периодически чистить без особого урона.

За счет этого объединения слоев мы получаем значительную экономию места на nfs сервере
и унификацию всех настроек программ на всех машинах. Скорость внесения изменений в базовый слой - мгновенно!

Особенности:


В общем то все зависит как будут организованы слои.
Самый простой способ использования это два слоя базовый и верхний слой где лежат только пользовательские настройки. Пользователь не может устанавливать в этом случае программы и добавлять устройства в машину.
Можно организовать трехслойную структуру, в этом случае у администора конкретной машины будет возможность устанавливать пакеты в средний слой, который базируется на общий для всех машин. А пользователи по-прежнему будут сохранять свои настройки в верхний слой.
Но в любом случае нельзя исправлять или добавлять файлы в верхние слои если слои не смонтированы в overlayfs. Дело в том что если слои размонтировать , то в папках слоев будут лежать файлы, которые привязаны к файлам более нижнего слоя и поэтому если файл более верхнего слоя изменился а система overlayfs об этом не знает , это может привести к не предсказуемым результатам.
Также есть особенность в пересборке ядра. Ядро нужно будет пересобирать только в базовом слое, то есть нужно загрузить машину с этим слоем, собрать ядро с указанием аргументов с тем что при загрузке ядро подмонтирует рутовую директорию коорая находится  на nfs ресурсе. Затем нужно пересобрать iniramfs и скопировать ядро и initramfs на tftp сервер, для того чтобы следующая загрузка машины произошла уже с новым ядром.


Реализация:


Итак рассмотрим реализация системы с двумя слоями.
В качестве сервера будет выступать система Centos 7.4
Поддержка модуля  overlayfs  в ядре появилась только с версии 4.16. Поэтому обновляем ядро до 4.16 или выше.
Устанавливаем пакет с репозиторием elrepo:


# rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
# rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm


Оставляем только ядерные ветки репозитория:


# yum --disablerepo="*" --enablerepo="elrepo-kernel" list available


Устанавливаем последнее ядро:


# yum --enablerepo=elrepo-kernel install kernel-ml


После этого перегружаем машину и смотрим какое ядро появилось в папке /boot

После этого нужно убедиться что в grub.conf - е указана правильная строка загрузки по умолчанию.
То есть смотрим файл /etc/default/grub и убеждаемся что там написано: 
GRUB_DEFAULT=0

Обычно после установки нового ядра оно прописывается в самый первый (нулевой) уровень загрузки.   Но если оно установилось в другой уровень то пропишите этот уровень в GRUB_DEFAULT

Создадим структуру папок для слоистой файловой системы:

Давайте подключим отдельный диск или раздел диска и подмонтируем его в папку /DATA
В ней создадим базовый слой:

# mkdir /DATA/base/centos7.4

можно в дальнейшем и базовые слои других операционных систем создать.

Создаем место для верхних слоев :


# mkdir /DATA/hosts/host1
# mkdir /DATA/hosts/host2
# mkdir /DATA/hosts/host3


И еще нужно  создать папки для временных рабочих файлов слоистой файловой системы:


# mkdir /DATA/works/host1
# mkdir /DATA/works/host2
# mkdir /DATA/works/host3


Устанавливаем систему в базовый слой :

# yum groups -y install "Server with GUI" --releasever=7 --installroot=/DATA/base/centos7.4

Здесь мы ставим например сервер с GUI.


Теперь устанавливаем nfs сервер:


# yum install nfs-utils


Создаем папки для выдачи соединенных слоев (соединять будем позже):


# mkdir  /export/hosts/host1
# mkdir  /export/hosts/host2
# mkdir  /export/hosts/host3


устанавливаем права на папки:


# chmod -R 755 /export/hosts/host1
# chown nfsnobody:nfsnobody  /export/hosts/host1


все то же самое для остальных папок.

Настраиваем файерволл:

firewall-cmd --permanent --zone=public --add-service=nfs
firewall-cmd --permanent --zone=public --add-service=mountd
firewall-cmd --permanent --zone=public --add-service=rpc-bind
firewall-cmd --reload


Запускаем все сервисы:


systemctl enable rpcbind
systemctl enable nfs-server
systemctl enable nfs-lock
systemctl enable nfs-idmap
systemctl start rpcbind
systemctl start nfs-server
systemctl start nfs-lock
systemctl start nfs-idmap


создаем файл /etc/exports и пишем туда примерно следующее:


/export/hosts/host1   192.168.0.1(rw,sync,no_root_squash,no_all_squash)
/export/hosts/host2   192.168.0.2(rw,sync,no_root_squash,no_all_squash)
/export/hosts/host3   192.168.0.3(rw,sync,no_root_squash,no_all_squash)


Теперь настроим монтирование слоев в overlayfs:

Во первых нужно настроить модуль ядра overlayfs, включаем возможность експортировать слои через NFS:


В файле /sys/module/overlay/parameters/nfs_export
 пишем YВ /sys/module/overlay/parameters/redirect_always_follow тоже пишем Y


Теперь подготовим файл /etc/fstab. Пишем для каждой машины:

overlay /exports/hosts/host1    overlay noauto,x-systemd.requires=/DATA,x-systemd.automount,lowerdir=/DATA/base/centos7.4/,upperdir=/DATA/hosts/host1/,workdir=/DATA/works/host1/,index=on 0 0

Здесь нужно отметить, что используется опция ядра index=on это важно для работы overlayfs через nfs. Иначе смонтированные по nfs верхние слои не будут видны на клиенте nfs.
А так же тут указаны важные опции noauto, x-systemd.requires и x-systemd.automount .
Это нужно для того чтобы монтирование папок происходило последовательно друг за другом.
То есть сначала монтируется диск с данными nfs ресурсов /DATA, а затем монтируются слои overlayfs. Чтобы такое произошло, нужно отключить автомонтирование слоев overlayfs. Кроме того монтирование будет производиться спомощью системы systemd.automount.

После перезагрузки сервера слои смонтируются и будут доступны по nfs.

Теперь нужно настроить выдачу загрузочного образа ядра для хостов через tftp сервер:


# yum install tftp-server


Меняем настройки по-умолчанию:

в файле /etc/xinetd.d/tftp меняем параметр disable  на  'no'

перегружаем xinetd:

# service xinetd restart

Устанавливаем основные загрузочные образы:

# yum install syslinux

Копируем нужные файлы в корень  (/var/lib/tftpboot/) tftp сервера:

# cp /usr/lib/syslinux/pxelinux.0 /var/lib/tftpboot
# cp /usr/lib/syslinux/menu.c32 /var/lib/tftpboot
# cp /usr/lib/syslinux/memdisk /var/lib/tftpboot
# cp /usr/lib/syslinux/mboot.c32 /var/lib/tftpboot
# cp /usr/lib/syslinux/chain.c32 /var/lib/tftpboot

Создаем директорию для загрузочного меню:

# mkdir /var/lib/tftpboot/pxelinux.cfg

Тут же создаем директорию для загрузочного образа базовой системы бездисковых станций:

# mkdir /var/lib/tftpboot/base-centos7.4

Туда же копируем vmlinuz и initrd.img.

в файле  /var/lib/tftpboot/pxelinux.cfg/default пишем примерно следующее:

default vesamenu.c32
prompt 0
timeout 300
ONTIMEOUT local

MENU TITLE PXE Menu
Local HHD0 Boot

LABEL Centos
    MENU LABEL Centos Install
        KERNEL base-centos7.4/vmlinuz
        APPEND initrd=base-centos7.4/initrd.img root=/dev/nfs rw nfsroot=nfs-server:/DATA/base/centos7.4 ip=eth0:dhcp selinux=0 ipv6.disable=1 console=tty0 plymouth.enable=0
       

Это настройки загрузки любой машины которую DHCP сервер перенаправил для загрузки ядра по сети на этот сервер. В данном случае загрузится базовый слой.

Для каждого хоста определяем IP адрес, который будет выдавать DHCP сервер, переводим этот адрес в hex представлении и создаем файл с таким именем и в нем прописываем загрузочное меню для этого хоста. tftp сервер будет выдавать это меню загрузки только для машин с IP для которого был сделан файл.
Вот пример файла:

default vesamenu.c32
prompt 0
timeout 60
ONTIMEOUT Main

MENU TITLE PXE Boot Host1

LABEL Main
    MENU LABEL host1
        KERNEL base-centos7.4/vmlinus
        APPEND initrd=base-centos7.4/initrd.img root=/dev/nfs rw nfsroot=nfs-server:/exports/hosts/host1 ip=eth0:dhcp selinux=0 ipv6.disable=1 console=tty0 plymouth.enable=0
LABEL base-pult
    MENU LABEL base-centos7.4
        KERNEL base-centos7.4/vmlinuz
        APPEND initrd=base-centos7.4/initrd.img root=/dev/nfs rw nfsroot=nfs-server:/DATA/base/centos7.4  ip=eth0:dhcp ipv6.disable=1 console=tty0 plymouth.enable=0


Теперь перегружаем сервисы :


# service xinetd restart
# service tftpbootd restart


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

Прописываем в /etc/dhcp/dhcpd.conf

next-server nfs-server;
filename "pxelinux.0";

в секции описывающей сеть или сегмент сети.

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

Про особенности работы в слоистой файловой системе было написано выше.


Добавление нового хоста в слоистую файловую систему


Как можно понять из описанного выше добавление нового бездискового хоста потребует внести изменения как минимм в трех местах на nfs сервере:

1. Cоздаем структуру в /DATA/hosts/newhost и /DATA/works/newhost
2. Делаем соответствующие записи в /etc/fstab
3. Создаем файл соответствующий IP адресу новой машины в /var/lib/tft[pboot/pxelinux.cfg/
4. Перегружаем все нужные сервисы.

Ну конечно же можно просто написать скрипт для автоматизации всего этого дела:

#!/usr/bin/python
import os, sys
import socket

ROOT_PATH = '/DATA'
EXPORT_PATH = '/exports'
BASE = 'base-centos7.4'
WORK_NET = '192.168.0.0/24'
EXPORT_CONF_FILE = '/etc/exports'
ROOT_TFTP = '/var/lib/tftpboot/pxelinux.cfg/'
IP_NFS_SERVER = '192.168.0.254'
LOWER_DIR = os.path.join(ROOT_PATH,'base',BASE)
BASE_WORK_DIR = os.path.join(ROOT_PATH,'works')


def ip_to_hex(ip):
    out = ''
    hex_symbol = ''
    for i in ip.split('.'):
        hex_symbol = hex(int(i)).replace('0x','').upper()
        if len(hex_symbol) == 1:
            hex_symbol = '0'+hex_symbol
        out += hex_symbol
    return out


def make_export_conf(EXPORT_CONF_FILE, export_row):
    with open(EXPORT_CONF_FILE,'r') as expfile:
        text_file  = expfile.read()
    if text_file.find(export_row) == -1:
        with open(EXPORT_CONF_FILE,'a') as expfile:
            expfile.write(export_row)
    return


def make_tftp_file(hex_name,EXPORT_DIR,ROOT_TFTP,if_name):
    conf_file_text = ''
    dict_replace = {'hostname':hostname,
                     'export_dir':EXPORT_DIR,
                     'ip_nfs_server':IP_NFS_SERVER,
                     'if_name':if_name}
    text_tpl = open('tftpboot.tpl','r').read()
    text_next = text_tpl
    for k in dict_replace.keys():
        conf_file_text = text_next.replace('{'+k+'}',dict_replace[k])
        text_next = conf_file_text
    print conf_file_text
    conf_file = open(ROOT_TFTP+'/'+hex_name,'w')
    conf_file.write(conf_file_text)
    return


def make_fstab(mount_row):
    with open('/etc/fstab','r') as expfile:
        text_file  = expfile.read()
    if text_file.find(mount_row) == -1:
        with open('/etc/fstab','a') as expfile:
            expfile.write(mount_row)
        os.system('umount '+EXPORT_DIR)
        print 'mount -t overlay overlay -o lowerdir='+LOWER_DIR\
                  +'/,upperdir='+UPPER_DIR+'/,workdir='+WORK_HOST_DIR+'/,index=on '+EXPORT_DIR
        os.system('mount -t overlay overlay -o lowerdir='+LOWER_DIR\
                  +'/,upperdir='+UPPER_DIR+'/,workdir='+WORK_HOST_DIR+'/,index=on '+EXPORT_DIR)
    return


count_args = len(sys.argv)
name_interface = 'eth0'
ip = None
print count_args
if count_args < 2:
    print "hostname is needed. python create_layer.py {hostname} "
    sys.exit(1)
# get name of network interface of machine
if count_args >= 2:
    hostname = sys.argv[1] #get hostname
if count_args >= 3:
    ip = sys.argv[2] #get ip address
if count_args == 4:
    name_interface = sys.argv[3] #get name ethernet

if not ip:
    IPHOLDER = socket.gethostbyname(hostname)
else:
    IPHOLDER = ip
hex_name = ip_to_hex(IPHOLDER)
print hostname
print ip
print name_interface
print hex_name



Ну и вызов этого скрипта: python create_layer.py {hostname}
Необходим хотябы один аргумент для этого скрипта - это имя бездисковой станции который прописан в ДНС сервере и для которого IP адрес соответствует выдамаемомы dhcp сервером.
Можно так же указать еще два аргумента это ip адрес (на случай если нет записи в ДНС) и имя интерфейса, если хочется указать какой именно интерфейс будет подключаться к сети и к nfs серверу.

Вот так можно все это создавать и автоматизировать.

Все успехов и удачи!

вторник, 11 сентября 2018 г.

Исследование возможностей использования JBOD для создания высокоустойчивого кластерного хранилища.


Итак имеем:



Одна дисковая корзина с 4 мя дисками.

https://www.supermicro.com/products/chassis/2U/826/SC826BE2C-R741JBOD


Корзина поключена к двум машинам на каждой из которых установлен Proxmox 5.2


Внутри каждого хоста видно по 4 диска, каждый по 5 ТB

Что нужно получить?



Нужно чтоб диски были объеденены в рейд (1 или 10). При этом каждый из хостов должен уметь обрабатывать события рейда, так чтобы при выходе из строя одного из хостов второй без промедления продолжил работу, зная состояние рейда и имея возможность продолжить все что начал первый хост (если в момент выхода из строя первого хоста, этот первый хост что-то делал с рейдом, например выводил из строя умерший диск или восстанавливал данные на замененный диск). Другими словами нужна система кластерного рейда на двух хостах.


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

Это можно сделать, например, создав LVM группу и указать ее как общий ресурс для этих двух хостов проксмокса в настройках кластера проксмокса (через веб интерфейс).


Это нужно чтоб виртуальные машины которые будут создаваться как lvm разделы внутри этой lvm группы "жили" на этих двух хостах в HA кластере (High Availability). То есть хранилище образов машин у этих двух хостов одно и то же, и когда на нем появляется образ машины то оба хоста видят этот образ. И в том случае если один из хостов отказывает, то второй просто подхватывает образ машины и продолжает работать.

Возможные варианты

Первый и кажущийся самый простой вариант: использование линукс софт рейд - mdadm для создания рейда из имеющихся физических дисков. Далее мы получаем одно устройство в системе /dev/md0 , которое мы добавляем в LVM группу и добавляем в кластере проксмокса как общее хранилище. 

Второй вариант это использование возможностей самого LVM2. lvm сам умеет создавать рейды, правда рейд там не связан напрямую с физическими устройствами, а работает с группой LVM куда добавлены все физические устройства. Далее LVM разбивает все доступное пространство на логические разделы и делает из них рейд.
В нашем случае т.к. дисков четыре, то скорее всего границы условных разделов совпадают с физическими дисками, но если бы их было три или более четырех, то границы полос которые собираются в рейд располагались бы как попало , на разных дисках.
Но в любом случае избыточность данных обеспечивается.
На выходе мы получаем LVM раздел, который внутри себя содержит рейд.
Но теперь нам нужно создать еще одну группу LVM, добавить туда раздел с рейдом и добавить эту группу в проксмоксе как общую для данных хостов.
Можно использовать хитрую и мощную ZFS для того чтоб спомощью ее создать нужный рейд и получить в итоге директорию на каждом из хостов и добавить ее в проксмоксе как общую для хостов.
В этом случае образы виртуальных маших будут храниться уже как файлы, а не разделы. Что замедляет работу, т.к. включается в работу уровень файцловой системы.

Проблемы


Вариант с использованием mdadm страдает тем, что нет нормальной реализации работы mdadm в кластере. То есть, если на одном из хостов вы решили провести например замену диска, то второй хост об этом ничего не узнает и будет продолжать работать с этим рейдом как с нормально работающим рейдом.
Если диск реально вышел из строя, то оба хоста и их mdadm начнут как-то реагировать на это (помечать диск и сообщать о вышедшем из строя устройстве). Но потом при восстановлении данных, когда диск будет заменен, каждый из хостов будет производить по своему и риск попортить восстанавливаемые даные очень велик. Когда две машины пишут в одно и то же место не договариваясь (а работа с договоренностью и есть работа в кластере), это приводит к непредсказуемым результатам.
В проксмоксе нет поддержки работы mdadm как кластерного софта.
Есть специальные дистрибутивы линуксов, где такую возможность реализовали, но это коммерческие продукты и не совместимые с дебиан системами. Это связано с тем что mdadm используют модули ядра.
Например это реализовали вот в таком дистрибутиве:

https://www.suse.com/products/highavailability/

Так что на чистом проксмоксе невозможно реализовать рейд на базу mdadm, работающий в кластере. 


В варианте где используется LVM для создания рейда, тоже не все
Сам LVM умеет работать в кластере использую CLVM (реализация LVM для кластера). Но здесь проблема в том что в той реализации CLVM, которая используется в проксмоксе кластерными создается только первичная группа физических томов. То есть если использовать группу LVM, которая создана как кластерная и затем добавлена в кластер проксмокса как общаяя, то с ней нет никаких проблем. Машины создаются и перемещаются. НО! нам нужен еще рейд!
Когда мы создаем рейд средствами lvm у нас нет возможности указать что это тоже кластерный раздел.
Дело в том что любой раздел LVM созданный внутри кластерной группы LVM уже не считается кластерным и доступ к нему на редактирование разрешен только тому хосту который его создавал. Второй хост может получить к нему доступ только если первый хост выключен и второй хост при этом должен перегрузить свою систему LVM. По сути второй хост должен перегрузиться чтоб получить доступ к разделу созданному на другом хосте.
В этом и есть засада, дальше все надстройки над группой LVM созданной как кластерная не имеют смысла, т.к. они уже не могут быть кластерными. 

Вот пример создания рейд10 средствами LVM:
# lvcreate --type raid10 -m 1 -i 4 -L 200G -n myr10vol myvg

Кроме этого в некоторой документации по CLVM прямо пишут что кластерный рейд в LVM не поддерживается.


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

Кроме этого внешнего инструмента придется использовать еще и pacemaker и pcsd службы для создания кластерной инфраструктуры, которая будет обслуживать ZFS. Эти службы будут взаимодействовать с corosync, которая по умолчанию работает в проксмоксе. Все вышеуказанные службы по факту дублируют службы HA самого проксмокса и в каких то места могут конфликтовать ними. Например они могут претендовать на взаимодействие с dlm службой и не давать другим службам работать с dlm.
Получается что кроме того что сама ZFS не быстрая, она еще требует дополнительных служб и ресурсов.
Для того чтобы она заработала в кластере нужно использовать export import функции ZFS. Есть специальный агент который может отслеживать изменения и делать экспорт и импорт вовремя: https://github.com/skiselkov/stmf-ha/
Но настройка и запуск его весьма не тривиальная задача. Проект весьма сырой и поддрежка его очень не большая. Поэтому не стоит расчитывать что он будет работать надежно.


Возможные решения:


Но все таки даже в такой ситуации есть решения, хотя и не такие простые и прямые как хотелось бы.

Давайте разделим задачу на несколько по функциям:

1. Нам нужно что-то что создает и поддерживает рейд из физических дисков
2. Нам нужно что-то что обеспечивает высокую доступность к общему хранилищу
желательно чтобы общее хранилище давало доступ к блочному устройству

поэтому первый вариант:


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

И дальнейшие работы с рейдом производятся на одной этой машине.

Сама эта машина содержит iSCSi сервер и предоставляет доступ к блочному устройству.

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

При выходе из строя одного из хостов проксмокса, кластер HA запускает машину с рейдом на втором хосте и хранилище снова доступно.

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

Вот здесь описано подробно как собрать такую схему:
http://yaricprog.blogspot.com/2018/09/jbod-raid10-proxmox-ve-ha-cluster.html


Еще один вариант

Это тоже отделение процессов от процессов хоста виртуальной машиной или линукс контейнером LXC.

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

в итоге машины тоже выдают на выходе iSCSI ресурс, но только выдают они каждый своему хосту.

В этом случае все изменения на хранилище будут синхронизоваться внутри виртуальных машин и на каждом хосте будут одинаковые данные.

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

В этом случае кластер HA проксмокса запустит все машины которые имели диски на общем хранилище, на втором хосте.


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

И если этот хост сломался, то на втором нужно перезапустить систему ЛВМ и тогда диски всех машин будут доступны для работы.

Но это уже не совсем HA.

JBOD + RAID10 + Proxmox VE + HA cluster. Как сделать дешевое надежное хранилище.

В предыдущей статье я описал какие проблемы возникают при создании кластера высокой доступности для хранилища использующего дисков в рейде.

Повторю исходные условия:
Имеем корзину с дисками (JBOD) по 5 Тб (4 диска).
Каждый диск имеет двойное проводное соединение с в двумя машинами, на которых устновлен Proxmox VE.
Таким образом мы имеем две системы (proxmox), в которых видны диски /dev/sda, /dev/sdb, /dev/sdc, /dev/sdd.

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

Но ресурс полученный при объединеннии дисков в рейд должен быть доступен к каждому проксмоксу и в нем могут создаваться виртуальные машины, которые будут автоматически переезжать на уцелевший хост проксмокса при поломке одного из хостов.
Это и есть HA cluster.

Итак начнем.

Подготовка хостов с проксмоксом.


Подготовим сначала основной диск, на котором установлен проксмокс. Это нужно сделать на обоих хостах.
Нужно сделать на основном диске отдельную партицию (первичный раздел). Размер этого раздела должен быть не меньше размер виртуальной машины для рейда + запас для разметки LVM. Минимальный размер , например Centos7.4  - 8 Gb. Поэтому если вы создатите раздел 10 - 15 Gb этого будет достаточно. Но это вы сами прикидывайте, т.к. если у вас машина с рейдом будет еще для чего-нибудь задействована, то может не хватить.
реть какой размер
И еще нужно убедиться, что при создании дополнительной партиции не пострадают данные в партиции где живет система проксмокса. Т.е. нужно посмотреть какой размер занимает сейчас проксмокс и оставлять ему этот размер с запасом.

Делается примерно так:


# fdisk /dev/sda

Command (m for help):d   - удаляем последнюю партицию

Command (m for help):n   - создаем новую партицию и указываем размер, который занимает проксмокс с запасом. А лучше это размер = прошлый размер последней партиции - размер создаваемой дополнительной партиции для виртуалки с рейдом (с запасом конечно).

Command (m for help): t  - Указываете тип партиции Linux LVM

Command (m for help): n   - создаем еще одну партицию теперь это уже для виртуалки

Command (m for help): t  - Указываете тип партиции Linux LVM

Command (m for help): w - записываем изменения на диск.

Теперь у нас есть  отдельный диск например /dev/sda4
Добавляем этот диск в LVM :


# pvcreate /dev/sda4


И добавляем группу drbd_pool . Фраза "drbd" должна быть обязательна для создания drbd пула.


# vgcreate drbd_pool /dev/sda4


Ну и гарантированно заполним предыдущий (обрезанный) раздел проксоксом:


# pvresize /dev/sde3

DRBD


Создаем пул для хранилища drbd

# lvcreate --size 15G --name drbdthinpool --thin drbdpool


Если у вас не установлен специальный пакет для thin-provisioning, то ставим его и еще пакет для работы с drbd:


# apt-get install thin-provisioning-tools drbdmanage -y


Теперь настроим хранилище drbd на этих вновь добавленный раздел.


# drbdmanage init -q 192.168.1.1
# drbdmanage add-node -q proxmox2 192.168.1.2
# drbdmanage nodes


Здесь происходит инициализация drbd хранилища, добавление ноды второго проксмокса.
Естественно сеть между хостами проксмоксов должна быть уже настроена. Лучше иметь независимую прямую связь между хостами через отдельную сетевые карты на хостовых машинах.

Рассмотрим настройку конфигурации drbd хранилища.
После создания хранилища появляется файл /etc/drbd.d/drbdctrl.res
Добавляем в раздел net{ следующие строки:


        allow-two-primaries yes;
        after-sb-0pri discard-zero-changes;
        after-sb-1pri discard-secondary;
        after-sb-2pri disconnect;


После этого нужно убедиться, что раздел .drbdctl внутри drbd хранилища на обоих нодах являются primary. Если какая-то нода не является primary вы можете сделать ее такой :


# drbdadm primary .drbdctl


Не забудьте добавить drbd хранилище в конфигурацию проксмокса. Добавляем следующие строки в файл /etc/pve/storage.cfg:


drbd: drbd-hw
        content rootdir,images
        nodes hw1,hw2


Назвать этот ресурс можно назвать как угодно, проксмокс сам найдет нужный раздел в LVM, т.к. на одной машине он должен быть только один.

Особенности работы drbd в HA кластере расмотрим позже.

RAID10


Во-первых создаем виртуальную машину на одном из проксмокс хостов.
Учтите что основной диск этой машины должен располагаться в drbd хранилище.
Прокидываем в машину диски. Это делается через конфиги виртуальной машины. 
После того как вы создали машину, то в папке /etc/pve/qemu-server/ появляется файл типа 100.conf.
Добавляем в конец этого файла:

virtio1: /dev/disk/by-id/scsi-35000cca24d4f5394,backup=0,replicate=0,size=5860522584K
virtio2: /dev/disk/by-id/scsi-35000cca24d4f7534,backup=0,replicate=0,size=5860522584K
virtio3: /dev/disk/by-id/scsi-35000cca24d4f07c0,backup=0,replicate=0,size=5860522584K
virtio4: /dev/disk/by-id/scsi-35000cca24d4f08fc,backup=0,replicate=0,size=5860522584K

Обратите внимание здесь нужно прописывать именно UUID дисков. Их вы можете посмотреть в /dev/disk/by-uuid/
Это нужно чтобы когда диски будут использоваться на разных хостах проксмоксах они не перепутывались

backup=0  - означает, что проксмокс не будет складывать эти диски в бекап во время бекапирования.
replicate=0  - тоже самое касается репликации данных этих дисков между хранилищами  данных о машинах.

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

Я поставил  на машину Centos 7.4, поэтому дальше все описание будет для нее.

Делаем апдейд пакетов и ставим самые необходимые пакеты:

# yum update
# yum install mc mdadm targetcli

подготавливаем диски, они видны внутри виртуалки как /dev/vdb и т.д.:

# fdisk /dev/vdb
Command (m for help): n - создаем партицию.

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

Command (m for help): w - Записываем изменения на диск.

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

Создаем рейд10:

# mdadm --create --verbose /dev/md0 --level=10  --raid-devices=4 /dev/vdb1 /dev/vdc1 /dev/vdd1 /dev/vde1

Теперь у нас есть один большой диск /dev/md0.

После этого рейд начинает синхронизировать блоки на диске и процесс можно наблюдать вот таким образом:

# cat /proc/mdstat

Далее запускаем targetcli и настраиваем iscsi-server

# targetcli

targetcli shell version 2.1.fb46
Copyright 2011-2013 by Datera, Inc and others.
For help on commands, type 'help'.

/>

Далее нужно настроить ресурс для выдачи в сеть одного iscsi диска.
Подробно процесс описывать не буду, т.к. в интернете полно подобных описаний.

В итоге мы должны получить примерно вот такое:

/> ls
o- / ......................................................................................................................... [...]
  o- backstores .............................................................................................................. [...]
  | o- block .................................................................................................. [Storage Objects: 1]
  | | o- disk0 ........................................................................... [/dev/md0 (10.9TiB) write-thru activated]
  | |   o- alua ................................................................................................... [ALUA Groups: 1]
  | |     o- default_tg_pt_gp ....................................................................... [ALUA state: Active/optimized]
  | o- fileio ................................................................................................. [Storage Objects: 0]
  | o- pscsi .................................................................................................. [Storage Objects: 0]
  | o- ramdisk ................................................................................................ [Storage Objects: 0]
  o- iscsi ............................................................................................................ [Targets: 1]
  | o- iqn.2003-01.org.linux-iscsi.localhost.x8664:sn.87fd8bccd4d6 ....................................................... [TPGs: 1]
  |   o- tpg1 .................................................................................................. [gen-acls, no-auth]
  |     o- acls .......................................................................................................... [ACLs: 2]
  |     | o- iqn.1993-08.org.debian:01:952561107f8e ............................................................... [Mapped LUNs: 1]
  |     | | o- mapped_lun0 ................................................................................. [lun0 block/disk0 (rw)]
  |     | o- iqn.1993-08.org.debian:01:bb376e9bb6b ................................................................ [Mapped LUNs: 1]
  |     |   o- mapped_lun0 ................................................................................. [lun0 block/disk0 (rw)]
  |     o- luns .......................................................................................................... [LUNs: 1]
  |     | o- lun0 ...................................................................... [block/disk0 (/dev/md0) (default_tg_pt_gp)]
  |     o- portals .................................................................................................... [Portals: 1]
  |       o- 0.0.0.0:3260 ..................................................................................................... [OK]
  o- loopback ......................................................................................................... [Targets: 0]
/>

Здесь мы видим, что iscsi сервер берет локальный (для виртуалки это локальный) диск /dev/md0 и выдает его в кчестве ресурса iqn.2003-01.org.linux-iscsi.localhost.x8664:sn.87fd8bccd4d6
Также мы видим, что к этому ресурсу разрешено подключаться машинам с идентификаторами:
iqn.1993-08.org.debian:01:952561107f8e и iqn.1993-08.org.debian:01:bb376e9bb6b
И что сервер работает на порту 3260, поэтому нужно открыть этот порт в файерволле.

# firewall-cmd --permanent --add-port=3260/tcp
# firewall-cmd --reload

И запускаем службу iscsi сервера:

# systemctl enable target
# systemctl start target

Теперь заходим в веб интерфейс проксомкса и во вкладке Datacenter -> Storage и добавляем там ISCSI ресурс.

ID  - Любое имя для ресурса
Portal  - Сетевое имя или IP адрес Iscsi  сервера. В данном случае это адрес нашей виртуалки с рейдом.
Taget - Это  адрес сетевого ресурса. В нашем случае: iqn.2003-01.org.linux-iscsi.localhost.x8664:sn.87fd8bccd4d6
Nodes - здесь нужно указать имена только тех проксмокс хостов к которым будет подключен ресурс.

После подключения этого ресурса на хосте появляется еще один диск типа /dev/sdf

Теперь добавляем диск /dev/sdf в физические диски в LVM :


# pvcreate /dev/sdf


Далее создаем группу LVM :


# vgcreate raid10_vg /dev/sdf


Теперь в веб-интерфейсе добавляем эту группу в хранилище. Это нужно делать через конфигурацию проксмокса.
Открываем файл /etc/pve/storage.cfg и добавляем:


lvm: raid10_vg
        vgname raid10_vg
        content rootdir,images
        nodes proxmox1, proxmox2
        shared 1


Это нужно делать именно так, потому что через вебинтерфейс можно добавить только lvm группу pve, которую проксмокс создает автоматически.

Заметьте shared = 1 это обязательно, т.к. оба хоста должны видеть изменения в группе.

 HA Cluster

Теперь рассмотрим как настроить кластер высокой доступности.
В веб-интерфейсе проксмокса настройка HA кластера делается весьма просто на вкладке Datacenter -> HA.
Сначала во вкладке Groups создаем группу и добавляем в нее оба хоста проксмокса
Затем переходим в основную вкладку HA и добавляем машину с рейдом и все машины у которых диски находятся на этом рейде.

Теперь рассмотрим особенности работы HA кластера с drbd и виртуальными машинами.

Проксмокс может менять статус drbd ресурсов, которые создавал сам проксмокс.
Дело в том что диск типа raw на которых живут виртуальные машины это всего лишь LVM раздел который создал проксмокс. Когда он создает такой раздел в drbd пуле , он устанавливает статус primary для этого раздела на том хосте, на котором в данный момент располагается и работает виртуальная машина этого раздела.
Но вот специальный раздел .drbdctl не изменяется проксмоксом, поэтому в момент когда один из хостов проксмокса выходи из строя, то на втором ничего не меняется в специальном разделе drbd. Если на оставшемся в живих хосте работает primary спец раздел drbd, то перезд и запуск всех машин в HA кластере проходит нормально. Но если этот хост оказался с secondary спецразделом, то машиные переезжают на живой хост и не запускаются, т.к. доступ к дискам заблокирован самим drbd. То есть secondary нода drbd ждет появления primary ноды и не дает работать с хранилищем.
Поэтому нужно следить затем чтобы на обоих нодах drbd спецраздел должен быть primary.

Вторая особенность работы HA кластера это восстановление нормальной работы после восстановления упавшего проксмокс хоста.
После того как все ноды drbd увидят друг друга,скорее всего проксмокс вообще заблокирует  все созданные им ресурсы на упавшем хосте. Это выглядит как переименование файла ресурса в папке /var/lib/drbd.d/ во что-то типа raid10-iscsi-disk-1.res.old
Нужно привести имя файла к типу *.res и перегрузить drbd систему. И делается это не тривиальным способом, а именно нужно перегрузить сервис dbus:


# systemctl restart dbus


после этого ресурс появится в стаусе drbd, но будет помечен как StandAlone, это означает, что он не синхронизован с текущей актуальной версией данных на этом ресурсе.
Спецраздел .drbdctl на упавшем и восстановленном хосте скорее всего будет secondary  и его можно легко перевести в состояние primary, но перед этим стоит синхронизовать все созданные проксомоксом ресурсы, чтобы данные были актуальные и чтобы не потерять наработок, которые произошли во время восстановления хоста.
Для этого сделаем следующие манипуляцие с разделом (в данном случае это раздел машины с рейдом).

На восстановленном хосте пишем:


# drbdadm disconnect raid10-iscsi-disk-1
# drbdadm connect raid10-iscsi-disk-1 --discard-my-data


на уцелевшем хосте нужно просто отцепить и опять прицепить этот ресурс:


# drbdadm disconnect raid10-iscsi-disk-1
# drbdadm connect raid10-iscsi-disk-1


НО! проследите чтобы пока данные не синхронизовались уцелевший хост должен иметь primary  статус в разделе машины, а восстановленный secondary.
Хотя автоматически это и так проставляется при появлении ноды в кластере.

И последняя особенность HA кластера проксмокса - в нем нельзя специально проставить время задержки запуска машины при восстановлении ее на уцелевшем хосте проксмокса.
То есть при падении одного из хостов, через минуту HA кластер просто запускает все машины которые находятся в той группе в которой находятся и хосты проксмокса сразу безо всяких задержек.
А нам в данной ситуации важно запускать машины, которые живут в рейде на пару минут позже чем запуск самой машине с рейдом.
Но эту ситуацию можно исправить установив количество перезапусков при неудачном пуске 5 -6 раз, тогда проксмокс будет пытаться запустить машины живущие на рейде несколько раз пока запускается машина в с рейдом. Несколько не приятная ситуация, но в принципе рабочая.

Вывод:

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