Найти в Дзене
Slonik

Использование кластерной файловой системы GFS2 в ОС GNU Debian 12

Оглавление

В статье расскажу про мой опыт настройки кластерной файловой системы GFS2 в ОС GNU Debian 12.

Примечание: в Debian 12 в ядре linux-image-6.1.0-18-amd64 (6.1.76-1) присутствует ошибка, которая не позволяет смонтировать GFS2. Для использования GFS2 необходимо использовать другое ядро.

Для работы GFS2 необходимо три сервера с общим диском. Возможно создание ФС на двух серверах, но тогда при остановке одного из них, примонтировать ФС GFS2 будет невозможно.

В качестве тестового стенда я буду использовать три виртуальные машины Qemu KVM. В каждой машине будет один индивидуальный диск для ОС и один общий диск для кластерной файловой системы GFS2. Все виртуальные диски будут созданы как файлы на физическом ПК.

В качестве общего диска можно использовать LUN, подключенный с системы хранения данных через iSCSI или Fibre Channel.

Подготовка виртуальных машин

Далее будет описана подготовка ВМ Qemu KVM в режиме UEFI для установки ОС Debian 12.

Если вы используете другую систему виртуализации или физические сервера, то можете пропустить этот раздел и перейти к следующему - "Подготовка операционных систем".

Более подробно создание виртуальных машин в Qemu KVM описано в другой моей публикации Создание виртуальных машин с помощью Qemu KVM

Нам понадобится установочный ISO образ Debian 12 и прошивки UEFI из пакета edk2-ovmf.

Создаём каталоги хранения виртуальных дисков и прошивок UEFI:

mkdir -p /mnt/virtual/gfs2cluster/gfs1/
mkdir -p /mnt/virtual/gfs2cluster/gfs2/
mkdir -p /mnt/virtual/gfs2cluster/gfs3/

Создаём диски для операционных систем:

qemu-img create -f qcow2 /mnt/virtual/gfs2cluster/gfs1/system.qcow2 20G
qemu-img create -f qcow2 /mnt/virtual/gfs2cluster/gfs2/system.qcow2 20G
qemu-img create -f qcow2 /mnt/virtual/gfs2cluster/gfs3/system.qcow2 20G

Для общего кластерного диска используем формат raw и заранее резервируем место:

qemu-img create -f raw -o preallocation=falloc /mnt/virtual/gfs2cluster/shared.raw 5G

Копируем образы прошивки UEFI (общий для всех ВМ) и NVRAM (индивидуальный для каждой ВМ) в каталоги ВМ:

cp /usr/share/edk2/ovmf/OVMF_CODE.fd /mnt/virtual/gfs2cluster/ OVMF_CODE.fd
cp /usr/share/edk2/ovmf/OVMF_VARS.fd /mnt/virtual/gfs2cluster/gfs1/ OVMF_VARS.fd
cp /usr/share/edk2/ovmf/OVMF_VARS.fd /mnt/virtual/gfs2cluster/gfs2/ OVMF_VARS.fd
cp /usr/share/edk2/ovmf/OVMF_VARS.fd /mnt/virtual/gfs2cluster/gfs3/ OVMF_VARS.fd

Создаём скрипты для запуска ВМ:

виртуальная машина 1:

#!/bin/sh
VMNAME=gfs1
VMDIR=/mnt/virtual/gfs2cluster
MAC=52:54:00:00:00:31
IFNAME=tap1
ISO=/mnt/virtual/iso/debian-12.4.0-amd64-netinst.iso

sudo qemu-system-x86_64 \
-run-with user=$USER \
-machine q35 \
-smbios type=1,manufacturer=Qemu,product=KVM,version=1 \
-cpu host \
-accel kvm \
-smp cpus=2,sockets=1,cores=2 \
-m 2G \
-rtc base=utc \
-k en-us \
-vga virtio \
-display gtk \
-usb -device usb-tablet \
-device i6300esb \
-watchdog-action reset \
-device virtio-balloon-pci \
-device virtio-net-pci,netdev=lan,mac=${MAC} \
-netdev tap,id=lan,ifname=${IFNAME} \
-drive if=pflash,format=raw,readonly=on,file=${VMDIR}/OVMF_CODE.fd \
-drive if=pflash,format=raw,file=${VMDIR}/${VMNAME}/OVMF_VARS.fd \
-device virtio-blk-pci,drive=drive0,share-rw=off \
-drive file=${VMDIR}/${VMNAME}/system.qcow2,id=drive0,format=qcow2,if=none,cache=writeback \
-device virtio-blk-pci,drive=drive1,share-rw=on \
-drive file=${VMDIR}/shared.raw,id=drive1,format=raw,if=none,cache=writeback \
-drive file=${ISO},media=cdrom

виртуальная машина 2:

#!/bin/sh
VMNAME=gfs2
VMDIR=/mnt/virtual/gfs2cluster
MAC=52:54:00:00:00:32
IFNAME=tap2
ISO=/mnt/virtual/iso/debian-12.4.0-amd64-netinst.iso
sudo qemu-system-x86_64 \
-run-with user=$USER \
-machine q35 \
-smbios type=1,manufacturer=Qemu,product=KVM,version=1 \
-cpu host \
-accel kvm \
-smp cpus=2,sockets=1,cores=2 \
-m 2G \
-rtc base=utc \
-k en-us \
-vga virtio \
-display qtk \
-usb -device usb-tablet \
-device i6300esb \
-watchdog-action reset \
-device virtio-balloon-pci \
-device virtio-net-pci,netdev=lan,mac=${MAC} \
-netdev tap,id=lan,ifname=${IFNAME} \
-drive if=pflash,format=raw,readonly=on,file=${VMDIR}/OVMF_CODE.fd \
-drive if=pflash,format=raw,file=${VMDIR}/${VMNAME}/OVMF_VARS.fd \
-device virtio-blk-pci,drive=drive0,share-rw=off \
-drive file=${VMDIR}/${VMNAME}/system.qcow2,id=drive0,format=qcow2,if=none,cache=writeback \
-device virtio-blk-pci,drive=drive1,share-rw=on \
-drive file=${VMDIR}/shared.raw,id=drive1,format=raw,if=none,cache=writeback \
-drive file=${ISO},media=cdrom

виртуальная машина 3:

#!/bin/sh
VMNAME=gfs3
VMDIR=/mnt/virtual/gfs2cluster
MAC=52:54:00:00:00:33
IFNAME=tap3
ISO=/mnt/virtual/iso/debian-12.4.0-amd64-netinst.iso
sudo qemu-system-x86_64 \
-run-with user=$USER \
-machine q35 \
-smbios type=1,manufacturer=Qemu,product=KVM,version=1 \
-cpu host \
-accel kvm \
-smp cpus=2,sockets=1,cores=2 \
-m 2G \
-rtc base=utc \
-k en-us \
-vga virtio \
-display qtk \
-usb -device usb-tablet \
-device i6300esb \
-watchdog-action reset \
-device virtio-balloon-pci \
-device virtio-net-pci,netdev=lan,mac=${MAC} \
-netdev tap,id=lan,ifname=${IFNAME} \
-drive if=pflash,format=raw,readonly=on,file=${VMDIR}/OVMF_CODE.fd \
-drive if=pflash,format=raw,file=${VMDIR}/${VMNAME}/OVMF_VARS.fd \
-device virtio-blk-pci,drive=drive0,share-rw=off \
-drive file=${VMDIR}/${VMNAME}/system.qcow2,id=drive0,format=qcow2,if=none,cache=writeback \
-device virtio-blk-pci,drive=drive1,share-rw=on \
-drive file=${VMDIR}/shared.raw,id=drive1,format=raw,if=none,cache=writeback \
-drive file=${ISO},media=cdrom

Подготовка операционных систем

Устанавливаем ОС Debian 12 на каждую виртуальную машину в минимальной конфигурации.

Для работы ФС GFS2 необходимо установить пакеты gfs2-utils, corosync, dlm-controld:

apt install gfs2-utils corosync dlm-controld

Gfs2-utils — это пакет, содержащий инструменты для создания, проверки и работы с файловыми системами GFS2, а также важные скрипты для поддержки кластеров GFS2.

Corosync — это программное обеспечение, которое управляет инфраструктурой кластера. Оно контролирует состояние узлов и их функционирование в группе.

Dlm-controld - это симметричный распределенный менеджер блокировок общего назначения. Сам менеджер блокировок является модулем ядра.

Настройка сети

В примере у меня будут виртуальные машины с адресами:

gfs1 - ip 192.168.1.11
gfs2 - ip 192.168.1.12
gfs3 - ip 192.168.1.13

Имена узлов должны разрешаться в IP-адреса на каждом из серверов, для этого необходимо прописать сопоставление в файле /etc/hosts или создать записи на DNS-сервере.

root:~# cat /etc/hosts
127.0.0.1 localhost
192.168.1.11 gfs1.local gfs1
192.168.1.12 gfs2.local gfs2
192.168.1.13 gfs3.local gfs3

В случае реальной реализации кластера, сетевых карт на каждом из серверов должно быть как минимум две. Карты необходимо объединить в логическое устройство bonding. Для корректной работы GFS2, устройства bonding должны работать только в режиме active-backup - использование режимов balance-slb, balance-tcp может привести к нарушению работы ФС . Физически карты должны подключаться к двум независимым коммутаторам. В примере у меня будет по одной сетевой карте на сервер.

Настройка сетевого экрана

Выполняем настройку экрана на каждом узле:

  • устанавливаем пакет для управления брандмауэром
apt install ufw
  • создаём разрешающие правила для ssh
ufw allow ssh
  • создаём разрешающие правила для службы corosync
ufw allow proto udp from 192.168.1.11 to any port 5405
ufw allow proto udp from 192.168.1.12 to any port 5405
ufw allow proto udp from 192.168.1.13 to any port 5405
  • создаём разрешающие правила для службы dlm-controld
ufw allow proto tcp from 192.168.1.11 to any port 21064
ufw allow proto tcp from 192.168.1.12 to any port 21064
ufw allow proto tcp from 192.168.1.13 to any port 21064
  • активируем правила
ufw enable

Настройка Corosync

Служба Corosync обеспечивает обмен сообщениями между узлами кластера, с помощью неё отслеживается, что узлы работают корректно.

Настройки Corosync хранятся в файле /etc/corosync/corosync.conf.

Создаём файл, описывающий наш кластер gfs2clst:

totem {
version: 2
cluster_name: gfs2clst
crypto_cipher: aes256
crypto_hash: sha256
}
logging {
fileline: off
to_stderr: yes
to_logfile: yes
logfile: /var/log/corosync/corosync.log
to_syslog: yes
debug: off
logger_subsys {
subsys: QUORUM
debug: off
}
}
quorum {
provider: corosync_votequorum
}
nodelist {
node {
name: gfs1
nodeid: 1
quorum_votes: 1
ring0_addr: 192.168.1.11
}
node {
name: gfs2
nodeid: 2
quorum_votes: 1
ring0_addr: 192.168.1.12
}
node {
name: gfs3
nodeid: 3
quorum_votes: 1
ring0_addr: 192.168.1.13
}
}

Имя кластера указывается в параметре totem.cluster_name, меняем стандартное debian на соответствующее нашему случаю gfs2clst (имя может быть любое).

Имя потребуется нам в дальнейшем при создании ФС GFS2.

С помощью параметра quorum_votes можно указать количество голосов, которыми обладает узел.

В секции nodelist имена узлов name должны в точности совпадать в тем, что выдаёт утилита hostname.

Включение шифрования сообщений Corosync

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

root# corosync-keygen

Она создаст файл /etc/corosync/authkey, этот файл необходимо скопировать на другие узлы кластера.

root@gfs1:~# scp /etc/corosync/authkey root@gfs2:/etc/corosync/authkey
root@gfs1:~# scp /etc/corosync/authkey root@gfs3:/etc/corosync/authkey

После в файле настроек /etc/corosync/corosync.conf необходимо задать параметры crypto_cipher и crypto_hash в секции totem:

totem {
crypto_cipher: aes256
crypto_hash: sha256
}

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

После изменений необходимо перезапустить службы на каждом узле:

root# systemctl restart corosync

Параметры узлов кластера Corosync

Для работы кластера необходимо указать список узлов. Это делается в секции nodelist.

nodelist {
node {
name: gfs1
nodeid: 1
ring0_addr: 192.168.1.11
}
node {
name: gfs2
nodeid: 2
ring0_addr: 192.168.1.12
}
node {
name: gfs3
nodeid: 3
ring0_addr: 192.168.1.13
}
}

После настройки, копируем файл /etc/corosync/corosync.conf на остальные узлы.

Перезагружаем все узлы кластера и проверяем работу службы corosync с помощью команд corosync-quorumtool:

Статус кворума службы corosync
Статус кворума службы corosync

В разделе Votequorum information, Flags дожен быть Quorate, а количество голосов должно совпадать с числом узлов кластера.

Создание ФС GFS2

После создания ВМ в каждой дожно быть как минимум два диска:

Список дисков
Список дисков

Первый диск (vda) для системы Debian, второй (vdb) для GFS2. У меня в примере для дисков используются драйвера virtio, у вас название дисков могут быть другими (sda, sdb).

На одном из узлов создаём ФС GFS2:

mkfs.gfs2 -p lock_dlm -t gfs2clst:clusterdisk -j 3 /dev/vdb

Описание параметров:

-p lock_dlm - позволяет осуществлять блокировку на основе DLM для использования в конфигурациях общего хранилища

-t <clustername>:<lockspace> - пара "таблица блокировки", используемая для уникальной идентификации этой файловой системы в кластере.

Имя кластера (clustername, максимум 32 символа) должно соответствовать имени, указанному вашему кластеру в /etc/corosync/corosync.conf; только членам этого кластера разрешено использовать эту файловую систему.

Название пространства блокировки (lockspace, максимум 30 символов) - это уникальное имя файловой системы, используемое для отличия этой файловой системы gfs2. Допустимые имена кластеров и пространства блокировки могут содержать только буквенно-цифровые символы, дефисы (-) и подчеркивания (_).

-j 3 - количество журналов, которые необходимо создать для mkfs.gfs2. Для каждой машины, которая будет одновременно подключать файловую систему, требуется по крайней мере один журнал. Если этот параметр не указан, будет создан только один журнал. Это число может использоваться как показатель количества узлов в кластере для оптимизации компоновки файловой системы.

Подробнее описание приведено в man - https://manpages.debian.org/testing/gfs2-utils/mkfs.gfs2.8.en.html

Желательно указывать в качестве диска путь на основе идентификаторов /dev/disk/by-id/, которые при перезагрузке остаются постоянными, но у меня в Qemu KVM используются драйвера virtio, которые не формируют идентификаторы, поэтому я указываю классический путь.

Создаём каталог для монтирования ФС GFS2 на каждом узле:

mkdir /mnt/gfs2/

Определяем UUID ФС с помощью команды blkid, и убеждаемся, что на каждом узле он одинаков:

root@gfs1:~# blkid /dev/vdb
/dev/vdb: LABEL="gfs2clst:clusterdisk" UUID="7dea7d24-e1a0-4217-bc42-24c6389fd246" BLOCK_SIZE="4096" TYPE="gfs2"
root@gfs2:~# blkid /dev/vdb
/dev/vdb: LABEL="gfs2clst:clusterdisk" UUID="7dea7d24-e1a0-4217-bc42-24c6389fd246" BLOCK_SIZE="4096" TYPE="gfs2"
root@gfs3:~# blkid /dev/vdb
/dev/vdb: LABEL="gfs2clst:clusterdisk" UUID="7dea7d24-e1a0-4217-bc42-24c6389fd246" BLOCK_SIZE="4096" TYPE="gfs2"

Выполняем пробное монтирование на каждом узле:

mount -U 7dea7d24-e1a0-4217-bc42-24c6389fd246 /mnt/gfs2/ -o noatime

Информация о файловых системах на узле
Информация о файловых системах на узле

Проверяем журнал событий ядра dmesg на первом узле:

Журнал событий при монтировании GFS2 на первом узле
Журнал событий при монтировании GFS2 на первом узле

Проверяем журнал событий dmesg на втором узле:

Журнал событий при монтировании GFS2 на втором узле
Журнал событий при монтировании GFS2 на втором узле

Проверяем журнал событий dmesg на третьем узле:

Журнал событий при монтировании GFS2 на третьем узле
Журнал событий при монтировании GFS2 на третьем узле

На первом узле после монтирования ФС на втором и третьем узлах дополнительно должны появиться записи:

Журнал событий при монтировании GFS2 на первом узле после монтирования на втором и третьем
Журнал событий при монтировании GFS2 на первом узле после монтирования на втором и третьем

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

Тест одновременной записи на общий диск с разных узлов
Тест одновременной записи на общий диск с разных узлов

Проверяем, что с каждого узла содержимое на диске отображается одинаково:

Список файлов на общем диске
Список файлов на общем диске

Настройка автоматического монтирования ФС GFS2 при запуске узлов

Для монтирования ФС GFS2 необходима работа служб networking.service, corosync.service, dlm.service поэтому использовать файл /etc/fstab для монтирования даже с параметром _netdev не получится.

Официально Red Hat рекомендует установить службу управления кластером Pacemaker, создать ресурс монтирования ФС GFS2, и использовать его для монтирования. Подробнее описано в руководстве.

Если Pacemaker для вас излишен, то в качестве менее надёжного решения можно использовать systemd.

Создаём файл /etc/systemd/system/mountGFS2.service на каждом узле:

[Unit]
Description=Mount GFS2 filesystem
After=networking.service corosync.service dlm.service
Requires=networking.service corosync.service dlm.service
[Service]
Type=oneshot
RemainAfterExit=true
ExecStartPre=/usr/bin/sleep 10
ExecStart=/usr/bin/mount -U 7dea7d24-e1a0-4217-bc42-24c6389fd246 /mnt/gfs2/ -o noatime
ExecStop=/usr/bin/umount /mnt/gfs2
[Install]
WantedBy=multi-user.target

В зависимостях службы важно указать и After= и Requires=, иначе при выключении узла, служба dlm.service остановится раньше, чем начнётся размонтирование GFS2 - узел зависнет, его придётся принудительно перезагружать.

Включаем автозапуск на каждом узле:

systemctl enable mountGFS2.service

Перезагружаем узлы и проверяем, что ФС GFS2 автоматически смонтировалась.

Если после перезагрузки GFS2 не смонтировалась и в журнале событий присутствуют записи:

kernel: gfs2: GFS2 installed
kernel: gfs2: fsid=gfs2clst:clusterdisk: Trying to join cluster "lock_dlm", "gfs2clst:clusterdisk"
kernel: dlm: no local IP address has been set
kernel: dlm: cannot start dlm midcomms -107
kernel: gfs2: fsid=gfs2clst:clusterdisk: dlm_new_lockspace error -107
mount[485]: mount: /mnt/gfs2: mount(2) system call failed: Конечная точка передачи не подсоединена.
mount[485]: dmesg(1) may have more information after failed mount system call.
systemd[1]: mountGFS2.service: Main process exited, code=exited, status=32/n/a
systemd[1]: mountGFS2.service: Failed with result 'exit-code'.
systemd[1]: Failed to start mountGFS2.service - Mount GFS2 filesystem.

Значит у вас служба монтирования запускается раньше, чем служба Corosync и DLM успевают настроить взаимодействие. В таком случае необходимо увеличить задержку запуска (ExecStartPre=/usr/bin/sleep 10) в файле mountGFS2.service на большее значение.