Резерв не импортирует пул после zpool upgrade
АйТи Фреш
Серверы и инфраструктура

После zpool upgrade резервный сервер не импортирует пул: как заранее задать совместимые feature flags

Автор: , директор ООО «АйТи-Фреш» · · ~16 мин чтения
Основной и резервный серверы у одной дисковой полки, разъём резерва не подходит: zpool upgrade и импорт пула
Формат пула меняется в одну сторону: проверять совместимость резерва нужно до апгрейда.

Резервный сервер не импортирует пул потому, что zpool upgrade включил возможности формата, которых его версия ZFS не знает, а откатить это нельзя. Защита одна и делается заранее: свойство compatibility с файлом совместимости до zpool upgrade. Ниже: как это настроить, как читать состояния feature flags и как спасти пул, который уже обновили.

Почему резерв не видит пул после zpool upgrade

Классика, которую я вижу из года в год. Основной сервер обновили, zpool status начал писать, что у пула не включены некоторые возможности, и добросовестный администратор выполнил zpool upgrade -a, чтобы предупреждение исчезло. Предупреждение исчезло. Вместе с ним исчезла возможность поднять этот пул на резервном сервере, у которого версия ZFS постарше. Если у вас живёт обслуживание серверов и хранилищ без регламента на такие вещи, это вопрос времени, а не вероятности.

Механика простая. Формат пула в современном OpenZFS описывается не номером версии, а набором возможностей, feature flags. Команда zpool upgrade по документации включает на пуле все поддерживаемые текущей версией возможности. Дальше пул принимает записи в новом формате, и система, которая этих возможностей не знает, не может импортировать его на запись. Документация OpenZFS пишет об этом прямо: включённую возможность выключить нельзя, а импорт на запись требует поддержки всех активных возможностей. Назад по тому же пути вы не вернётесь.

Отдельно про самую частую путаницу. Обновление пакетов ZFS и обновление формата пула это два разных действия. Пакеты можно поставить и откатить через менеджер пакетов, формат пула откатить нельзя. Я регулярно слышу фразу «мы же только обновили систему», когда на деле кто-то по привычке выполнил zpool upgrade. Пакеты ничего не включают сами, формат меняете вы. Именно поэтому порядок работ важнее любой отдельной команды.

И второй миф, который хочется похоронить. Звучит он так: «поставим compatibility, и всё вернётся». Нет. Compatibility ограничивает то, что будет включено при создании пула и при следующих zpool upgrade. Уже включённое оно не выключает. Это ремень безопасности, а не машина времени: пристёгиваться надо до поездки.

Что значат состояния disabled, enabled и active

У каждой возможности три состояния. disabled значит, что изменений формата на диске нет и не будет, пока администратор не включит возможность. enabled значит, что администратор её включил, но изменений формата ещё не сделано: пул по-прежнему можно импортировать программой, которая возможность не знает. active значит, что изменения формата уже на диске, и импорт на запись требует поддержки этой возможности. Обычно из enabled возможность переходит в active позже, когда кто-то реально начнёт ею пользоваться, например включит на датасете checksum=blake3. Но не всегда: по документации head_errlog становится active сразу при включении, а vdev_zaps_v2 после ближайшего импорта, и обе не совместимы с чтением. Так что «включили, но ещё не используем» для части возможностей просто не существует.

Из этого следует неприятная вещь: enabled это отложенная мина. Сегодня резерв пул ещё импортирует, потому что возможность лишь включена. Через месяц кто-то создаст датасет с новым параметром, возможность станет active, и резерв внезапно не поднимет пул на запись. Причём никакой команды «в этот момент» не было, поэтому причину ищут долго. Обратно из enabled в disabled возможность вернуть нельзя, документация говорит прямо: после включения выключить нельзя.

Есть и спасательный круг, но маленький. Некоторые возможности помечены как совместимые с чтением (read-only compatible). Если все неподдерживаемые пулом возможности именно такие, пул можно импортировать только для чтения, указав при импорте свойство readonly=on. Если среди неподдерживаемых есть хоть одна несовместимая с чтением, не поможет и это. Конкретный перечень зависит от версии, в документации он приведён в таблице возможностей, и для вашей версии его надо смотреть в man zpool-features.

Посмотреть текущее состояние можно без риска, это чтение свойств. Фильтр по feature@ покажет статус каждой возможности, а команда zpool upgrade без аргументов перечислит пулы, где поддерживаемые возможности не включены. Я делаю эту проверку на обеих машинах и сравниваю вывод глазами, а потом сохраняю в тикет. Скучно, зато через год будет что показать.

zpool upgrade
zpool get all tank | grep feature@
zpool get -H -o property,value all tank | grep -E 'feature@|compatibility'
После zpool upgrade резервный сервер не импортирует пул: как заранее задать совместимые feature flags — схема
Схема к статье. Открыть схему в полном размере

Как задать compatibility до zpool upgrade

Свойство compatibility принимает значение off (по умолчанию, все возможности разрешены), legacy (ничего не включать) либо список файлов через запятую. Если файлов несколько, будут разрешены только возможности, которые есть во всех файлах, то есть пересечение. Файлы ищутся в /etc/zfs/compatibility.d и /usr/share/zfs/compatibility.d, при совпадении имён побеждает первый каталог. В исходниках OpenZFS лежат готовые наборы: openzfs-2.1-linux, openzfs-2.2, grub2-2.12, compat-2021 и другие, а какие профили есть именно у вас, покажет обычный ls /usr/share/zfs/compatibility.d. Старый пакет может не знать свежих профилей, поэтому список проверяйте у себя.

Для нового пула всё просто: задаёте набор при создании. Для уже существующего пула порядок такой: сначала задаёте compatibility, потом выполняете zpool upgrade, и он включит только то, что разрешено файлом. Документация подтверждает, что zpool upgrade -a учитывает это свойство. Заодно обратите внимание на legacy: если выставить его, апгрейд вообще не произойдёт. Для пула, который вы пока не готовы трогать, это самый честный предохранитель.

# новый пул сразу с ограничением (имя файла проверьте через ls)
zpool create -o compatibility=openzfs-2.1-linux tank mirror /dev/sdb /dev/sdc

# существующий пул: сначала ограничение, потом апгрейд
zpool set compatibility=openzfs-2.1-linux tank
zpool get compatibility tank
zpool upgrade tank

Своя маска нужна, когда стандартные профили не подходят. Берёте ближайший файл из /usr/share/zfs/compatibility.d, копируете в /etc/zfs/compatibility.d под своим именем и убираете лишние возможности. Одна оговорка из документации, о которой забывают: неизвестная возможность в файле из /etc/zfs/compatibility.d это ошибка, обработка останавливается, а в системном каталоге только предупреждение. Если в вашем парке разные версии ZFS, сверяйте имена возможностей на обеих машинах, а не доверяйте памяти.

Что я рекомендую на практике. Основной и резервный серверы обновляются парой, а формат пула вы поднимаете только до пересечения возможностей обоих. Если версии отличаются, зафиксируйте профиль по более старой, пока не обновите вторую. Один скучный zpool set на этапе ввода в эксплуатацию экономит вечер в авральном режиме. Если выигрыш от новой возможности вы не измеряли, она вам, скорее всего, не нужна.

compatibility работает вперёд: оно ограничивает будущие включения возможностей, но не выключает те, что уже enabled или active. Откатить формат пула этим свойством нельзя.
Обратите внимание: Как задать compatibility до zpool upgrade — схема
Обратите внимание: Как задать compatibility до zpool upgrade. Открыть схему в полном размере

Пул уже обновили: read-only импорт, чекпоинт и пересоздание

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

Если импорт на запись не проходит, но все неподдерживаемые возможности совместимы с чтением, пул можно поднять только для чтения. В документации readonly указан как свойство, задаваемое только при импорте. Ключ -N не монтирует файловые системы, и это полезно, чтобы сначала посмотреть на пул, а потом уже монтировать. Дальше копируете данные куда-то на живую систему, а резерв получает обновлённые пакеты. Это спасение данных, а не возврат к нормальной работе.

zpool import -o readonly=on -N tank
zpool get all tank | grep -E 'unsupported@|feature@'
zfs mount -a

Документация описывает и вспомогательные свойства unsupported@: для каждой неподдерживаемой возможности на импортированном пуле появляется свойство, объясняющее, почему импорт был разрешён, со значениями inactive или readonly. Это быстрый способ понять, что именно мешает резерву. Имейте в виду, что я не нашёл в документации описания точной формулировки ошибки при отказе импорта, поэтому не привожу её текст: сверяйте по своему выводу.

Единственный честный способ вернуть пул в состояние, которое понимает старая версия, это создать новый пул с нужным compatibility и перелить данные через zfs send | zfs receive. Не забудьте про флаги потока: по документации zfs send с -L, -e и -c требует соответствующих возможностей на принимающем пуле (large_blocks, embedded_data, а при активных lz4_compress или zstd_compress ещё и их). Поэтому набор в compatibility должен их включать, иначе поток не примется. Это час-два возни для небольшого пула и недели для больших, так что заранее выбирайте профиль аккуратно. Отдельно про перенос и синхронизацию данных между площадками у нас есть разбор для случаев, когда ZFS на другой стороне нет вообще.

Теперь про zpool checkpoint. Это снимок состояния всего пула, к которому можно вернуться командой zpool import --rewind-to-checkpoint. Пока чекпоинт существует, запрещены remove, attach, detach, split и reguid, и он занимает свободное место, так что держать его неделями нельзя. Подходит ли он как страховка перед zpool upgrade, в документации на странице чекпоинта прямо не сказано. Поэтому я проверяю это на тестовом пуле на файлах-заглушках перед тем, как опираться на него в проде, и после проверки обязательно удаляю чекпоинт командой zpool checkpoint -d.

zpool checkpoint tank
zpool upgrade tank
# если что-то пошло не так, пул экспортируют и импортируют с откатом
zpool export tank
zpool import --rewind-to-checkpoint tank
# когда всё проверено
zpool checkpoint -d tank

Если пул у вас уже в таком состоянии и времени разбираться нет, напишите нам: мы в АйТи-Фреш снимем состояние feature flags, поднимем данные в режиме чтения и выровняем версии на обоих серверах, не трогая рабочую нагрузку дольше одного окна.

Не доверяйте чекпоинту как единственной страховке перед zpool upgrade, пока не отрепетировали откат на тестовом пуле. Он не заменяет резервную копию, которую вы проверяли восстановлением.

Как у ГидропоникаЛаб Урожай это сломалось и как мы это починили

Разберу на условном клиенте, ГидропоникаЛаб Урожай, 11 рабочих мест. Схема скромная: основной файловый сервер на ZFS с пулом из двух зеркал по два диска и холодный резервный сервер, которому по ручной процедуре передаётся дисковая полка, если основной сервер вышел из строя. Данные: около 6 ТБ журналов теплиц, протоколов замеров и сканов документов. Резерв обновляли два года назад и забыли, потому что «он же выключен».

На основном сервере обновили систему, OpenZFS переехал с ветки 2.1 на 2.2, а резерв так и остался на 2.1. В ближайшее окно кто-то выполнил zpool upgrade -a. Спустя три недели мы проводили плановую репетицию переключения, и пул на резерве не поднялся вообще. Репетиция, к слову, это та часть работы, которую все считают лишней, пока она не сработает. Здесь она сработала ровно так, как должна: плохие новости пришли в плановый четверг, а не в ночь аварии.

Дальше было просто, хотя и не быстро. Первым делом попробовали импорт только для чтения, и он тоже не прошёл: среди включённых возможностей была head_errlog, которая становится active сразу и с чтением не совместима. Так что спасательный круг из прошлого раздела здесь не сработал, и это честный пример его пределов. Мы вернули полку на основной сервер, убедились по zpool status, что данные целы, и обновили пакеты ZFS на резерве до той же ветки 2.2. После этого пул штатно поднялся на резерве на запись. Дольше всего было не технически, а организационно: нужно было согласовать окно и проверить, что пулов с другой версией в парке больше нет. Всего ушло около четырёх часов работы и одно окно в выходной.

Что мы закрепили после этого. Версии ZFS на обоих серверах выровнены, в карточке сервера записан профиль compatibility, а zpool upgrade вынесен в отдельный пункт регламента с подписью. Репетиция переключения теперь раз в квартал и включает пробный импорт с readonly=on. Деньги тут простые: четыре часа плановой работы дешевле, чем сутки простоя производства, которое планирует поливы по этим журналам.

Регламент: в каком порядке обновлять ZFS-пулы

Вот порядок, который я использую и вам советую. Сначала обновляете пакеты на резерве и проверяете, что пул на нём импортируется. Потом обновляете пакеты на основном сервере. Затем задаёте compatibility по профилю, который понимают оба сервера. И только после этого, отдельным окном, выполняете zpool upgrade для конкретного пула, а не -a для всех подряд. Если кому-то кажется, что -a быстрее, пусть сравнит эту экономию с часами простоя.

Обязательная часть регламента, которую пропускают: проверка после. Через zpool get убедитесь, что compatibility на месте, а состояния feature@ те, которых вы ждали. Потом на резерве выполните пробный импорт в безопасном режиме: свойство altroot по документации задаёт альтернативный корень и не сохраняется, так что тестовый импорт не оставляет следов в обычном монтировании. Вспомним ещё и про zpool import -N: он нужен, чтобы импорт прошёл без монтирования файловых систем.

zpool import -N -o readonly=on -o altroot=/mnt/test tank
zpool get compatibility,version tank
zpool export tank

Бэкап делают все, проверяет его почти никто, и со снимками ZFS то же самое. Если у вас репликация через zfs send, добавьте в регламент проверку принимающей стороны: по документации потоки с -L, -e и -c требуют включённых соответствующих возможностей на получателе. Для расширения пула и смежных операций у нас есть отдельный разбор про автоматическое расширение и zpool online, он пригодится при замене дисков на новые большие.

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

Памятка: Регламент: в каком порядке обновлять ZFS-пулы — схема
Памятка: Регламент: в каком порядке обновлять ZFS-пулы. Открыть схему в полном размере

Сводка: варианты защиты и что они закрывают

Чтобы не терять нить, соберу варианты в одну таблицу. Колонки простые: что делаете, на что влияет и какой у этого предел. Слева самое дешёвое, справа самое дорогое. Всё в таблице следует из документации OpenZFS по zpool-features, zpool-upgrade и zpoolprops. | Что делаете | Что даёт | Предел | |---|---|---| | compatibility=файл до upgrade | Включаются только возможности из файла | Не отменяет уже включённое | | compatibility=legacy | Апгрейд формата не происходит | Новые возможности недоступны | | import -o readonly=on | Чтение данных, если неподдерживаемые возможности совместимы с чтением | Запись невозможна | | zpool checkpoint | Возврат к состоянию на момент снимка | Блокирует remove, attach, detach, split, reguid; занимает место | | Новый пул и zfs send/receive | Формат под нужный профиль | Время и место на копию |

Мой выбор по умолчанию: профиль compatibility на обоих серверах, zpool upgrade по одному пулу и квартальная репетиция. Чекпоинт использую как дополнительный слой, но не как основной. Если пул уже обновили, сначала readonly=on и спасение данных, потом выравнивание версий, а пересоздание пула оставляю на крайний случай. Да, это скучно. Именно потому и работает.

Частые вопросы

Можно ли вернуть возможность из enabled в disabled?

Нет. По документации OpenZFS возможность, однажды включённую, выключить нельзя. Если нужен формат без неё, создают новый пул с нужным compatibility и переносят данные через zfs send и zfs receive.

Чем enabled отличается от active?

При enabled администратор возможность включил, но изменений формата на диске ещё нет, и пул можно импортировать системой без её поддержки. При active изменения формата уже на диске, и для импорта на запись нужна поддержка этой возможности.

Можно ли импортировать пул только для чтения, если ZFS старее?

Можно, если все неподдерживаемые возможности пула совместимы с чтением. Тогда пул импортируют со свойством readonly=on, например командой zpool import -o readonly=on -N tank. Если есть хотя бы одна несовместимая возможность, не поможет.

Как задать compatibility на уже созданном пуле?

Командой zpool set compatibility=имя_файла пул, до zpool upgrade. Затем апгрейд включит только возможности, которые есть во всех указанных файлах. Свойство не выключает уже включённые возможности.

Где лежат файлы compatibility?

В каталогах /etc/zfs/compatibility.d и /usr/share/zfs/compatibility.d. При одинаковых именах приоритет у /etc/zfs/compatibility.d. Какие профили есть на вашей машине, покажет ls.

Столкнулись с похожей задачей? Обращайтесь — решим

Если у вас происходит что-то из описанного в этой статье — или любая другая проблема с ИТ-инфраструктурой, — обращайтесь в любое время. Мои специалисты и я лично разберём ситуацию, найдём настоящую причину и доведём до решения.

Возьмёмся и за разовую задачу, и за постоянное обслуживание. Первичная консультация — бесплатно и без обязательств.

📞 +7 903 729-62-41 ✈ Telegram @ITfresh_Boss

С уважением, Семёнов Евгений Сергеевич, директор «АйТи Фреш» — IT-аутсорсинг для компаний до 50 рабочих мест, 15+ лет практики

Источники

© ООО «АйТи-Фреш» · Москва · Все статьи