АйТи Фреш
Главная / Статьи / Серверы и хостинг
Серверы и хостинг

Поменяли диски на более ёмкие, а пул ZFS остался прежнего размера: разбираемся с autoexpand, expandsize и zpool online -e

Автор: Семёнов Евгений Сергеевич, директор ООО «АйТи-Фреш» · · ~23 мин чтения
Поменяли диски на более ёмкие, а пул ZFS остался прежнего размера: разбираемся с autoexpand, expandsize и zpool online -e
Иллюстрация к статье «Поменяли диски на более ёмкие, а пул ZFS остался прежнего размера: разбираемся с autoexpand, expandsize и zpool online -e».

Это для тех, кто закупил диски вдвое-втрое ёмче, честно отресилверил их по одному, а в zpool list увидел ту же самую цифру, что и месяц назад. Разберу, куда делось место, почему autoexpand не спасает задним числом, почему в mirror и RAIDZ бессмысленно менять один диск из группы, чем autoreplace принципиально не про ёмкость, и как выглядит правильная последовательность замены. С разбором выезда в фотостудию на 22 рабочих места: RAIDZ2 из шести дисков, девять дней ресилвера и цифры до и после.

«Диски новые, места нет»: первым делом смотрим колонку EXPANDSIZE

Типичный звонок. Купили шесть дисков вместо шести старых, поменяли, каждый честно отресилверился, zpool status зелёный, все vdev ONLINE, ошибок ноль. А zpool list показывает ровно тот же размер, что и до всей эпопеи. Первая мысль у админа обычно неправильная: «диски не той серии», «контроллер режет», «ZFS не видит полный объём». Диски он видит. Просто новое место лежит рядом с пулом и в пул не присоединено.

У пула есть отдельное свойство expandsize, и в мануале OpenZFS оно описано буквально так: «Amount of uninitialized space within the pool or device that can be used to increase the total capacity of the pool» — количество неинициализированного пространства в пуле или устройстве, которое можно использовать для увеличения общей ёмкости. Хорошая новость: zpool list печатает эту колонку по умолчанию, вместе с name, size, allocated, free, checkpoint, fragmentation, capacity, dedupratio, health и altroot. То есть ответ на вопрос «а место-то вообще есть?» у вас уже на экране, просто взгляд по привычке цепляется за SIZE и CAP.

Смотреть надо так — обязательно с -v, чтобы увидеть картину по каждому vdev и по каждому диску отдельно:

zpool list -v tank
zpool list -o name,size,allocated,free,expandsize,fragmentation,health
zpool get autoexpand,autoreplace,size,expandsize,free tank

Если в EXPANDSIZE стоит прочерк — расширять нечего, проблема в другом месте (об этом в последнем разделе). Если там число — всё в порядке, ZFS видит новое место, просто вы ей не сказали его забрать. Это не поломка и не потеря данных, это ровно одна команда.

Прежде чем идти в гарантию и ругать поставщика дисков — выполните `zpool list -v`. В 9 случаях из 10 при жалобе «пул не вырос» EXPANDSIZE уже показывает ровно ту дельту, которую вы ожидали получить.
Памятка: «Диски новые, места нет»: первым делом смотрим колонку EXPANDSIZE — схема
Памятка: «Диски новые, места нет»: первым делом смотрим колонку EXPANDSIZE. Открыть схему в полном размере

autoexpand по умолчанию выключен — и включать его задним числом бесполезно

Свойство autoexpand в мануале zpoolprops(7) описано так: «Controls automatic pool expansion when the underlying LUN is grown. If set to on, the pool will be resized according to the size of the expanded device». Значение по умолчанию — **off**. Это не баг и не «недокрутили дефолты»: ZFS сознательно не забирает лишнее пространство молча, потому что операция односторонняя — пул можно нарастить, но нельзя ужать обратно.

И вот тут главный подвох, на котором обжигаются почти все. autoexpand — реактивное свойство. Оно срабатывает по событию изменения размера устройства: диск подменили, LUN на СХД растянули, ZFS получила уведомление и, если флаг включён, пересчитала ёмкость. Оно не работает как фоновый сканер. Если вы прогнали всю замену дисков при autoexpand=off, а потом сообразили и включили — ничего не произойдёт. События уже прошли, их некому переиграть. В zpoolprops(7) нет отдельного предупреждения на эту тему, но из описания видно: свойство управляет расширением «when the underlying LUN is grown», то есть в момент роста устройства. Если этот момент прошёл при выключенном свойстве, место остаётся в EXPANDSIZE и забирается вручную.

Эта «дополнительная настройка» — команда zpool online -e. В мануале zpool-online(8) синопсис zpool online [--power] [-e] pool device…, а флаг -e описан как «Expand the device to use all available space». Обратите внимание: команда принимает устройство, а не пул целиком. Значит, прогнать её нужно по каждому диску, который вы поменяли.

# включаем на будущее
zpool set autoexpand=on tank

# и разово добираем то, что уже заменено
# -P печатает полные пути устройств, -H убирает заголовки
for d in $(zpool list -vHP tank | awk '$1 ~ /^\/dev\// {print $1}'); do
  zpool online -e tank "$d"
done

zpool list tank

Отдельная мелочь, которая на форумах съедает людям вечер: имя устройства должно совпадать с тем, что печатает zpool status (короткое имя вроде ata-…), или быть полным путём к тому же узлу (zpool status -P покажет его целиком). На FreeBSD и TrueNAS CORE это gptid/38bb3ed6-… — в известном треде на форуме FreeNAS человек подставлял голый GUID без префикса gptid/, и команда отвечала, что такого устройства нет. Голый серийник или обрезанный идентификатор не сработает.

Правильный порядок: `zpool set autoexpand=on` выполняется ДО первой замены диска, а не после последней. Это одна строчка, которая экономит потом час возни и нервный звонок в субботу.
Поменяли диски на более ёмкие, а пул ZFS остался прежнего размера: разбираемся с autoexpand, expandsize и zpool online -e — схема
Схема к статье. Открыть схему в полном размере

Один диск ничего не решает: mirror и RAIDZ расширяются только целой группой

Второй по частоте вопрос: «а достаточно ли заменить один диск, чтобы получить хоть какую-то прибавку?» Нет. Мануал формулирует однозначно, и формулировка повторяется и в описании свойства autoexpand, и в описании флага -e: если устройство входит в mirror или raidz, то всё новое место становится доступным пулу только после того, как увеличены **все** устройства этой mirror/raidz-группы.

Логика простая, если помнить, как ZFS раскладывает данные. В зеркале обе половины должны хранить одно и то же — ёмкость зеркала равна меньшему из дисков. В RAIDZ полоса режется на равные куски по всем участникам группы, поэтому ширина полосы упирается в самый маленький диск. Заменили в RAIDZ2 из шести дисков три штуки на вдвое большие — прибавка ровно ноль. Заменили пять — тоже ноль. Прибавка появляется только на шестом.

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

А вот разные vdev в пуле друг от друга не зависят. Если у вас пул из двух зеркал и вы полностью переобули первое зеркало — пул вырастет сразу, не дожидаясь второго. Это, кстати, весомый аргумент в пользу зеркал вместо одного широкого RAIDZ там, где важно наращивать ёмкость поэтапно: обновляем по два диска, получаем прибавку сразу, повторяем через полгода.

Пока группа не переобута полностью, вы едете на уменьшенной избыточности дольше, чем нужно, и не получаете взамен ни байта места. Планируйте замену RAIDZ-группы как один непрерывный проект, а не как растянутую на месяцы историю.
Памятка: Один диск ничего не решает: mirror и RAIDZ расширяются только целой группой — схема
Памятка: Один диск ничего не решает: mirror и RAIDZ расширяются только целой группой. Открыть схему в полном размере

autoreplace — это вообще не про ёмкость

Свойства лежат рядом в выводе zpool get all, называются похоже, и их регулярно путают. Так вот, autoreplace в мануале описан так: «Controls automatic device replacement. If set to off, device replacement must be initiated by the administrator by using the zpool replace command. If set to on, any new device, found in the same physical location as a device that previously belonged to the pool, is automatically formatted and replaced». Дефолт тоже **off**.

Переведу на человеческий: autoreplace — про физический слот. Умер диск в корзине номер 4, вы выдернули труп, вставили новый — и ZFS сама, без вашей команды, размечает его и запускает ресилвер. Ни слова про размер, ни слова про расширение. Если новый диск больше — пул от этого не вырастет, вырастет только expandsize. Расширением по-прежнему заведует autoexpand или ручной zpool online -e. И важная деталь из того же мануала: autoreplace (как и autoonline) работает только при настроенном и запущенном демоне событий ZFS — ZED. На Linux без zfs-zed флаг autoreplace=on просто ничего не делает.

Моё личное мнение: на серверах клиентов я держу autoreplace=off и делаю zpool replace руками. Причина не в недоверии к механизму, а в бытовухе. У малого бизнеса в серверную периодически заходят не только айтишники, и диск в свободный слот втыкают по самым неожиданным поводам — «подключить архив», «скинуть бэкап», «посмотреть, что на нём». С включённым autoreplace такой диск будет отформатирован и съеден пулом молча. С выключенным — просто появится в системе и будет ждать решения. На стойке в ЦОДе, где к железу физически никто левый не подходит, аргумент слабее, и там autoreplace=on вполне разумен.

zpool get autoreplace tank
# ручная замена по идентификатору, с ожиданием завершения
zpool replace -w tank /dev/disk/by-id/ata-OLD_SERIAL /dev/disk/by-id/ata-NEW_SERIAL
Если ждёте роста ёмкости — вам нужен autoexpand. autoreplace на размер пула не влияет вообще никак, сколько его ни включай.

Разбор выезда: фотостудия «ФотоПричал», 22 рабочих места, RAIDZ2 из шести дисков

Стенд. Фотографическая студия «ФотоПричал», 22 рабочих места: фотографы, ретушёры, менеджеры по заказам. Сервер один — файловый архив RAW-съёмок и готовых проектов плюс приёмник резервных копий рабочих станций. Debian 12 с OpenZFS из bookworm-backports (ветка 2.2), пул tank — один vdev RAIDZ2 из шести WD Red Plus по 4 ТБ на HBA в режиме IT. Сырой объём 21,8 ТиБ, полезный около 14,5 ТиБ, занято 88 %. Компрессия lz4 (на RAW почти ничего не даёт, но и не мешает), ashift=12, дедупликация выключена. Задача понятная: за сезон свадеб и каталожных съёмок архив раздулся, под копии места не осталось, закупили шесть Toshiba MG07ACA12TE по 12 ТБ.

Работу вёл приходящий администратор студии, я подключился уже по факту. Замену он делал аккуратно и правильно: по одному диску, zpool replace, дожидался конца ресилвера, только потом следующий. При 88 % заполнения каждый ресилвер шёл от 14 до 20 часов — итого девять дней с выходными. И вот в пятницу вечером финальный диск досинхронизировался, zpool status чистый, а zpool list показывает те же 21,8T. Дальше был звонок владельца студии и фраза «мы, кажется, купили не те диски на двести с лишним тысяч».

Диски были ровно те. Смотрим:

$ zpool list -v tank
NAME    SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP  HEALTH
tank   21.8T  19.2T   2.6T        -     43.7T    31%    88%  1.00x  ONLINE
  raidz2-0 21.8T 19.2T 2.6T       -     43.7T

$ zpool get autoexpand tank
NAME  PROPERTY    VALUE   SOURCE
tank  autoexpand  off     default

EXPANDSIZE 43,7 ТиБ — ровно разница между шестью дисками по 12 ТБ и шестью по 4 ТБ. Место на месте, autoexpand в дефолтном off, событий расширения уже не будет. Лечение заняло меньше минуты: включаем свойство на будущее и добираем текущее вручную по каждому из шести устройств.

zpool set autoexpand=on tank
for d in /dev/disk/by-id/ata-TOSHIBA_MG09ACA12TE_*; do
  [ -b "$d" ] || continue
  case "$d" in *-part*) continue;; esac
  zpool online -e tank "$d"
done
zpool list tank

После этого SIZE стал 65,5 ТиБ, полезная ёмкость RAIDZ2 выросла с 14,5 до 43,6 ТиБ, занятость упала с 88 % до 29 %. Дальше запустили полный zpool scrub — он прошёл за 31 час без единой ошибки — и вернули хранение копий на нормальную глубину. Ни экспорта пула, ни перезагрузки, ни простоя: ретушёры всё это время открывали RAW-файлы с шар как обычно.

Что в этой истории стоит запомнить помимо самой команды. Первое: девять дней пул ехал на пониженной избыточности. RAIDZ2 это пережил спокойно — при ресилвере одного диска остаётся ещё один запасной уровень чётности. Будь там RAIDZ1, все девять дней сервер жил бы вообще без права на ошибку, при полностью нагруженных чтением старых дисках, — а именно в ресилвере старьё и сыпется. Второе: 88 % заполнения удвоили время ресилвера. Пул, который вы собираетесь переобувать, лучше сначала почистить.

На RAIDZ1 из дисков по 12 ТБ и старше я замену по одному без свежей проверенной резервной копии просто не начинаю. Ресилвер — самая тяжёлая нагрузка на оставшиеся диски за весь их жизненный цикл, и умирают соседи именно в этот момент.

Порядок замены, который я использую сам

Последовательность отработанная, отклоняться от неё смысла нет. До первого прикосновения к железу: полный zpool scrub и дождаться результата, smartctl -a по всем текущим дискам, актуальная копия критичных данных вне пула. Если scrub нашёл ошибки — сначала разбираемся с ними, потом меняем диски. Начинать ресилвер на пуле с неизвестными проблемами — способ получить второй инцидент поверх первого.

Дальше — свойства. zpool set autoexpand=on до первой замены. Затем работаем строго по одному диску: zpool replace -w, где -w заставляет команду ждать окончания ресилвера, — это удобнее, чем крутить zpool status в цикле, и заодно защищает от соблазна дёрнуть второй диск раньше времени. Мануал требует, чтобы размер нового устройства был не меньше минимального размера устройств в mirror или raidz-конфигурации; на практике это значит «не покупайте диски меньше существующих», а вот больше — сколько угодно.

# 1. подготовка
zpool scrub -w tank      # -w: дождаться окончания scrub
zpool status -v tank
zpool set autoexpand=on tank

# 2. по одному диску, с ожиданием
zpool replace -w tank /dev/disk/by-id/ata-OLD1 /dev/disk/by-id/ata-NEW1
zpool status -v tank

# 3. после последнего диска группы — добрать место, если autoexpand не сработал
zpool list -v tank
zpool online -e tank /dev/disk/by-id/ata-NEW1

# 4. контрольный прогон
zpool scrub -w tank

Отдельно про идентификаторы. Всегда by-id или wwn, никогда sda/sdb. Имена вида sdX меняются от перезагрузки к перезагрузке и от порядка инициализации HBA, и рано или поздно вы выдернете не тот диск. У меня в практике был случай, когда админ по sdX выдернул живой диск вместо помеченного на замену на RAIDZ1 — дальше вечер восстановления. С by-id серийник совпадает с наклейкой на дисковой корзине, и ошибиться физически сложнее.

Не запускайте вторую замену, пока не завершилась первая. В RAIDZ1 это гарантированная потеря пула при отказе любого третьего диска, а в RAIDZ2 — снятие последнего страховочного слоя ровно в момент максимальной нагрузки.
Порядок действий: Порядок замены, который я использую сам — схема
Порядок действий: Порядок замены, который я использую сам. Открыть схему в полном размере

EXPANDSIZE пустой, а место должно быть: четыре причины и особенности Proxmox и TrueNAS

Первая и самая частая — vdev собран не на целом диске, а на разделе. Если вы отдавали ZFS /dev/sda1, то она честно работает внутри границ этого раздела и чужую таблицу разделов трогать не станет. Сначала растите раздел средствами ОС (growpart, parted resizepart, sgdisk), перечитывайте таблицу, и только потом zpool online -e. Когда vdev создан на целом диске, ZFS сама раскладывает GPT (основной раздел данных плюс небольшой резервный в конце) и при -e переразмечает его самостоятельно — вручную лезть не надо.

Вторая — виртуальный диск или LUN на СХД растянули на уровне хранилища, но гостевая ОС об этом не знает. Пока ядро видит старый размер блочного устройства, expandsize будет нулевым. На Linux помогает echo 1 > /sys/block/sdX/device/rescan или rescan-scsi-bus.sh, дальше lsblk должен показать новый размер, и только потом идём в ZFS. Проверяйте цепочку снизу вверх: СХД → ядро → ZFS.

Третья — вы просто не всю группу переобули. Возвращаемся к третьему разделу: пять из шести дисков дают ноль. Смотрите zpool list -v построчно, там сразу видно устройство, которое осталось маленьким.

Четвёртая — вам на самом деле нужно не расширение, а добавление диска в существующую группу. С OpenZFS 2.3 появилась RAIDZ expansion (нужна feature raidz_expansion): zpool attach pool raidz-vdev new_device добавляет диск прямо в RAIDZ-группу. По man zpool-attach(8), избыточность сохраняется во время и после операции, при отказе диска расширение ставится на паузу до восстановления группы, а в конце автоматически запускается scrub всего пула. Честная оговорка: старые блоки сохраняют прежнее соотношение данных и чётности, а ZFS продолжает считать ёмкость по старому коэффициенту, поэтому zfs list и df покажут прибавку меньше арифметической. Лечится перезаписью данных: классическое zfs send -R | zfs receive в новый датасет или команда zfs rewrite, которая переписывает блоки файлов на новое место без изменения содержимого (-r — рекурсия, -v — вывод имён; в 2.4.0 добавлен -P, сохраняющий логическое время рождения блоков, чтобы не раздувать инкрементальные send-потоки).

Отдельно про готовые платформы, где разделы создаёт не админ, а установщик. Proxmox VE при установке на ZFS сам размечает загрузочные диски: на каждом есть BIOS boot, ESP и раздел ZFS (обычно третий), а пул rpool собран именно на …-part3. Поэтому замена диска в rpool по документации Proxmox — это sgdisk <здоровый диск> -R <новый диск>, sgdisk -G <новый диск>, zpool replace -f rpool <старый -part3> <новый -part3> и proxmox-boot-tool format/init для ESP. Копирование таблицы со старого маленького диска даст маленький третий раздел — пока его не растянете (sgdisk/parted resizepart), zpool online -e прибавки не даст. Пулы данных, созданные в GUI Proxmox на целых дисках, этой проблемы не имеют. TrueNAS тоже кладёт ZFS в раздел (на CORE — gptid/…, часто со swap-разделом перед ним), но при замене через веб-интерфейс сам размечает новый диск на всю ёмкость; после замены последнего диска группы проверяйте EXPANDSIZE и, если нужно, выполняйте zpool online -e с полным gptid/….

# раздел вместо целого диска
growpart /dev/sda 1 && partprobe /dev/sda
zpool online -e tank /dev/disk/by-id/…-part1

# LUN растянули на СХД
echo 1 > /sys/block/sdb/device/rescan
lsblk /dev/sdb

# добавить диск в существующую RAIDZ-группу (OpenZFS 2.3+)
zpool attach -w tank raidz2-0 /dev/disk/by-id/ata-NEW7
Если EXPANDSIZE прочерк и раздел с LUN тут ни при чём — не пытайтесь «починить» это флагом `-f` или пересозданием пула. Проверьте по `zpool list -v`, какое устройство осталось маленьким: в моей практике это почти всегда забытый шестой диск или диск, который поставщик прислал другой модели и меньшего фактического объёма.

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

Достаточно ли заменить один диск, чтобы пул стал больше?

Нет. Мануал OpenZFS однозначен: если устройство входит в mirror или raidz, новое место становится доступным пулу только после того, как увеличены все устройства этой группы. Пять новых дисков из шести в RAIDZ2 дают ровно ноль прибавки. Исключение — если в пуле несколько vdev: полностью переобутый vdev даёт прибавку сразу, не дожидаясь остальных.

Я включил autoexpand=on уже после замены дисков, и ничего не произошло. Это баг?

Нет, это штатное поведение. autoexpand реагирует на событие увеличения устройства, а не сканирует пул периодически. События замены уже прошли, переигрывать их нечему. Выполните `zpool online -e <pool> <device>` по каждому заменённому диску — место присоединится за секунды.

Нужен ли простой, перезагрузка или export/import пула?

Нет. `zpool online -e` работает на смонтированном рабочем пуле, пользователи ничего не замечают. Export/import или перезагрузка при включённом autoexpand тоже обычно подхватывают новый размер, но это лишний простой ради результата, который достигается одной командой без остановки сервиса.

Насколько опасна команда zpool online -e? Данные не пострадают?

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

Чем autoreplace отличается от autoexpand?

autoreplace автоматически подхватывает и заменяет диск, вставленный в тот же физический слот, где раньше стоял член пула. К размеру пула это не имеет отношения вообще. За ёмкость отвечает autoexpand либо ручной `zpool online -e`. Оба свойства по умолчанию off.

У меня Proxmox, пул rpool на загрузочных дисках. Что там иначе?

Установщик Proxmox кладёт `rpool` в третий раздел каждого диска. Если при замене вы скопировали таблицу разделов со старого диска через `sgdisk -R`, раздел ZFS на новом диске останется старого размера. После замены всех дисков зеркала растяните третий раздел на каждом из них, перечитайте таблицу и выполните `zpool online -e rpool <диск>-part3` для каждого. Пулы данных, созданные в GUI на целых дисках, расширяются без возни с разделами.

Можно ли вместо полной замены группы просто добавить один диск в существующий RAIDZ?

Да, начиная с OpenZFS 2.3 работает RAIDZ expansion: `zpool attach pool raidz-vdev new_device`. Избыточность сохраняется во время и после операции, по завершении автоматически стартует scrub всего пула; нужна feature `raidz_expansion`. Учтите нюанс: старые блоки сохраняют прежнее соотношение данных и чётности, поэтому реальная прибавка полезного объёма сразу после расширения окажется скромнее расчётной. Выравнивается перезаписью данных — через `zfs send | zfs receive` или командой `zfs rewrite`.

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

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

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

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

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

Источники

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