В предыдущей статье я рассмотрел возможность создания отказоустойчивого NFS-сервера с помощью DRBD и Proxmox. Получилось довольно неплохо, но не будем останавливаться на достигнутом и теперь постараемся "выжать все соки" из нашей хранилки.
В этой статье я расскажу как подобным образом создать отказоустойчивый iSCSI-таргет, который при помощи LVM мы будем нарезать на маленькие кусочки и использовать под виртуальные машины.
Именно такой подход позволят снизить нагрузку и повысить скорость доступа к данным в несколько раз, это бывает особенно выгодно когда не требуется конкурентный доступ к данным, например в случае когда вам нужно организовать хранилище под виртуальные машины.
Пара слов о DRBD
DRBD достаточно простое и зрелое решение, код восьмой версии принят в состав ядра Linux. По сути представляет ссобой сетевое зеркало RAID1. В девятой версии появилась поддержка кворума и репликации с больше чем двумя нодами.
По сути он позволяет вам объединить блочные устройства на нескольких физических нодах в одно общее расшаренное по сети.
Используя DRBD можно добиться очень интересных кофигураций. Сегодня пойдет речь об iSCSI и LVM.
Подробнее о нем можете узнать прочитав мою предыдущую статью, где я подробно описал это решение.
Пара слов об iSCSI
iSCSI — это протокол доставки блочного устройства по сети.
В отличии от того же NBD он поддерживает авторизацию, без проблем отрабатывает сетевые сбои и поддерживает множество других полезных функций, а главное показывает очень хорошую производительность.
Существует огромное количество его реализаций, некоторые из них так же включены в ядро и не требуют особых сложностей для его настройки и подключения.
Пара слов об LVM
Стоит упомянуть, что у LINBIT существует собственное решение для Proxmox, оно должно работать из коробки и позволит добиться похожего результата, но в данной статье я не хотел бы заострять внимание только на Proxmox а описать некоторое более унивирсальное решение которое подойдет как для Proxmox так и для чего-нибудь еще, в данном примере proxmox используется только как средство оркестрации контейнеров, по сути вы можете заменить его на другое решение, например запускать контейнеры с таргетом в Kubernetes.
Что касается конкретно Proxmox, то он отлично работает с shared LUN и LVM, используя только собственные стандратные драйверы.
К плюсам LVM можно отнести то, что его использование не является чем-то революционно новым и недостаточно обкатанным, а наоборот, оно показывает сухую стабильность, что обычно и требуется от хранилища. Стоит упомянуть что LVM довольно активно используются и в других средах, например в OpenNebula или в Kubernetes и достаточно неплохо там поддерживается.
Таким образом вы получите универсальное хранилище которое можно использовать в разных системах (не только в proxmox), используя только готовые драйверы, без особой необходимости дорабатывать его напильником.
К сожалению, при выборе решения под хранилище всегда приходится идти на какие-нибудь компромиссы. Так и тут, данное решение не даст вам той-же гибкости как например Ceph.
Размер виртуального диска ограничен размером LVM-группы, а область размеченная под конкретный виртуальный диск обязательно будет преаллоцирована — это сильно улучшает скорость доступа к данным, но не дает возможности к Thin-Provisioning (когда виртуальный диск занимает меньше места чем есть на самом деле). Стоит упомянуть что производительность LVM достаточно сильно проседает при использовании снапшотов, в связи с чем возможность свободного их использования, часто исключается.
Да, LVM поддерживает Thin-Provision пулы, которые лишены данного недостатка, но к сожалению их использование возможно только в контексте одной ноды и нет возможности расшарить один Thin-Provision пул на несколько нод в кластере.
Но несмотря на эти недостатки, из-за своей простоты LVM по прежнему не дает возможности конкурентам обойти его и полностью вытеснить с поля боя.
При достаточно небольших накладных расходах LVM по прежнему представляет очень быстрое, стабильное и достаточно гибкое решение.
Общая схема
- У нас есть три ноды
- На каждой ноде распределенное drbd-устройство.
- Поверх drbd-устройства запущен LXC-контейнер с iSCSI-таргетом.
- Таргет подключен ко всем трем нодам.
- На подключенном таргете создана LVM-группа.
- При необходимости LXC-конейнер может переехать на другую ноду, вместе с iSCSI-таргетом
Настройка
С идеей разобрались теперь перейдем к реализации.
По умолчанию в комплекте с ядром Linux поставляется модуль восьмой версии drbd, к сожалению он нам не подходит и нам необходимо установить модуль девятой версии.
Подключим репозиторий LINBIT и установим все необходимое:
wget -O- https://packages.linbit.com/package-signing-pubkey.asc | apt-key add -
echo "deb http://packages.linbit.com/proxmox/ proxmox-5 drbd-9.0" > /etc/apt/sources.list.d/linbit.list
apt-get update && apt-get -y install pve-headers drbd-dkms drbd-utils drbdtop
pve-headers
— заголовки ядра необходимые для сборки модуляdrbd-dkms
— модуль ядра в формате DKMSdrbd-utils
— основные утилиты для управления DRBDdrbdtop
— интерактивный инструмент, как top только для DRBD
После установки модуля проверим, все ли с ним в порядке:
# modprobe drbd
# cat /proc/drbd
version: 9.0.14-1 (api:2/proto:86-113)
Если вы увидите в выводе команды восьмую версию значит что-то пошло не так и загружен in-tree модуль ядра. Проверьте dkms status
что-бы разобраться в чем причина.
Каждая нода у нас будет иметь одно и тоже drbd-устройство запущенное поверх обычных разделов. Для начала нам нужно подготовить этот раздел под drbd на каждой ноде.
В качестве такого раздела может выступать любое блочное устройство, это может быть lvm, zvol, раздел диска или весь диск целиком. В этой статье я буду использовать отдельный nvme диск с разделом под drbd: /dev/nvme1n1p1
Стоит заметить, что имена устройств имеют свойство иногда меняться, так что лучше сразу взять за привычку использовать постоянный симлинк на устройство.
Найти такой симлинк для /dev/nvme1n1p1
можно таким образом:
# find /dev/disk/ -lname '*/nvme1n1p1'
/dev/disk/by-partuuid/847b9713-8c00-48a1-8dff-f84c328b9da2
/dev/disk/by-path/pci-0000:0e:00.0-nvme-1-part1
/dev/disk/by-id/nvme-eui.0000000001000000e4d25c33da9f4d01-part1
/dev/disk/by-id/nvme-INTEL_SSDPEKKA010T7_BTPY703505FB1P0H-part1
Опишем наш ресурс на всех трех нодах:
# cat /etc/drbd.d/tgt1.res
resource tgt1 {
meta-disk internal;
device /dev/drbd100;
protocol C;
net {
after-sb-0pri discard-zero-changes;
after-sb-1pri discard-secondary;
after-sb-2pri disconnect;
}
on pve1 {
address 192.168.2.11:7000;
disk /dev/disk/by-partuuid/95e7eabb-436e-4585-94ea-961ceac936f7;
node-id 0;
}
on pve2 {
address 192.168.2.12:7000;
disk /dev/disk/by-partuuid/aa7490c0-fe1a-4b1f-ba3f-0ddee07dfee3;
node-id 1;
}
on pve3 {
address 192.168.2.13:7000;
disk /dev/disk/by-partuuid/847b9713-8c00-48a1-8dff-f84c328b9da2;
node-id 2;
}
connection-mesh {
hosts pve1 pve2 pve3;
}
}
Желательно для синхронизации drbd использовать отдельную сеть.
Теперь создадим метаданные для drbd и запустим его:
# drbdadm create-md tgt1
initializing activity log
initializing bitmap (320 KB) to all zero
Writing meta data...
New drbd meta data block successfully created.
success
# drbdadm up tgt1
Повторим эти действия на всех трех нодах и проверим состояние:
# drbdadm status
tgt1 role:Secondary
disk:Inconsistent
pve2 role:Secondary
peer-disk:Inconsistent
pve3 role:Secondary
peer-disk:Inconsistent
Сейчас наш диск Inconsistent на всех трех нодах, это потому, что drbd не знает какой диск должен быть взят в качестве оригинала. Мы должны пометить один из них как Primary, что бы его состояние синхронизировалось на остальные ноды:
drbdadm primary --force tgt1
drbdadm secondary tgt1
Сразу после этого начнется синхронизация:
# drbdadm status
tgt1 role:Secondary
disk:UpToDate
pve2 role:Secondary
replication:SyncSource peer-disk:Inconsistent done:26.66
pve3 role:Secondary
replication:SyncSource peer-disk:Inconsistent done:14.20
Нам не обязательно дожидаться ее окончания и мы можем параллельно выполнять дальнейшие шаги. Их можно выполнять на любой ноде, вне зависимости от ее текущего состояния локального диска в DRBD. Все запросы будут автоматически перенаправлены на устройство с UpToDate состоянием.
Стоит не забыть активировать автозапуск drbd-сервиса на нодах:
systemctl enable drbd.service
Настройка LXC-контейнера
Опустим часть настройки кластера Proxmox из трех нод, эта часть хорошо описана в официальной wiki
Как я говорил раньше наш iSCSI-таргет будет работать в LXC-контейнере. Сам контейнер мы будем держать на устройстве /dev/drbd100
, которое мы только что создали.
Сначала нам нужно создать файловую систему на нем:
mkfs -t ext4 -O mmp -E mmp_update_interval=5 /dev/drbd100
Proxmox по умолчанию включает multimount protection на уровне файловой системы, в принципе мы можем обойтись и без нее, т.к. DRBD по умолчанию имеет собственную защиту, он просто запретит второй Primary для устройства, но осторожность нам не повредит.
Теперь скачаем шаблон Ubuntu:
# wget http://download.proxmox.com/images/system/ubuntu-16.04-standard_16.04-1_amd64.tar.gz -P /var/lib/vz/template/cache/
И создадим из него наш контейнер:
pct create 101 local:vztmpl/ubuntu-16.04-standard_16.04-1_amd64.tar.gz --hostname=tgt1 --net0=name=eth0,bridge=vmbr0,gw=192.168.1.1,ip=192.168.1.11/24 --rootfs=volume=/dev/drbd100,shared=1
В данной команде мы указываем что корневая система нашего контейнера будет находиться на устройстве /dev/drbd100
и добавим параметр shared=1
что бы разрешить миграцию контейнера между нодами.
Если что-то пошло не так, вы всегда можете поправить это через интерфейс Proxmox или в конфиге контейнера /etc/pve/lxc/101.conf
Proxmox распакует шаблон и подготовит корневую систему контейнера для нас. После этого мы можем запустить наш контейнер:
pct start 101
Настройка iSCSI-таргета.
Из всего множества таргетов я выбрал istgt, так как он обладает наибольшей производительностью и работает в пространстве пользователя.
Теперь давайте залогинимся в наш контейнер:
pct exec 101 bash
Установим обновления и istgt:
apt-get update
apt-get -y upgrade
apt-get -y install istgt
Создадим файл который мы и будем отдавать по сети:
mkdir -p /data
fallocate -l 740G /data/target1.img
Теперь нам нужно написать конфиг для istgt /etc/istgt/istgt.conf
:
[Global]
Comment "Global section"
NodeBase "iqn.2018-07.org.example.tgt1"
PidFile /var/run/istgt.pid
AuthFile /etc/istgt/auth.conf
MediaDirectory /var/istgt
LogFacility "local7"
Timeout 30
NopInInterval 20
DiscoveryAuthMethod Auto
MaxSessions 16
MaxConnections 4
MaxR2T 32
MaxOutstandingR2T 16
DefaultTime2Wait 2
DefaultTime2Retain 60
FirstBurstLength 262144
MaxBurstLength 1048576
MaxRecvDataSegmentLength 262144
InitialR2T Yes
ImmediateData Yes
DataPDUInOrder Yes
DataSequenceInOrder Yes
ErrorRecoveryLevel 0
[UnitControl]
Comment "Internal Logical Unit Controller"
AuthMethod CHAP Mutual
AuthGroup AuthGroup10000
Portal UC1 127.0.0.1:3261
Netmask 127.0.0.1
[PortalGroup1]
Comment "SINGLE PORT TEST"
Portal DA1 192.168.1.11:3260
[InitiatorGroup1]
Comment "Initiator Group1"
InitiatorName "ALL"
Netmask 192.168.1.0/24
[LogicalUnit1]
Comment "Hard Disk Sample"
TargetName disk1
TargetAlias "Data Disk1"
Mapping PortalGroup1 InitiatorGroup1
AuthMethod Auto
AuthGroup AuthGroup1
UseDigest Auto
UnitType Disk
LUN0 Storage /data/target1.img Auto
Перезапустим istgt:
systemctl restart istgt
На этом настройка таргета закончена
Настройка HA
Теперь мы можем перейти к кофигурации HA-manager. Создадим отдельную HA-группу для нашего устройства:
ha-manager groupadd tgt1 --nodes pve1,pve2,pve3 --nofailback=1 --restricted=1
Наш ресурс будет работать только на нодах указанных для этой группы. Добавим наш контейнер в эту группу:
ha-manager add ct:101 --group=tgt1 --max_relocate=3 --max_restart=3
Рекомендации и тюнинг
Так как мы не используем multipathing, в нашем случае рекомендуется отключить переодические проверки соединения на клиентах, а так же увеличить таймауты ожидания для восстановления сессии в /etc/iscsi/iscsid.conf
.
node.conn[0].timeo.noop_out_interval = 0
node.conn[0].timeo.noop_out_timeout = 0
node.session.timeo.replacement_timeout = 86400
Использование
Proxmox
Полученный iSCSI-таргет можно сразу-же подключить в Proxmox, не забыв снять галочку с Use LUN Directly.
Сразу после этого станет возможным создание LVM поверх него, не забудьте поставить галочку напротив shared:
Другие среды
Если вы планируете использовать это решение в другой среде возможно вам потребуется установить кластерное расширение для LVM на данный момент из существует две реализации. CLVM и lvmlockd.
Настройка CLVM не совесм тривиальна и требует работающего кластер менеджера.
Куда как второй метод lvmlockd — еще не до конца протестирован и только-только начинает появляться в стабильных репозиториях.
Рекомендую к прочтению отличную статью о блокировах в LVM
При использовании LVM с Proxmox кластерное дополнение не требуется, так как управление томами обеспечивается самим proxmox, который обновляет и следит за метаданными LVM самостоятельно. Тоже касается и OpenNebula, на что явно указывает официальная документация.
MagicGTS
LVM уже давольно давно умеет ThinProvision, где нет проблем с производительностью снапшотов.
kvaps Автор
Да, но LVM не поддерживает конкурентный доступ к thin-provision пулу с нескольких нод.
Есть способ под каждую виртуалку генерить отдельный thin-provision пул в общем LVM, правда это не в контексте проксмоса уже.
MagicGTS
Тогда фразу в статье стоит изменить. А то создалось впичатление, что вы не знали про ThinProvision, а вопрос был в поддержке кластеризации.
kvaps Автор
Спасибо за замечание, поправил.