Учёт хранения в ZFS: квоты, резервирование и отчётность без иллюзий | AdminWiki

Учёт хранения в ZFS: квоты, резервирование и отчётность без иллюзий

13 сентября 2026 15 мин. чтения
Содержание статьи

Пул на 14,5 ТиБ показывает в zpool list заполнение 17% (ALLOC 2,40 ТиБ), а сумма USED по всем датасетам в zfs list даёт около 1,8 ТиБ. Обе команды правы. zpool list отвечает на вопрос, сколько места на дисках занято блоками, включая чётность RAIDZ, метаданные и выравнивание. zfs list отвечает на другой вопрос: сколько данных и снапшотов числится за конкретным датасетом с учётом его квот и резервирований.

Расхождение объясняется четырьмя причинами. Избыточность и метаданные существуют только на уровне пула. Компрессия уменьшает физический объём, но не логический. Снапшоты занимают блоки в пуле, хотя по датасету цифры могут выглядеть скромно. Квоты и резервирования меняют доступное место (AVAIL), не меняя занятое. Отсюда рабочее правило: ёмкость пула планируйте по CAP и FREE из zpool list, а лимиты датасетов читайте в zfs list по AVAIL, USED, USEDSNAP и refquota.

Материал построен вокруг колонок обеих команд, четырёх свойств квот и резервирований, поведения снапшотов, готовых скриптов отчётности и ошибок, из-за которых администратор узнаёт о переполнении из ошибок ввода-вывода приложений. Поведение проверялось на OpenZFS 2.x в Linux и TrueNAS SCALE, в других сборках набор метрик может отличаться. Базовые операции собраны в шпаргалке по администрированию ZFS.

Почему цифры в zfs list и zpool list не совпадают: базовая модель учёта

ZFS ведёт два независимых учёта места. Первый работает с блоками: аллокатор отмечает занятые и свободные диапазоны в spacemap каждого vdev, учитывает чётность RAIDZ и метаданные. Второй работает с объектами: датасетами, томами zvol, снапшотами и их свойствами. zpool list читает первый уровень, zfs list читает второй. Числа не обязаны совпадать, и расхождение не указывает на сбой.

Что именно показывает zpool list: физика пула

Без флагов команда выводит десять колонок. Значение каждой закрывает половину типовых вопросов про «пропавшее» место.

КолонкаЧто означает
NAMEИмя пула, а в режиме -v ещё и имена vdev
SIZEСырой суммарный объём дисков, включая место под чётность
ALLOCФизически занятое место: данные, чётность, метаданные, выравнивание блоков, удержанные снапшоты и закреплённые резервы
FREESIZE минус ALLOC, без вычета slop-резерва аллокатора
CKPOINTМесто, удерживаемое чекпойнтом zpool checkpoint (OpenZFS 2.0 и новее)
EXPANDSZПрирост, который даст расширение vdev
FRAGФрагментация свободных диапазонов в процентах
CAPALLOC в процентах от SIZE
DEDUPКоэффициент дедупликации или off
HEALTHONLINE, DEGRADED, FAULTED, OFFLINE, REMOVED, UNAVAIL

Пример для пула из четырёх дисков по 4 ТБ в RAIDZ1:

NAME    SIZE  ALLOC  FREE  CKPOINT  EXPANDSZ  FRAG  CAP  DEDUP  HEALTH
tank    14.5T  2.40T  12.1T  -        -         19%   17%  1.00x  ONLINE

SIZE 14,5 ТиБ это сумма четырёх дисков до вычета чётности. ALLOC 2,40 ТиБ включает полезные данные на 1,8 ТиБ, чётность RAIDZ1, метаданные (spacemap, dnode, uberblock), выравнивание блоков и закреплённые резервирования. Если сложить FREE по всем датасетам и получить значение, которое не бьётся с пулом, проверьте, сколько места держат refreservation: закреплённое место вычитается из свободного на уровне пула.

FREE 12,1 ТиБ тоже не равен объёму, который можно записать. Аллокатор держит slop-резерв размером 1/32 объёма пула, то есть примерно 3,1%. Новые блоки перестают размещаться около 96,9% CAP, и запросы падают с ENOSPC, хотя FREE показывает сотни гибибайт. Для пула 14,5 ТиБ это около 450 ГиБ «свободного» места, которое данным не отдадут.

Остальные колонки читаются так. FRAG 19% означает фрагментацию свободных диапазонов: высокое значение тормозит последовательную запись, но ёмкости не отнимает. DEDUP показывает коэффициент, когда дедупликация включена. CKPOINT заполняется, если в пуле есть чекпойнт: блоки, освобождённые после его создания, остаются занятыми, пока не выполнен zpool checkpoint -d. Такие «пропавшие» гигабайты администраторы ищут регулярно.

Для скриптов используйте zpool list -H -o name,capacity, для точных байтов флаг -p, для разбивки по vdev флаг -v. Вывод zpool list -v показывает отдельные диски, раскладку чётности и состояние кешей L2ARC и SLOG.

Что показывает zfs list: логика датасетов

По умолчанию выводятся NAME, USED, AVAIL, REFER и MOUNTPOINT. Полная раскладка доступна через zfs list -o space, и именно она нужна для отчётности.

КолонкаЧто означает
USEDDSСобственные данные датасета, место после компрессии
USEDSNAPМесто, удержанное снапшотами датасета
USEDREFRESERVМесто, закреплённое свойством refreservation
USEDCHILDСуммарное место дочерних датасетов
USEDСумма всех четырёх частей плюс место, удержанное свойством reservation
AVAILСколько датасет ещё может записать с учётом квот и резервов
REFERЛогический объём данных, доступных датасету
logicalusedЛогический объём до компрессии

USED учитывает место после компрессии. Для датасета с 100 ГиБ текста и включённым lz4 с коэффициентом 2,5x значение USED будет около 40 ГиБ, а logicalused покажет исходные 100 ГиБ. Сумма USED по всем датасетам не равна ALLOC пула: ALLOC включает чётность и метаданные, а USED уменьшен компрессией.

AVAIL сообщает, сколько места датасет может записать: минимум из свободного места пула, остатка quota и остатка refquota. Если квот нет, все датасеты показывают одинаковый AVAIL, равный свободному месту пула, и складывать эти значения бессмысленно.

Квоты и резервирование в ZFS: как они управляют ёмкостью

Четыре свойства образуют две пары. Квоты ограничивают максимум, резервирования гарантируют минимум. В каждой паре есть вариант «на датасет с потомками» и вариант «только на сам датасет».

СвойствоНа что действуетУчитывает снапшотыГарантирует место
quotaДатасет и все потомкиДаНет
refquotaТолько сам датасетНетНет
reservationДатасет и все потомкиНетДа
refreservationТолько сам датасетНетДа

Quota vs refquota: когда что использовать

quota ограничивает USED датасета вместе с потомками и снапшотами. refquota ограничивает только собственные данные датасета (USEDDS) и не смотрит на снапшоты и дочерние датасеты.

zfs set quota=100G tank/data
zfs set refquota=60G tank/data
zfs get quota,refquota,used,avail tank/data

Выбор зависит от задачи. Проекту с вложенными датасетами (tank/project/db, tank/project/logs, tank/project/cache) нужен quota, потому что лимит должен покрывать всё дерево. Для домашних каталогов удобнее refquota: при quota=50G и снапшотах на 20 ГиБ пользователь запишет только 30 ГиБ, при refquota=50G ему доступны все 50 ГиБ, а снапшоты живут отдельно.

Отдельный сценарий - квоты на пользователей и группы внутри одного датасета: zfs set userquota@ivan=20G tank/home, zfs userspace tank/home, zfs groupspace tank/home. Ограничение действует на данные конкретного пользователя и не требует создавать датасет под каждого.

Ловушка refquota в том, что снапшоты она не ограничивает. Датасет с refquota=60G способен удерживать ещё сотни гибибайт в снапшотах и заполнить весь пул, при этом zfs list по датасету покажет скромные цифры. Контролируйте USEDSNAP, а не только квоту.

Reservation vs refreservation: гарантии места

reservation гарантирует датасету и его потомкам минимум места в пуле. refreservation делает то же для одного датасета. Закреплённое место вычитается из свободного пространства пула, поэтому другие датасеты его не получат.

zfs set reservation=20G tank/vm
zfs set refreservation=10G tank/data/zvol
zfs get -r reservation,refreservation tank

Пример с томом: zfs create -V 100G tank/vm/disk1 создаёт zvol, для которого refreservation по умолчанию равен volsize, то есть 100 ГиБ закреплены физически. Флаг -s при создании (zfs create -s -V 100G) оставляет том разреженным, а refreservation=none можно выставить и после создания.

Переподписка (overcommit) обходится дороже всего. Пул 1 ТиБ, два датасета с reservation=600G: место первого резерва вычитается из свободного, и на второй пул отвечает ошибкой out of space. Если резервы всё-таки выставлены поверх свободного места, датасеты без гарантий начнут получать ENOSPC задолго до заполнения по данным. Как считать запас под снапшоты и резервы, разобрано в руководстве по расчёту объёма хранилища.

Резервирование не покрывает снапшоты. Блоки, которые удерживает снапшот, занимают место сверх резерва, и при полном пуле запись в датасет с гарантией всё равно упадёт.

Снапшоты и учёт места: когда они действительно занимают объём

ZFS использует copy-on-write. В момент создания снапшот не занимает ничего: он ссылается на те же блоки, что и активный датасет. Место появляется, когда блок перезаписывается или удаляется: ZFS пишет новую копию, а старая остаётся у снапшота. Удалили файл на 10 ГиБ, который существовал на момент снапшота, и USEDSNAP вырос примерно на 10 ГиБ, а место в пуле не освободилось.

Как читать zfs list -t snapshot и оценивать вклад снапшотов

zfs list -t snapshot -o name,used,refer,creation tank/data
zfs list -t snapshot -o name,used -s used -r tank | head -n 20

USED снапшота это физический объём, который освободится при его удалении, с учётом компрессии. REFER показывает объём данных, на которые снапшот ссылается, включая блоки, общие с активным датасетом. REFER может равняться 500 ГиБ при USED в 3 ГиБ: снапшот ссылается на большой объём, но удерживает только те блоки, которые активный датасет уже перезаписал.

Для датасета USEDSNAP равен вкладу всех его снапшотов, но складывать USED отдельных снапшотов с USEDDS нельзя: блоки бывают общими для нескольких снапшотов и активных данных, и сумма даст завышенную оценку. Ориентируйтесь на USEDSNAP родителя и на сравнение USED с logicalused.

Удаление: zfs destroy tank/data@snap. Команда не сработает, если на снапшоте стоит hold (zfs hold keep tank/data@snap, посмотреть - zfs holds tank/data) или если от него создан клон: сначала удаляется клон. Флаг -d помечает снапшот для отложенного удаления, и он исчезнет, когда снимут последний hold.

Автоматическое удаление снапшотов и политики хранения

Распространённые инструменты: sanoid с syncoid, zfs-auto-snapshot, zfs_autobackup. В TrueNAS SCALE расписания живут в Data Protection, раздел Periodic Snapshot Tasks, там же задаётся срок хранения. Рабочая политика для активного датасета выглядит так: каждые 15 минут хранить 4 часа, ежечасные сутки, ежедневные 7 дней, еженедельные 4 недели. Правило задаёт не только частоту, но и то, сколько места максимум удержат снапшоты.

Вариант без внешних пакетов, который оставляет семь последних снапшотов:

zfs list -H -t snapshot -o name -s creation tank/data | head -n -7 | while read -r snap; do zfs destroy "$snap"; done

Это заготовка: подставьте свои имена, сначала проверьте вывод zfs list -t snapshot -o name,used, учтите, что head -n -7 требует GNU coreutils. Снапшоты с hold или с клонами не удалятся, и скрипт должен писать такие случаи в лог, иначе удаление тихо не сработает.

Как правильно читать отчёты и не попасть в ловушку метрик

Компрессия и дедупликация: как они искажают отчётность

Компрессия меняет связь между USED и логическим объёмом. Датасет с 100 ГиБ данных и compressratio 2,5x занимает 40 ГиБ, и именно 40 ГиБ увидят скрипты отчётности. Планировать по USED опасно, когда состав данных меняется: архивы, медиафайлы и зашифрованные контейнеры сжимаются в 1,0-1,1x, и тот же объём займёт втрое больше места. Смотрите zfs get compressratio,logicalused,used tank/data и проверяйте, на каких именно данных получена экономия.

Дедупликация искажает учёт сильнее. Режим dedup=on держит таблицу DDT в памяти: ориентир 1-5 ГиБ ОЗУ на 1 ТиБ данных при recordsize 128K, точное значение зависит от размера блока и фактического коэффициента. Часть таблицы занимает место и на диске. Главный эффект для отчётности: USED датасета перестаёт означать, сколько места освободится при его удалении, потому что блоки общие с другими датасетами. Практика включения дедупликации, расчёт памяти и сравнение алгоритмов сжатия разобраны в материале про датасеты, снапшоты и дедупликацию.

Thin provisioning и overcommit: почему AVAIL обманчив

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

Классический сценарий с томом: zvol создан с флагом -s, refreservation=none, гостю отдано 500 ГиБ при пуле на 300 ГиБ свободного места. Гость пишет данные, AVAIL уменьшается, и на очередном гигабайте запись падает с ошибкой ввода-вывода, а файловая система внутри гостя уходит в ошибки. Для дисков виртуальных машин, баз данных и всего, где нужен предсказуемый результат, ставьте refreservation или держите CAP пула с запасом.

Ещё одна ловушка - df -h внутри ZFS. Утилита читает квоты и refquota и ничего не знает о снапшотах и общем заполнении пула. Картина «df показывает 20 ГиБ свободно, а запись падает с ENOSPC» объясняется именно этим: место съели снапшоты другого датасета или пул подошёл к порогу slop-резерва. Складывать AVAIL по датасетам тоже бессмысленно: без квот каждый показывает всё свободное место пула.

Правило отчётности из трёх строк: ёмкость пула отслеживайте по zpool list CAP и FREE, лимиты датасетов по zfs list с quota, refquota и AVAIL, снапшоты по USEDSNAP.

Автоматические отчёты и оповещения о нехватке места

Скрипт для проверки свободного места и квот

Скрипт ниже проверяет три вещи: заполнение пулов, остаток по квотам датасетов и долю снапшотов в занятом месте.

#!/bin/bash
set -uo pipefail
POOL_THRESH=80
QUOTA_PCT=10
MAILTO="admin@example.com"

zpool list -H -o name,capacity | while read -r pool cap; do
  used=${cap%\%}
  if [ "$used" -ge "$POOL_THRESH" ]; then
    echo "POOL ${pool}: ${cap} занято" | mail -s "ZFS pool capacity" "$MAILTO"
  fi
done

zfs list -H -p -o name,avail,quota -t filesystem,volume | while read -r ds avail quota; do
  case "$quota" in
    none|-|0) continue ;;
  esac
  limit=$(( quota * QUOTA_PCT / 100 ))
  if [ "$avail" -lt "$limit" ]; then
    echo "DATASET ${ds}: свободно ${avail} Б, квота ${quota} Б" | mail -s "ZFS dataset quota" "$MAILTO"
  fi
done

zfs list -H -p -o name,used,usedbysnapshots -t filesystem,volume | while read -r ds used snaps; do
  if [ "$used" -gt 0 ] && [ $(( snaps * 100 / used )) -ge 20 ]; then
    echo "DATASET ${ds}: снапшоты держат ${snaps} Б из ${used} Б" | mail -s "ZFS snapshots" "$MAILTO"
  fi
done

Разбор строк: zpool list -H -o name,capacity отдаёт пары вида tank и 80%, параметр -H убирает заголовок, суффикс процента снимается через подстановку ${cap%\%}. Флаг -p в zfs list даёт точные байты, поэтому сравнение идёт без разбора суффиксов K, M, G, T. Проверка снапшотов сигналит, когда они держат больше 20% занятого места датасета. В продакшене добавьте логирование в journald и вызов вебхука вместо mail, потому что письма от cron легко теряются.

Расписание: 0 * * * * /usr/local/bin/zfs_check.sh в cron или systemd timer с OnCalendar=hourly. Порог 80% подходит пулам, где снапшоты удаляются регулярно; если автоснапшоты хранятся месяцами, ставьте 70%.

Интеграция с системами мониторинга

ZED (ZFS Event Daemon) следит за событиями пула: ошибки ввода-вывода, деградация vdev, проблемы с кешами. Настройки лежат в /etc/zfs/zed.d/zed.rc, где задаются ZED_EMAIL_ADDR, ZED_NOTIFY_VERBOSE и интервал уведомлений. События видны в zpool events -v, состояние в zpool status -x. О заполнении ёмкости ZED сам не сообщит, для этого нужен отдельный скрипт или экспортёр.

Метрики ёмкости удобно снимать отдельным экспортёром (zfs_exporter), который отдаёт zfs_pool_allocated_bytes, zfs_pool_size_bytes, zfs_dataset_used_bytes, zfs_dataset_available_bytes и zfs_dataset_used_by_snapshots_bytes. node_exporter с флагом --collector.zfs читает kstat-файлы ARC, zfetch и ZIL и полезен для производительности, но ёмкость пулов через него не выводится, а часть файлов требует прав root. Имена метрик зависят от версии экспортёра, поэтому сверяйтесь с выводом /metrics. Готовые дашборды, правила алертов и разбор метрик ARC собраны в руководстве по мониторингу ZFS.

Правило Prometheus на заполнение пула:

- alert: ZFSPoolCapHigh
expr: zfs_pool_allocated_bytes / zfs_pool_size_bytes > 0.85
for: 30m
labels:
  severity: warning
annotations:
  summary: "Пул {{ $labels.pool }} заполнен более чем на 85%"

В TrueNAS SCALE похожую роль играют встроенные алерты: System Settings, раздел Alert Settings, где включается предупреждение о ёмкости пулов, плюс графики Reporting. Порог настраивается, оповещение приходит на почту. Заполнение датасетов с квотами система тоже показывает, а USEDSNAP придётся смотреть вручную или скриптом.

Стек из Prometheus, Grafana и экспортёров удобно держать на отдельном хосте, чтобы метрики не пропадали при перезагрузке основной системы хранения, под такие задачи подходит облачный сервер под мониторинг. Отслеживать нужно три уровня: пул, датасеты с квотами и рост снапшотов.

Практические примеры конфигураций и типовые ошибки

Пример: квоты для пользовательских данных

zfs create -o mountpoint=/home/user1 tank/home/user1
zfs set quota=50G tank/home/user1
zfs set refquota=40G tank/home/user1
zfs set compression=lz4 tank/home/user1
zfs get quota,refquota,used,avail tank/home/user1
zfs list -o name,used,avail,quota,refquota tank/home

refquota=40G ограничивает данные пользователя и защищает его от собственных снапшотов. quota=50G работает потолком для всего дерева, включая снапшоты и вложенные датасеты, и сработает раньше. Если снапшоты создаются по расписанию с хранением 7 дней, оставляйте между refquota и quota зазор 20-30% или считайте рост снапшотов вручную по USEDSNAP.

Пример: резервирование для виртуальных машин

zfs create -o volblocksize=16K -V 100G tank/vm/disk1
zfs get volsize,refreservation,used,avail tank/vm/disk1
zfs set refreservation=100G tank/vm/disk1

Том для виртуальной машины создаётся с закреплённым местом, и пул обязан держать эти 100 ГиБ свободными. Если диск нужен разреженным, используйте zfs create -s -V 100G: экономия места обменивается на риск ENOSPC в момент записи. Параметр volblocksize задаётся при создании и не меняется, для большинства гостей подходит 16K, для баз данных стоит посчитать под размер страниц и тип нагрузки.

Снапшот тома делается так же, как снапшот датасета (zfs snapshot tank/vm/disk1@before-update), но учёт места отличается: у zvol свой объём метаданных и своя цена изменённых блоков. Клон тома экономии в отчётах не даёт: он ссылается на блоки снапшота, и пока жив клон, снапшот не удалить.

Типовые ошибки

  1. Судить о свободном месте по сумме AVAIL или по df вместо CAP и FREE пула.
  2. Ставить quota там, где нужен refquota, и терять квоту из-за снапшотов.
  3. Раздавать reservation и refreservation без проверки суммы: переподписка приводит к ENOSPC у датасетов без гарантий.
  4. Хранить снапшоты месяцами при активной записи и не смотреть USEDSNAP.
  5. Включать dedup «на всякий случай» и получить нехватку ОЗУ и запутанные отчёты.
  6. Ждать, что удаление снапшота освободит место, когда на нём hold или от него есть клон.
  7. Рассчитывать на 100% ёмкости пула и забывать про slop-резерв 3,1% и место под метаданные.
  8. Не проверять volblocksize и recordsize до создания датасетов: у тома размер блока не изменить, у датасета новое значение действует только на новые записи.

Перед любой правкой квот и резервов снимайте картину целиком: zfs list -r -o space tank, zpool list -v, zfs get -r quota,refquota,reservation,refreservation tank. Отдельно помните про границы самой ZFS: требования к памяти, ошибки проектирования пулов и последствия отключённых checksums разобраны в обзоре возможностей и ограничений ZFS.

Чек-лист администратора ZFS по учёту хранения

  1. Снимите базу: zpool list -v и zfs list -r -o space tank. Запишите CAP, FREE и USEDSNAP по каждому датасету.
  2. Проверьте квоты: zfs get -r quota,refquota,reservation,refreservation tank. Убедитесь, что сумма резервов не превышает свободное место пула.
  3. Сравните USED и logicalused: zfs get -r logicalused,used,compressratio tank. Поймите, на что опирается планирование ёмкости.
  4. Посмотрите снапшоты: zfs list -t snapshot -o name,used,refer,creation -s used tank. Десяток крупнейших покажет, куда уходит место.
  5. Настройте автоматические снапшоты и срок хранения: sanoid, zfs-auto-snapshot или Periodic Snapshot Tasks в TrueNAS.
  6. Поставьте алерты на CAP пула (порог 70-80%), на остаток по квотам и на рост usedbysnapshots.
  7. Проверьте zpool events и zpool status -x: ёмкость не единственная причина отказов записи.
  8. Перечитайте отчётность через месяц: состав данных и коэффициент компрессии меняются, значит и прогноз ёмкости требует пересчёта.

Начните с одной команды: zfs list -r -o name,used,avail,usedsnap,quota,refquota tank > /var/log/zfs-audit-$(date +%F).txt. Через месяц этот файл покажет, растёт ли место за счёт данных или за счёт снапшотов, и даст ответ, где именно заканчивается ёмкость задолго до ошибок ENOSPC в приложениях.

Поделиться:
Сохранить гайд? В закладки браузера