DRBD


Глубокое понимание внутреннего устройства DRBD позволяет более тонко настраивать работу системы и правильно планировать ресурсы. К счастью, у команды DRBD уже есть отличная документация, которая довольно подробно разбирает эту тему. Мы опирались на нее в своей работе, и решили перевести и выложить в открытом доступе 17-ю главу — как удобную шпаргалку по внутреннему устройству DRBD. Так что это не обычная статья, а перевод части официальной документации (исходная нумерация разделов сохранена).


В этой главе представлена информация о внутренних алгоритмах и структурах DRBD. Она довольно подробно рассматривает внутреннюю работу DRBD, но делает это не настолько глубоко, чтобы служить справочником для разработчиков. Для этой цели рекомендуем обратиться к материалам, перечисленным в разделе Publications, и, естественно, к комментариям в исходном коде DRBD.



17.1. Метаданные DRBD


DRBD хранит в выделенной области информацию о содержащихся в нем данных. Эти метаданные включают:



Метаданные могут храниться внутри или снаружи устройства — это определяется для каждого случая индивидуально.



17.1.1. Внутренние метаданные


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


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


Недостатки
Если нижестоящим устройством является отдельный физический жесткий диск (а не RAID-массив), внутренние метаданные могут негативно влиять на производительность записи. Выполнение приложением операций записи может вызывать обновление метаданных в DRBD. Если метаданные хранятся на том же цилиндре жесткого диска, что и производственные данные, операция записи может привести к дополнительным движениям считывающей/записывающей головки.


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

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

Чтобы этого избежать, вы должны выполнить одно из следующих действий:


  • Увеличить размер нижестоящего устройства с помощью любого менеджера логических томов (например, LVM) при условии, что в соответствующей группе томов есть свободное место. Эта возможность также может поддерживаться аппаратными средствами хранения.
  • Уменьшить размер существующей файловой системы на нижестоящем устройстве. Такая возможность может поддерживаться или не поддерживаться вашей файловой системой.
  • Если ни одно из перечисленных выше действий невозможно, используйте внешние метаданные.
  • Чтобы оценить, насколько необходимо увеличить нижестоящее устройство или уменьшить файловую систему, смотрите раздел «Оценка размера метаданных».


17.1.2. Внешние метаданные


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


Преимущества
Для некоторых операций записи внешние метаданные позволяют снизить задержку (latency).


Недостатки
Метаданные физически отделены от production-данных. Это означает, что в случае аппаратного сбоя, уничтожившего только production-данные (но не метаданные DRBD), для полной синхронизации потребуется ручное вмешательство.


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


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

Как определить размер блочного устройства для хранения метаданных вашего устройства, читайте в разделе «Оценка размера метаданных».


Для внешних метаданных необходим минимум 1 Мбайт.


17.1.3. Оценка размера метаданных


Рассчитать, сколько места потребуется для метаданных DRBD, можно с помощью следующей формулы:



M_s=\left\lceil\frac{C_s}{2^{18}}\right\rceil * 8 * N+72


Рисунок 14. Расчет точного размера метаданных DRBD


Cs — размер устройства данных в секторах, N — количество пиров.


Получить размер устройства в байтах можно, выполнив команду blockdev --getsize64 <device>. Чтобы перевести значение в мегабайты, разделите его на 1048576 (= 220 или 10242).

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



M_{M B}<\frac{C_{M B}}{32768} * N+1


Рисунок 15. Приблизительная оценка размера метаданных DRBD



17.2. Идентификаторы поколений


DRBD использует идентификаторы поколений (GI) для идентификации «поколений» реплицируемых данных.


Этот внутренний механизм DRBD используется для:


  • определения, действительно ли два узла являются членами одного кластера (в отличие от двух узлов, которые соединились случайно);
  • определения направления фоновой ресинхронизации (при необходимости);
  • определения того, нужна ли полная ресинхронизация или достаточно частичной;
  • идентифицирования состояния split brain.


17.2.1. Поколения данных


DRBD отмечает начало нового поколения данных в каждом из следующих случаев:


  • произведена первичная синхронизация устройства;
  • отключенный (disconnected) ресурс переходит в роль Primary;
  • произошло отключение ресурса в роли Primary.

Из этого можно сделать вывод: когда ресурс находится в состоянии Connected и диски обоих узлов UpToDate, текущее поколение данных на обоих узлах совпадает. Обратное также верно. Обратите внимание, что текущая реализация использует младший бит для кодирования роли узла (Primary/Secondary). Поэтому младший бит может отличаться на разных узлах, даже если считается, что у них одно поколение данных.


Каждое новое поколение идентифицируется 8-байтным уникальным идентификатором (UUID).



17.2.2. Кортеж идентификаторов поколений


DRBD сохраняет некоторую информацию о нынешних и прошлых поколениях данных в локальных метаданных ресурса.


Current UUID
Это идентификатор текущего поколения данных с точки зрения локального узла. Когда ресурс подключен и полностью синхронизирован, current UUID идентичен между узлами.


Bitmap UUIDs
Это UUID поколения, от которого bitmap на диске отслеживает изменения для каждого удаленного хоста. Как и сам bitmap синхронизации диска, этот идентификатор имеет значение, только пока удаленный хост отключен.


Historical UUIDs
Это идентификаторы поколений данных, предшествующих текущему, с таким расчетом, чтобы на каждый (возможный) удаленный хост приходилось по одному слоту.


Вместе эти элементы называются кортежем идентификаторов поколений (GI tuple).


Пример вывода команды drbdadm show-gi (примечание переводчика)
       +--<  Current data generation UUID  >-
       |               +--<  Bitmap's base data generation UUID  >-
       |               |                 +--<  younger history UUID  >-
       |               |                 |         +-<  older history  >-
       V               V                 V         V
92194A89F6C70246:0000000000000000:0000000000000000:0000000000000000:1:1:0:0:0:0:0:0:0:0:0:0
                                                                    ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
                                      -<  Data consistency flag  >--+ | | | | | | | | | | |
                             -<  Data was/is currently up-to-date  >--+ | | | | | | | | | |
                                  -<  Node was/is currently primary  >--+ | | | | | | | | |
 -<  This node was a crashed primary, and has not seen its peer since  >--+ | | | | | | | |
             -<  The activity-log was applied, the disk can be attached  >--+ | | | | | | |
        -<  The activity-log was disabled, peer is completely out of sync  >--+ | | | | | |
                              -<  This node was primary when it lost quorum  >--+ | | | | |
                                          -<  Node was/is currently connected  >--+ | | | |
                              -<  The peer's disk was out-dated or inconsistent  >--+ | | |
                                 -<   A fence policy other the dont-care was used  >--+ | |
                  -<  Node was in the progress of marking all blocks as out of sync  >--+ |
                     -<  At least once we saw this node with a backing device attached >--+


17.2.3. Как меняются идентификаторы поколений



Начало нового поколения данных


Когда в результате сбоя сети или ручного вмешательства узел Primary теряет соединение со своим пиром, DRBD изменяет локальные идентификаторы поколений следующим образом:




Рисунок 16. Изменение кортежа GI при начале нового поколения данных


  1. Primary создает UUID для нового поколения данных. Он становится новым current UUID для primary-узла.
  2. Предыдущий current UUID теперь ссылается на поколение, от которого bitmap отслеживает изменения, поэтому он становится новым bitmap UUID для primary-узла.
  3. На secondary-узле(ах) GI tuple остается неизменным.


Завершение ресинхронизации


После завершения ресинхронизации synchronization target принимает весь GI tuple от synchronization source.


Synchronization source сохраняет тот же набор и не генерирует новые UUID.



17.2.4. Как DRBD использует идентификаторы поколений


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


Current UUIDs пусты на обоих узлах
Локальный узел обнаруживает, что current UUID пуст и у него, и у пира. Так обычно бывает с новыми ресурсами, для которых еще не проведена полная первичная синхронизация. Синхронизация не происходит автоматически, ее необходимо запустить вручную.


Сurrent UUID пуст на одном из узлов
Локальный узел обнаруживает, что current UUID у пира пустой, а у него — нет. Это нормальная ситуация для только что настроенного ресурса, на котором стартовала первичная полная синхронизация, при этом локальный узел является источником. DRBD устанавливает все биты в bitmap синхронизации диска (все устройство для него становится out-of-sync) и начинает синхронизацию как synchronization source.


В противоположном случае — когда current UUID локального узла пуст, а у пира нет — DRBD выполняет те же шаги, но локальный узел становится synchronization target.


Одинаковые current UUIDs
Локальный узел обнаруживает, что его current UUID и current UUID пира не пусты и равны друг другу. Это нормальное явление для ресурса, который перешел в disconnected в роли Secondary и не был «повышен» ни на одном узле в период отключения. Синхронизация не происходит, так как не требуется.


Bitmap UUID совпадает с current UUID пира
Локальный узел обнаруживает, что его bitmap UUID совпадает с current UUID пира, при этом bitmap UUID пира пуст. Это нормальное и ожидаемое явление после сбоя secondary-узла, при котором локальный узел выполняет роль Primary. Это означает, что пир никогда не был Primary и всегда работал с одним поколением данных. DRBD запускает нормальную фоновую ресинхронизацию, при этом локальный узел становится synchronization source.


Если локальный узел обнаруживает, что его bitmap UUID пуст, а bitmap пира совпадает с current UUID локального узла, то это нормальное и ожидаемое явление после сбоя локального узла. DRBD запускает нормальную фоновую ресинхронизацию, только в этот раз локальный узел становится synchronization target.


Current UUID совпадает с historical UUID пира
Локальный узел обнаруживает, что его current UUID совпадает с одним из historical UUID пира. Это означает, что у двух наборов данных общий предок, у пира актуальные данные, а информация в bitmap’е пира устарела и не пригодна для использования. Поэтому нормальной синхронизации недостаточно. DRBD помечает все устройство как out-of-sync и запускает полную фоновую ресинхронизацию, при этом локальный узел становится synchronization target.


В обратном случае — когда один из historical UUID локального узла совпадает с current UUID пира — DRBD выполняет те же шаги, но локальный узел становится synchronization source.


Bitmap UUIDs совпадают, current UUIDs не совпадают
Локальный узел обнаруживает, что его current UUID отличается от current UUID пира, а bitmap UUID совпадает. Это split brain-ситуация, где у данных общий предок. В этом случае DRBD запускает стратегию автоматического восстановления после split brain’а, если она настроена. Если она не настроена, DRBD отключается и ожидает ручного разрешения.


Ни current, ни bitmap UUID не совпадают
Локальный узел обнаруживает, что его current UUID отличается от current UUID пира и что bitmap UUID не совпадают. Это split brain с несвязанными поколениями предков. В этом случае стратегии автоматического восстановления, даже если они настроены, бесполезны. DRBD отключается и ожидает ручного разрешения split brain.


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



17.3. Журнал активности



17.3.1. Назначение


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


Случайный временной фактор может вызвать ситуацию, когда операция записи завершена, но передача по сети еще не произошла, или наоборот. Если в этот момент активный узел выходит из строя и начинается смена ролей, то этот блок данных оказывается не синхронизированным между узлами — он был записан на вышедшем из строя узле, но репликация еще не завершена. Поэтому после восстановления узла этот блок должен быть удален из набора данных при последующей синхронизации. В противном случае вышедший из строя узел будет иметь «на одну запись больше» от выжившего узла, а это нарушает принцип «всё или ничего» реплицируемого хранилища.


Такая проблема характерна не только для DRBD, но практически для всех конфигураций реплицируемого хранилища. Многие решения для хранения данных (в том числе DRBD до версии 0.7) требуют полной синхронизации данных при восстановлении после падения активного узла. Начиная с версии 0.7, DRBD использует иной подход. Журнал активности (хранится с метаданными) отслеживает недавно записанные блоки (их еще называют hot extents).


Если временно недоступный узел, который находился в активном режиме в момент отказа, начинает синхронизацию, то синхронизируются только hot extents из AL (а также любые блоки, отмеченные в bitmap на теперь активном пире), но не все устройство. Это значительно сокращает время синхронизации после отказа активного узла.



17.3.2. Active Extents


У журнала активности есть настраиваемый параметр — количество active extents. Каждый active extent добавляет 4 мегабайта к объему данных, пересылаемых после сбоя Primary. Этот параметр следует понимать как компромисс между двумя противоположностями.


Много active extents
Большой журнал активности улучшает скорость записи. Каждый раз, когда активируется новый extent, старый переводится в неактивное состояние. Для этого требуется произвести запись в метаданные. Если количество active extents большое, состояние старых меняется редко. Это снижает количество операций записи в метаданные и улучшает производительность.


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



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


Выбор количества active extents должен основываться на желаемом времени синхронизации при заданной скорости синхронизации. Рассчитать его можно следующим образом:



E=\frac{R * t_{s y n c}}{4 M i B}


Рисунок 17. Расчет active extents на основе скорости синхронизации и целевого времени синхронизации


R — скорость синхронизации, выраженная в Мбайт/с. tsync — целевое время синхронизации, в секундах. E — необходимое количество active extents.


Пример: предположим, что в кластере есть система ввода-вывода с пропускной способностью 200 МБайт/с, настроенная на скорость синхронизации (R) 60 МБайт/с. Мы хотим сохранить целевое время синхронизации (tsync) на уровне 4 минут или 240 секунд:



E=\frac{(60 M i B / s * 240 s)}{4 M i B}=3600


Рисунок 18. Пример расчета active extents на основе скорости синхронизации и целевого времени синхронизации


Стоит отметить, что DRBD 9 должен вести журнал активности даже на secondary-узлах — их данные могут использоваться для синхронизации других secondary-узлов.



17.4. Quick-sync bitmap


Quick-sync bitmap — это внутренняя структура данных, которую DRBD использует для каждого ресурса и узла, чтобы отслеживать синхронизированные (идентичные на обоих узлах) или несинхронизированные блоки. Это имеет значение только в случае, когда ресурс находится в disconnected-режиме.


В quick-sync bitmap один бит представляет чанк на диске размером 4 Кбайт. Если бит не установлен, это означает, что соответствующий блок синхронизирован с пиром. Другими словами, в него не производилась запись с момента отключения. Если же бит установлен, значит, блок был изменен и должен быть ресинхронизирован, когда соединение восстановится.


Как только DRBD обнаруживает операции записи на отключенном устройстве, он начинает устанавливать биты в quick-sync bitmap. Это делается в оперативной памяти — тем самым избегая дорогостоящих синхронных операций ввода-вывода метаданных. Только когда соответствующие блоки «остывают» (то есть исключаются из журнала активности), DRBD меняет информацию о них в quick-sync bitmap. Аналогичным образом, если случается ситуация, когда ресурс вручную выключается на оставшемся узле во время отключения, DRBD выгружает весь quick-sync bitmap в постоянное хранилище.


Когда пир восстанавливается или соединение возобновляется, DRBD объединяет bitmap-информацию с обоих узлов, чтобы определить, сколько данных необходимо ресинхронизировать. Одновременно проверяются идентификаторы поколений, чтобы определить направление синхронизации.


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


Ресинхронизацию также можно приостановить и возобновить вручную с помощью команд drbdadm pause-sync и drbdadm resume-sync. Однако не следует этим злоупотреблять: прерывая ресинхронизацию, вы оставляете диск secondary-узла в состоянии Inconsistent дольше, чем это необходимо.


17.5. Интерфейс для fencing’а пиров


В DRBD есть интерфейс для fencing’а[14] пиров для случаев прерывания связи репликации. fence-peer должен пометить диск(и) на пире как устаревший (Outdated) или выключить пир. При выполнении этих задач подразумевается, что сеть репликации отключена.


Fencing helper вызывается в случае, если соблюдаются следующие условия:


  1. Обработчик fence-peer определен в секции handlers для ресурса (или common), и
  2. Параметр fencing для ресурса установлен в resource-only или resource-and-stonith, и
  3. Узел был Primary, и связь репликации была прервана достаточно долго для того, чтобы DRBD[15] обнаружил отказ сети, или
  4. узел был повышен до Primary, но не был подключен к пиру, а диски пира не были помечены как Outdated.

У программы или скрипта-обработчика fence-peer есть переменные окружения DRBD_RESOURCE и DRBD_PEER. Они содержат имя затронутого ресурса DRBD и имя пира соответственно.


Любой fencing helper должен возвращать один из следующих exit-кодов:


Таблица 1. Коды возврата обработчика fence-peer


Код возврата Значение
3 диск на пир-узле уже находится в состоянии Inconsistent.
4 диск на пир-узле успешно переведен в состояние Outdated (или изначально был в состоянии Outdated).
5 не удалось установить соединение с пир-узлом, пир недоступен.6 — пир-узел отказался помечать диск как Outdated, потому что у затронутого ресурса роль Primary.
7 пир-узел успешно «огорожен» (fenced) от кластера. Это происходит только в случае, если fencing для затронутого ресурса имеет значение resource-and-stonith.


17.6. Режим клиента


Начиная с версии 9.0.13, DRBD поддерживает клиентов. В терминах DRBD клиент — это постоянный бездисковый (Diskless) узел. В конфигурации это обозначается ключевым словом none при указании блочного устройства хранения данных (ключевое слово — disk). В выводе команды drbdsetup status вы увидите, что состояние диска Diskless отображается зеленым цветом (обычно оно отображается красным).


Под капотом все пиры умышленно бездискового (intentional diskless) узла настраиваются с опцией peer-device-option --bitmap=no. Это означает, что они не выделяют слот для bitmap в метаданных diskless-узла. На intentional diskless-узле устройство помечается опцией --diskless=yes. Это можно сделать при помощи подкоманды new-minor в drbdsetup.


При помощи подкоманды статуса events2 можно посмотреть следующие флаги:


  • Поле client: у device. Если в нем указано yes, локальное устройство отмечено как постоянный diskless.
  • Поле peer-client у peer-device. Если оно установлено в yes, для этого пира не используется bitmap для отслеживания изменений.

Релевантные команды и их значение:


  • Команду drbdsetup peer-device-options --bitmap=yes ... можно запустить только в том случае, если в метаданных есть свободные bitmap-слоты (поскольку ее выполнение приведет к выделению нового слота).
  • Команду drbdsetup peer-device-options --bitmap=no ... можно запустить только в том случае, если пир является diskless: поскольку ее выполнение не приведет к высвобождению bitmap-слота.
  • Команда drbdsetup forget-peer ... используется для необратимого высвобождения bitmap-слота, закрепленного за определенным пиром.
  • Если один из двух или оба diskful-пира при подключении ожидают, что пир будет постоянным diskless, подключение завершится ошибкой.



14 — Для обсуждения Fencing и STONITH, пожалуйста, обратитесь к соответствующей странице Pacemaker.
15 — Это означает, например, timeout TCP, ping-timeout или обрыв соединения ядром, произошедший из-за отключения сетевого интерейса.


P.S.


Читайте также в нашем блоге:


Комментарии (1)


  1. Lirein
    03.06.2023 03:49
    +3

    Хабр снова торт. Прочитал статью на одном дыхании, очень годная вещь для понимания процессов внутри DRBD9. В эксплуатации Linstor кластер на трех нодах. В статье остался за кадром вопрос использования Diskless ресурсов и как хранятся метаданные к ним. Если можно прояснить и этот вопрос - будет просто замечательно.