Большинство ПО кластерных систем предполагает наличие файловой системы доступной со всех узлов кластера. Эта файловая система используется для хранения ПО, данных, для организации работы некоторых кластерных подсистем и т.д. Требования на производительность такой FS могут сильно отличаться для разных задач, однако, чем она выше, тем считается, что кластер более устойчив и универсален. NFS сервер на мастер-узле является минимальным вариантом такой FS. Для больших кластеров NFS дополняется развертыванием LustreFS — высокопроизводительной специализированной распределенной файловой системы, использующей несколько серверов в качестве хранилища файлов и несколько метаинформационных серверов. Однако такая конфигурация обладает рядом свойств, которые сильно затрудняют работу с ней в случае, когда клиенты используют независимые виртуализированные кластера. В системе HPC HUB vSC для создания разделяемой FS используется широко известное решение CEPH и файловая система GFS2.
В рамках работы над проектом HPC HUB, базирующимся на OpenStack, возникла задача создать отказоустойчивое и высокопроизводительное разделяемое хранилище для нескольких групп виртуальных машин, каждая из которых представляет из себя маленький виртуальный HPC кластер. Каждый такой кластер выдается отдельному клиенту, который изнутри его полностью контролирует. Таким образом, задача не может быть решена в рамках “обычной” распределенной системы, собранной на физических компьютерах, т.к. Ethernet сети кластеров изолированы друг от друга на уровне L2 для защиты данных клиентов и их сетевого трафика друг от друга. При этом данные нельзя разместить на каждой из виртуальных машин в рамках классической разделяемой системы ибо не хватает возможностей каждой из машин и хочется освободить ценные процессорные ресурсы сразу по окончании расчетов, не тратя его на «переливание данных», особенно с учетом использования модели pay-per-use. К счастью, не все так плохо. В свете того, что разделяемое хранилище предполагается использовать для HPC расчетов, можно сделать несколько предположений о нагрузке:
Одноранговая система с узлами, на которых делается расчет и одновременно хранятся данные, противоречит парадигме HPC в принципе, т.к. на узле появляются две не синхронизированные активности: счет и запись данных с какого-то другого узла. Мы вынуждены были от нее отказаться изначально. Было решено строить двухуровневую систему. Подходов к такому хранилищу видится несколько:
Попробуем проанализировать достоинства и недостатки этих подходов.
В варианте 1) классическая распределенная файловая система (или какое-то поддерево) монтируется в гостевую ОС. В этом случае сервера файловой системы видны из каждой системы и друг другу. Это нарушает изоляцию сети по уровню L2 и потенциально позволяет различным пользователям увидеть трафик друг друга с сохраняемыми данными. Кроме того, для случая CEPH возможны только кооперативные квоты (т.е. пользователь может переиспользовать допустимое дисковое пространство за счет других пользователей) в режиме монтирования файловой системы через FUSE (filesystem in userspace), а не непосредственно в ядре. При монтировании непосредственно в ядре квоты не поддерживаются вообще.
В варианте 2) — виртуальная файловая система (virtio-9p в QEMU) поверх распределенной файловой системы, смонтированной в хосте — для работы требуется запускать QEMU от лица суперпользователя без понижения привилегий, что снижает общую защищенность системы ну или настраивать специфические ACL для хранения идентификаторов гостевых пользователей и гостевых прав доступа.
В варианте 3) при отсутствии дискового пространства на физических серверах мы можем использовать только удаленные блочные устройства какого-то распределенного хранилища. Этот вариант нецелесообразен по нескольким причинам:
В варианте 4) мы решили попробовать распределенное блочное устройство на базе CEPH и какую-то файловую систему внутри кластера, работающую с таким устройством. На самом деле гость, конечно же, видит устройство не как блочное устройство CEPH, а как обычный виртуальный VirtIO SCSI или, по желанию, VirtIO block. Мы выбрали VirtIO SCSI по очень простой причине — из-за поддержки SCSI команды unmap, аналог широко известной команды SATA TRIM, которая посылается гостевой системой при удалении файлов и освобождает место в виртуальном хранилище.
Выбор файловой системы для гостя тоже прост. Тут есть всего два варианта:
Установка CEPH прекрасно описана в документации docs.ceph.com/docs/master/start и особых проблем не вызвала. В настоящий момент система работает с одной избыточной копией данных и без авторизации. Сеть CEPH изолирована от клиентской сети. Создание блочного устройства также проблем не вызвало. Все стандартно.
К сожалению, с GFS2 настройка заняла намного больше времени, чем изначально оценивалось. Толковых описаний в сети крайне мало и они запутаны. Общая идея выглядит примерно следующим образом. Работа разделяемой файловой системой в ядре контролируется несколькими демонами, правильная настройка которых требует нетривиальных усилий или знаний.
Первое и самое главное, что надо знать, это то, что поднять эту файловую систему на Ubuntu у вас, скорее всего, не получится. Или вам придется пересобрать много малоиспользуемых за пределами RedHat-а пакетов под Ubuntu, разрешив кучу нетривиальных конфликтов уровня API.
Под RedHat-подобной системой последовательность более или менее понятна. Ниже приведены команды для гостевой системы CentOS 7.
Первым делом, отключаем SELinux. GFS2 с ним работать не будет. Это официальная информация от производителя.
Ставим базовый софт:
Включаем/отключаем нужны сервисы:
NTP необходим для работы системы, на него завязаны все распределенные блокировки областей блочного устройства. Система настраивается для изолированного от внешнего мира кластера, поэтому firewall’ы выключаем. Далее ставим нужную программную обвязку на каждом из узлов кластера:
Обратите внимание, пользователя ‘hacluster’ создавать не надо, он создается сам при инсталляции пакетов. В дальнейшем его пароль вам понадобится для создания сети взаимной авторизации между машинами кластера.
Теперь можно создать распределенное хранилище, если это не было сделано ранее. Делается на любом узле CEPH, то есть в нашем случае на физическом узле:
При форматировании файловой системы указывается имя кластера — в нашем случае ‘gfs2’ и имя файловой системы в рамках этого кластера ‘fs’, количество журналов ‘-j17’, которое должно быть равно количеству узлов (в нашем случае 17), одновременно работающих с этим кластером и протокол блокировки для выделения дискового пространства (в нашем случае ‘lock_dlm’ — распределенная блокировка). Имена кластера и файловой системы должны будут указаны при монтировании раздела. В изолированной сети внутри кластера можно использовать одни и те же имена для разных кластеров. Это не вызывает проблем.
Теперь остается только настроить монтирование в гостевой ОС. Конфигурация выполняется с одной из машин кластера один раз.
Создание сети взаимной авторизации:
здесь, master и n01..n04 — это виртуальные хосты, на которых будет доступен общий раздел.
Создаем кластер по умолчанию. Обратите внимание, имя кластера должно совпадать с использованным при создании файловой системы на предыдущем шаге.
Запуск служебных демонов — clvmd & dlm:
Монтирование GFS2 раздела в /shared, общее блочное устройство — sdb:
Начиная с этого момента, можно насладится стартом всей системы, поднимающей сервисы один за другим при помощи команды:
В конце-концов, если у вас все получилось правильно, вы должны увидеть, что файловая система /shared будет доступна на каждом из узлов.
Для тестов использовался простой скрипт, последовательно читающий и записывающий данные с каждого из узлов через dd, например:
Размер блока выставлен большой, чтение производится в режиме ‘direct’ для тестирования собственно файловой системы, а не скорости работы с дисковым кэшем. Результаты получились следующие:
Рис.1. Одновременное чтение со всех узлов виртуальных кластеров разных размеров. Производительность одного узла.
Рис.2. Одновременное чтение со всех узлов виртуальных кластеров разных размеров. Совокупная производительность.
Рис.3. Одновременная запись со всех узлов виртуальных кластеров разных размеров. Производительность одного узла.
Рис.4. Одновременная запись со всех узлов виртуальных кластеров разных размеров. Совокупная производительность
Какие можно сделать выводы:
К подводным камням можно отнести необходимость корректного останова как минимум последней из всех виртуальных машин, использующих кластер. Иначе файловая система может оказаться в состоянии, требующем восстановления. Серьезной проблемой является также корректная настройка fencing для отключения от pcs кластера узла, потерявшего синхронизацию с другими. Но об этом в следующих статьях.
Материал подготовлен Андреем Николаевым, Денисом Луневым, Анной Субботиной.
В рамках работы над проектом HPC HUB, базирующимся на OpenStack, возникла задача создать отказоустойчивое и высокопроизводительное разделяемое хранилище для нескольких групп виртуальных машин, каждая из которых представляет из себя маленький виртуальный HPC кластер. Каждый такой кластер выдается отдельному клиенту, который изнутри его полностью контролирует. Таким образом, задача не может быть решена в рамках “обычной” распределенной системы, собранной на физических компьютерах, т.к. Ethernet сети кластеров изолированы друг от друга на уровне L2 для защиты данных клиентов и их сетевого трафика друг от друга. При этом данные нельзя разместить на каждой из виртуальных машин в рамках классической разделяемой системы ибо не хватает возможностей каждой из машин и хочется освободить ценные процессорные ресурсы сразу по окончании расчетов, не тратя его на «переливание данных», особенно с учетом использования модели pay-per-use. К счастью, не все так плохо. В свете того, что разделяемое хранилище предполагается использовать для HPC расчетов, можно сделать несколько предположений о нагрузке:
- операции будут производится крупными блоками;
- операции записи не будут частыми, а будут перемежаться временными значительными интервалами;
- операции записи могут начинаться сразу со всех узлов практически одновременно, но даже в таком случае запись хочется закончить как можно быстрее, чтобы узлы продолжили счет.
Одноранговая система с узлами, на которых делается расчет и одновременно хранятся данные, противоречит парадигме HPC в принципе, т.к. на узле появляются две не синхронизированные активности: счет и запись данных с какого-то другого узла. Мы вынуждены были от нее отказаться изначально. Было решено строить двухуровневую систему. Подходов к такому хранилищу видится несколько:
- Распределенная файловая система, смонтированная на виртуальный узел через специально организованное сетевое устройство, чтобы избежать потерь производительности от OpenStack-виртуализации сети.
- Распределенная файловая система, смонтированная на физический узел и доступ к ней из гостевой системы через виртуальную файловую систему.
- Классическая распределенная файловая система внутри виртуального кластера поверх блочного устройства гостя.
- Распределенное блочное устройство, общее для каждого виртуального узла, и преднастроенная файловая система внутри виртуального кластера, работающая поверх этого общего устройства.
Попробуем проанализировать достоинства и недостатки этих подходов.
В варианте 1) классическая распределенная файловая система (или какое-то поддерево) монтируется в гостевую ОС. В этом случае сервера файловой системы видны из каждой системы и друг другу. Это нарушает изоляцию сети по уровню L2 и потенциально позволяет различным пользователям увидеть трафик друг друга с сохраняемыми данными. Кроме того, для случая CEPH возможны только кооперативные квоты (т.е. пользователь может переиспользовать допустимое дисковое пространство за счет других пользователей) в режиме монтирования файловой системы через FUSE (filesystem in userspace), а не непосредственно в ядре. При монтировании непосредственно в ядре квоты не поддерживаются вообще.
В варианте 2) — виртуальная файловая система (virtio-9p в QEMU) поверх распределенной файловой системы, смонтированной в хосте — для работы требуется запускать QEMU от лица суперпользователя без понижения привилегий, что снижает общую защищенность системы ну или настраивать специфические ACL для хранения идентификаторов гостевых пользователей и гостевых прав доступа.
В варианте 3) при отсутствии дискового пространства на физических серверах мы можем использовать только удаленные блочные устройства какого-то распределенного хранилища. Этот вариант нецелесообразен по нескольким причинам:
- для доступа к данным потребуется в 2 раза больше пропускной способности сети. Данные сначала передаются из распределенного хранилища на конкретный виртуальный узел и только потом отсылаются финальному потребителю;
- как правило все такие системы поддерживают избыточность по данным, что неприемлемо. Например, при настройках по умолчанию для CEPH полезный объем хранилища уменьшается уже в 4 раза от доступного объема дисков. Все сетевые нагрузки, обеспечивающие работу распределенной FS также будут внутри виртуального сетевого пространства. И, что самое тяжелое, последний вариант фактически потребует выделения физических узлов под виртуалки хранилища, что приводит к резкому снижению общей эффективности.
В варианте 4) мы решили попробовать распределенное блочное устройство на базе CEPH и какую-то файловую систему внутри кластера, работающую с таким устройством. На самом деле гость, конечно же, видит устройство не как блочное устройство CEPH, а как обычный виртуальный VirtIO SCSI или, по желанию, VirtIO block. Мы выбрали VirtIO SCSI по очень простой причине — из-за поддержки SCSI команды unmap, аналог широко известной команды SATA TRIM, которая посылается гостевой системой при удалении файлов и освобождает место в виртуальном хранилище.
Выбор файловой системы для гостя тоже прост. Тут есть всего два варианта:
- OCFS2,
- GFS2.
Конфигурация разделяемого хранилища
Установка CEPH прекрасно описана в документации docs.ceph.com/docs/master/start и особых проблем не вызвала. В настоящий момент система работает с одной избыточной копией данных и без авторизации. Сеть CEPH изолирована от клиентской сети. Создание блочного устройства также проблем не вызвало. Все стандартно.
Конфигурация GFS2
К сожалению, с GFS2 настройка заняла намного больше времени, чем изначально оценивалось. Толковых описаний в сети крайне мало и они запутаны. Общая идея выглядит примерно следующим образом. Работа разделяемой файловой системой в ядре контролируется несколькими демонами, правильная настройка которых требует нетривиальных усилий или знаний.
Первое и самое главное, что надо знать, это то, что поднять эту файловую систему на Ubuntu у вас, скорее всего, не получится. Или вам придется пересобрать много малоиспользуемых за пределами RedHat-а пакетов под Ubuntu, разрешив кучу нетривиальных конфликтов уровня API.
Под RedHat-подобной системой последовательность более или менее понятна. Ниже приведены команды для гостевой системы CentOS 7.
Первым делом, отключаем SELinux. GFS2 с ним работать не будет. Это официальная информация от производителя.
vi /etc/sysconfig/selinux # надо перезагрузится, но это можно сделать позже
Ставим базовый софт:
yum -y install ntp epel-release vim openssh-server net-tools
Включаем/отключаем нужны сервисы:
chkconfig ntpd on
chkconfig firewalld off
NTP необходим для работы системы, на него завязаны все распределенные блокировки областей блочного устройства. Система настраивается для изолированного от внешнего мира кластера, поэтому firewall’ы выключаем. Далее ставим нужную программную обвязку на каждом из узлов кластера:
yum -y install gfs2-utils lvm2-cluster pcs fence-agents-all
chkconfig pcsd on # запускаем PaceMaker - сердце RedHat кластера
lvmconf --enable-cluster # необходимо для работы GFS2 в распределенном режиме
echo <PASSWORD> | passwd --stdin hacluster # пароль для администратора кластера. Выберите свой :)
reboot # для отключения SELinux и применения настроек LVM
Обратите внимание, пользователя ‘hacluster’ создавать не надо, он создается сам при инсталляции пакетов. В дальнейшем его пароль вам понадобится для создания сети взаимной авторизации между машинами кластера.
Теперь можно создать распределенное хранилище, если это не было сделано ранее. Делается на любом узле CEPH, то есть в нашем случае на физическом узле:
rbd create my-storage-name --image-format 2 --size 6291456 # размер в Мб!
sudo rbd map rbd/my-storage-name
sudo mkfs.gfs2 -p lock_dlm -t gfs2:fs -j17 /dev/rbd0
sudo rbd unmap /dev/rbd0
При форматировании файловой системы указывается имя кластера — в нашем случае ‘gfs2’ и имя файловой системы в рамках этого кластера ‘fs’, количество журналов ‘-j17’, которое должно быть равно количеству узлов (в нашем случае 17), одновременно работающих с этим кластером и протокол блокировки для выделения дискового пространства (в нашем случае ‘lock_dlm’ — распределенная блокировка). Имена кластера и файловой системы должны будут указаны при монтировании раздела. В изолированной сети внутри кластера можно использовать одни и те же имена для разных кластеров. Это не вызывает проблем.
Теперь остается только настроить монтирование в гостевой ОС. Конфигурация выполняется с одной из машин кластера один раз.
pcs cluster destroy --all # кластер надо уничтожить, если он уже существовал
Создание сети взаимной авторизации:
pcs cluster auth master n01 n02 n03 n04 -u hacluster -p 1q2w3e4r --force
здесь, master и n01..n04 — это виртуальные хосты, на которых будет доступен общий раздел.
Создаем кластер по умолчанию. Обратите внимание, имя кластера должно совпадать с использованным при создании файловой системы на предыдущем шаге.
pcs cluster setup --name gfs2 master n01 n02 n03 n04
pcs cluster start --all # стартовать кластер прямо сейчас
pcs cluster enable --all # стартовать кластер при перезагрузке виртуального хоста
Запуск служебных демонов — clvmd & dlm:
pcs property set no-quorum-policy=ignore
pcs stonith create local_stonith fence_kdump pcmk_monitor_action="metadata"
pcs resource create dlm ocf:pacemaker:controld op monitor interval=30s \
on-fail=fence clone interleave=true ordered=true
pcs resource create clvmd ocf:heartbeat:clvm op monitor interval=30s \
on-fail=fence clone interleave=true ordered=true
pcs constraint order start dlm-clone then clvmd-clone
pcs constraint colocation add clvmd-clone with dlm-clone
Монтирование GFS2 раздела в /shared, общее блочное устройство — sdb:
pcs resource create clusterfs Filesystem device="/dev/sdb" \
directory="/shared" fstype="gfs2" "options=noatime,nodiratime" op \
monitor interval=10s on-fail=fence clone interleave=true
pcs constraint order start clvmd-clone then clusterfs-clone
pcs constraint colocation add clusterfs-clone with clvmd-clone
Начиная с этого момента, можно насладится стартом всей системы, поднимающей сервисы один за другим при помощи команды:
pcs status resources
В конце-концов, если у вас все получилось правильно, вы должны увидеть, что файловая система /shared будет доступна на каждом из узлов.
Тесты производительности
Для тестов использовался простой скрипт, последовательно читающий и записывающий данные с каждого из узлов через dd, например:
dd if=/shared/file of=/dev/null bs=32M iflag=direct
dd if=/root/file of=/shared/file bs=32M oflag=direct
Размер блока выставлен большой, чтение производится в режиме ‘direct’ для тестирования собственно файловой системы, а не скорости работы с дисковым кэшем. Результаты получились следующие:
Рис.1. Одновременное чтение со всех узлов виртуальных кластеров разных размеров. Производительность одного узла.
Рис.2. Одновременное чтение со всех узлов виртуальных кластеров разных размеров. Совокупная производительность.
Рис.3. Одновременная запись со всех узлов виртуальных кластеров разных размеров. Производительность одного узла.
Рис.4. Одновременная запись со всех узлов виртуальных кластеров разных размеров. Совокупная производительность
Выводы
Какие можно сделать выводы:
- чтение практически упирается в пропускную способность сети (~9 Гбит, Infiniband, IPoIB) и неплохо масштабируется при увеличении количества узлов виртуального кластера;
- запись упирается в практический потолок и не масштабируется. Но с учетом сделанных предположений нас это пока устраивает. Механизм, который обусловил наличие такого потолка, нам остался пока не ясен.
Подводные камни
К подводным камням можно отнести необходимость корректного останова как минимум последней из всех виртуальных машин, использующих кластер. Иначе файловая система может оказаться в состоянии, требующем восстановления. Серьезной проблемой является также корректная настройка fencing для отключения от pcs кластера узла, потерявшего синхронизацию с другими. Но об этом в следующих статьях.
Материал подготовлен Андреем Николаевым, Денисом Луневым, Анной Субботиной.