Пул на 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 | Физически занятое место: данные, чётность, метаданные, выравнивание блоков, удержанные снапшоты и закреплённые резервы |
| FREE | SIZE минус ALLOC, без вычета slop-резерва аллокатора |
| CKPOINT | Место, удерживаемое чекпойнтом zpool checkpoint (OpenZFS 2.0 и новее) |
| EXPANDSZ | Прирост, который даст расширение vdev |
| FRAG | Фрагментация свободных диапазонов в процентах |
| CAP | ALLOC в процентах от SIZE |
| DEDUP | Коэффициент дедупликации или off |
| HEALTH | ONLINE, 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 свой объём метаданных и своя цена изменённых блоков. Клон тома экономии в отчётах не даёт: он ссылается на блоки снапшота, и пока жив клон, снапшот не удалить.
Типовые ошибки
- Судить о свободном месте по сумме AVAIL или по df вместо CAP и FREE пула.
- Ставить quota там, где нужен refquota, и терять квоту из-за снапшотов.
- Раздавать reservation и refreservation без проверки суммы: переподписка приводит к ENOSPC у датасетов без гарантий.
- Хранить снапшоты месяцами при активной записи и не смотреть USEDSNAP.
- Включать dedup «на всякий случай» и получить нехватку ОЗУ и запутанные отчёты.
- Ждать, что удаление снапшота освободит место, когда на нём hold или от него есть клон.
- Рассчитывать на 100% ёмкости пула и забывать про slop-резерв 3,1% и место под метаданные.
- Не проверять volblocksize и recordsize до создания датасетов: у тома размер блока не изменить, у датасета новое значение действует только на новые записи.
Перед любой правкой квот и резервов снимайте картину целиком: zfs list -r -o space tank, zpool list -v, zfs get -r quota,refquota,reservation,refreservation tank. Отдельно помните про границы самой ZFS: требования к памяти, ошибки проектирования пулов и последствия отключённых checksums разобраны в обзоре возможностей и ограничений ZFS.
Чек-лист администратора ZFS по учёту хранения
- Снимите базу: zpool list -v и zfs list -r -o space tank. Запишите CAP, FREE и USEDSNAP по каждому датасету.
- Проверьте квоты: zfs get -r quota,refquota,reservation,refreservation tank. Убедитесь, что сумма резервов не превышает свободное место пула.
- Сравните USED и logicalused: zfs get -r logicalused,used,compressratio tank. Поймите, на что опирается планирование ёмкости.
- Посмотрите снапшоты: zfs list -t snapshot -o name,used,refer,creation -s used tank. Десяток крупнейших покажет, куда уходит место.
- Настройте автоматические снапшоты и срок хранения: sanoid, zfs-auto-snapshot или Periodic Snapshot Tasks в TrueNAS.
- Поставьте алерты на CAP пула (порог 70-80%), на остаток по квотам и на рост usedbysnapshots.
- Проверьте zpool events и zpool status -x: ёмкость не единственная причина отказов записи.
- Перечитайте отчётность через месяц: состав данных и коэффициент компрессии меняются, значит и прогноз ёмкости требует пересчёта.
Начните с одной команды: zfs list -r -o name,used,avail,usedsnap,quota,refquota tank > /var/log/zfs-audit-$(date +%F).txt. Через месяц этот файл покажет, растёт ли место за счёт данных или за счёт снапшотов, и даст ответ, где именно заканчивается ёмкость задолго до ошибок ENOSPC в приложениях.