null

ZFS on Linux = data corruption

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

Первая проблема связана с тем, ZoL при загрузке может самостоятельно, т.е. без выполнения пользователем команды zpool import, импортировать пул на подключенном устройстве. Так, например, у меня ZoL сам импортировал свежесозданный на FreeBSD пул на флешке, которая была подключена в момент загрузки системы. Результатом может стать:

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

Вторая проблема связана с тем, что ZFS для определения был ли пул импортирован на какой либо системе записывает на пул hostid импортировавшего узла. На Solaris и FreeBSD уникальность hostid худо-бедно обеспечивается, а вот в различных дистрибутивах Linux с этим всё гораздо хуже. В Linux библиотечная функция gethostid(3) в первую очередь пытается прочитать содержимое файла /etc/hostid, если он отсутствует, то пытается отрезолвить имя узла и использовать полученный IP в качестве идентификатора, иначе возвращается 00000000. В, например, Oracle Enterprise Linux файл /etc/hostid автоматически не генерируется, и в момент импорта пула hostid вполне может оказаться равным 00000000. ZFS же использует такой идентификатор как обозначание, что пул не был ни кем импортирован, т.е. такой пул можно спокойно импортировать второй раз. В Debian файл /etc/hostid генерируется, но в большинстве случаев туда записывается идентификатор 007f0101. А в результате при использовании разделяемого хранилища Вы сможете спокойно повторно импортировать пул на другом сервере и даже не заметить это.

Для борьбы с этой проблемой достаточно обеспечить уникальность hostid на всех узлах с ZoL. В Oracle Enterprise Linux для этого можно воспользоваться командой genhostid(1). Если разработчики дистрибутива забыли её положить, то можно выполнить:

dd if=/dev/random of=/etc/hostid bs=1 count=4

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

  1. Импорт пула на первом (активном) узле кластера
  2. Останов работы первого узла по какой либо причине - kernel panic, аппаратная проблема и т.д.
  3. Импорт пула на втором (резервном) узле кластера
  4. Запуск первого узла

После выполнения этих шагов, даже если Вы обеспечили уникальность hostid на них, пул будет импортирован на обоих узлах. В Solaris и FreeBSD эта проблема отсутствует, и в ZoL, судя по спискам рассылки, эта проблема появилась не очень давно, но на текущий момент она всё еще актуальна.

Последние две проблемы могут привести к двойному импортированию одного пула на нескольких системах, и, соответствено, устройства, хранящие данные пула, должны быть доступны нескольким системам, через, например, FC-AL, SAS, iSCSI или т.д. Двойное импортирование рано или поздно приведёт к разрушению целостности метаданных пула и полной надоступности данных на нём:

# zpool import
   pool: tank
     id: 9400711421901820791
  state: FAULTED
 status: The pool metadata is corrupted.
 action: The pool cannot be imported due to damaged devices or data.
   see: http://zfsonlinux.org/msg/ZFS-8000-72
 config:

        tank        FAULTED  corrupted data
          sdb       ONLINE

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

Коротко о себе:

Работаю в компании Tune-IT и тьютором кафедры Вычислительной техники в СПбГУИТМО.

Очень люблю команду cat, core solaris и IPv6.