Зачем ZFS для хранения инструментов и артефактов
ZFS закрывает три задачи хранилища артефактов одним набором инструментов: датасет изолирует окружение или проект, снапшот фиксирует версию и позволяет откатиться за секунды, компрессия убирает лишние байты без участия администратора. Дедупликация в этом списке стоит последней: она экономит место только там, где блоки действительно повторяются, и требует оперативной памяти под таблицу DDT.
Рабочая схема для большинства команд выглядит так: пул на 4-8 дисках, внутри него pool/artifacts/env/project/version для сборок и pool/tools/tool/version для утилит. Квота в 200 ГБ не даст кэшу CI занять весь пул, снапшот с именем релиза зафиксирует набор артефактов, а zstd-3 сжимает бинарники и текстовые манифесты в 2-3 раза. Для пула на 10 ТБ этого набора хватает в девяти случаях из десяти; дедупликация выигрывает только при десятках почти одинаковых виртуальных машин или контейнеров.
Ограничение, о котором стоит помнить сразу: ZFS не заменяет бэкап. Снапшоты живут внутри пула, и при потере дисков они исчезнут вместе с данными. Копию на другой сервер делают через zfs send, об этом ниже есть отдельный пример.
Какие задачи решает ZFS в хранилище артефактов
- Кэш сборок CI/CD. Каталоги .m2, node_modules, кэш pip и NuGet, промежуточные объектные файлы. Объём растёт до сотен гигабайт, а ценность у файлов разная, поэтому квота и снапшоты важнее скорости записи.
- Внутренний репозиторий пакетов. jar, deb, rpm, whl, npm-тарболлы, oci-образы в виде tar-архивов. Здесь много похожих файлов, но каждый уникален по содержимому, и компрессия даёт больше, чем дедупликация.
- Склад версий инструментов. Terraform, kubectl, helm, ansible-коллекции, установочные ISO. Каждая версия лежит отдельно, старые нужны для воспроизводимости сборок полугодовой давности.
- Релизные срезы. Набор артефактов, который уходит заказчику или в продакшен. Такой датасет переводят в режим только для чтения и хранят годами.
- Изоляция окружений. dev, stage и prod разводят по разным датасетам, чтобы эксперимент в dev не заблокировал запись в prod и не съел место у stage.
Датасет удобен тем, что у него собственные свойства: квота, компрессия, политика снапшотов, точка монтирования. Один пул обслуживает и быстрый NVMe-кэш для сборок, и медленный HDD-архив релизов, при этом набор команд остаётся одинаковым.
Ключевые возможности ZFS: датасеты, снапшоты, квоты, компрессия, дедупликация
Датасет похож на отдельную файловую систему внутри пула: своя точка монтирования, свои лимиты, свои снапшоты. Создание датасета не требует выделения места и выполняется мгновенно, а свойства наследуются от родителя, поэтому общие параметры задают один раз на верхнем уровне.
Снапшот сохраняет состояние датасета на момент создания и не копирует данные: место расходуется только на блоки, которые изменились после снимка. Квоты и резервации ограничивают рост. Компрессия работает на уровне блоков и прозрачна для приложений. Дедупликация убирает повторяющиеся блоки, но платит за это памятью: таблица DDT должна помещаться в ARC, иначе пул начинает читать её с диска на каждой записи.
Базовый уровень возможностей и типовые ошибки проектирования разобраны в материале о ZFS в системах хранения: пулы, датасеты, контрольные суммы, scrubs и требования к памяти. Ниже разберём, как эти механизмы работают именно на складе артефактов.
Проектирование датасетов для инструментов и артефактов
Разбиение пула определяет, насколько удобно будет удалять старые версии, ограничивать кэш и считать занятое место. Правило простое: отдельный датасет нужен там, где понадобится независимая квота, независимый снапшот или независимое удаление целиком.
Схема разбиения: окружения, проекты, версии
Рабочая иерархия для склада артефактов:
- tank/artifacts - корень для сборок и релизов.
- tank/artifacts/dev, tank/artifacts/stage, tank/artifacts/prod - окружения с разными квотами и политикой снапшотов.
- tank/artifacts/prod/billing - проект внутри окружения.
- tank/tools/terraform, tank/tools/kubectl - склад утилит по имени инструмента.
- tank/tools/terraform/1.9.5 - конкретная версия там, где нужна своя политика хранения.
Версии не обязательно превращать в датасеты: если политика хранения у них одинаковая, хватит подкаталогов внутри датасета проекта, а версии фиксировать снапшотами. Отдельный датасет на версию оправдан, когда старую версию нужно удалить одной командой, передать на другой сервер или отдать только для чтения отдельной команде.
Переусложнять вредно. Двести датасетов на пуле администрировать тяжело: zfs list выдаёт длинную простыню, а свойства приходится проверять вручную. Десятки датасетов на крупное хранилище артефактов - норма, сотни - повод пересмотреть схему в пользу подкаталогов.
Квоты и резервации: управление пространством
Quota ограничивает суммарный объём датасета вместе с его снапшотами и дочерними датасетами. Refquota считает только сам датасет, без снапшотов и потомков. Reservation резервирует место в пуле, refreservation - только под сам датасет. Резервация уменьшает свободное место, доступное остальным: если зарезервировать 2 ТБ из 8 ТБ, для других датасетов останется 6 ТБ.
zfs set quota=200G tank/artifacts/dev zfs set refquota=500G tank/artifacts/prod zfs set reservation=1T tank/tools tank/artifacts/dev quota 200G - zfs list -o name,used,avail,quota,refquota tank/artifacts
Для кэша сборок удобна quota: она режет и сам кэш, и его снапшоты, поэтому ветка CI не разрастётся незаметно. Для продакшен-артефактов чаще берут refquota, чтобы снапшоты релизов не блокировали запись новых файлов. При исчерпании лимита запись падает с ошибкой "disk quota exceeded" (EDQUOT), и это сразу видно в логах job-а: сначала падает CI, потом администратор узнаёт о проблеме.
Планируя лимиты, полезно посчитать чистую ёмкость пула с учётом RAID-Z и служебных расходов, иначе квота в 200 ГБ может не поместиться физически. Формулы и примеры расчёта собраны в руководстве по планированию объёма хранилища.
Снапшоты для версионирования и откатов артефактов
Снапшот занимает секунды на создание независимо от объёма датасета и не требует останавливать сервисы. Для склада артефактов это дешёвый способ зафиксировать состояние: релизный набор, состояние кэша перед крупным обновлением, точку перед чисткой старых версий.
Создание и управление снапшотами
zfs snapshot tank/artifacts/prod@release-2026-09-11 zfs snapshot -r tank/artifacts@daily-2026-09-11 zfs list -t snapshot -o name,used,refer,creation tank/artifacts/prod zfs destroy tank/artifacts/prod@release-2026-08-01
Имя после символа @ несёт смысл: дату, номер версии, идентификатор сборки. Флаг -r создаёт рекурсивный снимок всех дочерних датасетов, и это удобно для согласованного среза окружения. Столбец used показывает, сколько места занимают изменения относительно снимка, refer - сколько данных в датасете на момент снимка.
Снапшот можно защитить от удаления командой zfs hold с произвольной меткой: полезно для релизных срезов, которые не должен снести скрипт автоочистки. Посмотреть, что изменилось между снимком и текущим состоянием, позволяет zfs diff.
Откат и клонирование для тестирования
Rollback возвращает датасет к состоянию снапшота и без флага -r удаляет все снимки новее выбранного. Это самая частая причина потери данных на ровном месте: перед откатом создайте страховочный снимок, а для точечного восстановления файлов используйте скрытый каталог .zfs/snapshot внутри точки монтирования, оттуда файл копируется обычным cp без всякого отката.
zfs snapshot tank/tools/terraform@before-rollback zfs rollback tank/tools/terraform@1.9.5 zfs clone tank/tools/terraform@1.9.5 tank/test/terraform-1.9.5
Клон - записываемая копия снапшота. Блоки общие, поэтому клон не занимает места, пока в него не начали писать. Клон удобен для проверки новой версии инструмента: тестовая копия живёт рядом, оригинал не трогается. Один нюанс: клон зависит от снапшота, и удалить снимок, пока клон существует, не получится без zfs promote, которым зависимости переворачиваются.
Автоматические снимки ставят через sanoid, syncoid, zfs-auto-snapshot или обычный cron. Для офлайн-копии снапшот уходит на другой сервер потоком:
zfs send -i tank/artifacts/prod@2026-09-01 tank/artifacts/prod@2026-09-11 | ssh backup01 zfs receive -F backup/artifacts/prod
Инкрементальная передача шлёт только изменившиеся блоки, поэтому ежедневная репликация десятитерабайтного склада занимает минуты. Проверять восстановление нужно регулярно: команда zfs receive на тестовом хосте дешевле, чем выяснить неработоспособность копии в момент аварии.
Компрессия в ZFS: выбор алгоритма для артефактов
Начиная с OpenZFS 0.8 алгоритм lz4 включён по умолчанию на всех новых пулах, а с OpenZFS 2.0 доступен zstd с уровнями от 1 до 19. Для склада артефактов компрессия даёт заметную экономию почти всегда, потому что бинарники, JSON-манифесты, логи и текстовые индексы сжимаются хорошо.
Сравнение lz4, zstd и gzip
| Алгоритм | Скорость | Типичное сжатие | Нагрузка на CPU | Когда выбирать |
|---|---|---|---|---|
| lz4 | Очень высокая | 1.5-2.2x | Низкая | Активные данные, базы, ВМ, значения по умолчанию |
| zstd-1..3 | Высокая | 2-3x | Средняя | Артефакты, кэш сборок, репозитории пакетов |
| zstd-9..19 | Низкая | 2.5-4x | Высокая | Холодные релизные срезы, которые пишутся один раз |
| gzip-1..9 | Низкая | 2.5-3.5x | Высокая | Совместимость со старыми пулами |
| off | Максимальная | 1.0x | Нет | Данные, сжатые заранее: zip, jpg, mp4, tar.gz |
Высокий уровень zstd не всегда оправдан. Запись с zstd-19 упирается в процессор и на одном ядре обрабатывает десятки мегабайт в секунду, что заметно замедляет заливку крупного релиза. Выигрыш по сравнению с zstd-3 обычно составляет 10-30% объёма, и он того стоит только для архивных датасетов.
Практические рекомендации по настройке
zfs set compression=zstd-3 tank/artifacts zfs set compression=zstd-19 tank/artifacts/prod/archive zfs get compressratio tank/artifacts zfs get compressratio,used,logicalused tank/artifacts/prod
Смена алгоритма влияет только на новые записи: старые блоки остаются сжатыми прежним способом, и метрика compressratio меняется постепенно. Если датасет уже заполнен, помогает перезапись данных (копирование в новый датасет или, для больших объёмов, отправка и приём снапшота).
Значения, с которых стоит начать: compression=zstd-3 на датасетах артефактов и кэша, atime=off для экономии мелких записей, xattr=sa для быстрых расширенных атрибутов, recordsize=1M для датасетов с крупными образами и tar-архивами, recordsize=16K или 128K для каталогов с миллионами мелких файлов. На датасете с библиотеками переключение с lz4 на zstd-3 обычно поднимает compressratio с 1.2x до 2.2-2.5x, а скорость сборки не меняется, потому что на чтении распаковка упирается в память, а не в диск.
Компрессия бесполезна там, где данные сжаты заранее. Датасет с ISO-образами, mp4-записями и .tar.gz даст compressratio около 1.0x. Проверить эффект можно командой zfs get compressratio до и после смены алгоритма: если прирост ниже 1.05x, тратить процессорное время незачем.
Дедупликация в ZFS: когда она оправдана
Дедупликация сравнивает хеш каждого блока с таблицей уже записанных блоков и при совпадении не пишет данные повторно, а увеличивает счётчик ссылок. Экономия возможна только при реальном повторении блоков: у разнородных бинарников разных версий совпадений почти нет.
Как работает дедупликация и что такое DDT
Таблица DDT (Deduplication Table) хранит для каждого уникального блока контрольную сумму и число ссылок. На диск блок записывается один раз, а записи с тем же содержимым указывают на него. Таблица живёт в оперативной памяти (в ARC) и на диске; при нехватке памяти ZFS читает её с носителя, и каждая операция записи превращается в дополнительное чтение случайных блоков.
Оценить выгоду до включения позволяет симуляция на существующих данных:
zdb -S tank zfs set dedup=on tank/artifacts/similar zpool status -D tank zfs get dedupratio tank/artifacts/similar
Команда zdb -S показывает расчётный коэффициент без изменения пула, а zpool status -D выводит статистику по блокам и размер DDT. Коэффициент ниже 1.3x означает, что место вы почти не сэкономите, а память потратите.
Требования к RAM и производительности
Каждая запись в DDT занимает около 300 байт, поэтому расход памяти зависит от числа блоков, то есть от размера блока записи и объёма данных. Практическое правило для блоков 4-8 КБ: 1-2 ГБ оперативной памяти на каждый терабайт дедуплицированных данных при коэффициенте 1.5-2x. Для 10 ТБ это 10-20 ГБ только под таблицу, поверх памяти для ARC и приложений. При блоках 128 КБ записей в таблице в разы меньше, но и совпадений между крупными блоками почти не бывает.
Если памяти не хватает, начинается свопинг и деградация по всем операциям ввода-вывода, а не только по записи. Пул из 20 ТБ, на котором включили dedup=on без 32 ГБ RAM, легко теряет в скорости в десять раз по сравнению с тем же пулом на компрессии. Как дедупликация влияет на IOPS и задержки для разных типов данных, разобрано с тестами в статье про дедупликацию, IOPS и расход памяти: для логов dedup не нужен вовсе, а для десятков однотипных ВМ он окупается, если DDT целиком помещается в ARC.
Режим dedup=verify дополнительно проверяет совпадение по содержимому блока, а не только по хешу. Это надёжнее и ещё дороже по процессору, поэтому для больших пулов его включают редко. В OpenZFS 2.3 появился режим fast dedup: расход памяти снижен, метаданные DDT пишутся через журнал, а таблицу можно вынести на special vdev. Если версия ZFS старше 2.0, рассчитывайте на классическое поведение таблицы.
Когда дедупликация не нужна: альтернативы
Для склада артефактов чаще достаточно других механизмов:
- Компрессия. Работает всегда, не требует памяти под таблицу и сжимает разнородные данные лучше, чем дедупликация.
- Снапшоты и клоны. Дубли библиотек между версиями сборки не копируются повторно: блоки общие и у снапшота, и у клона.
- Один экземпляр вместо трёх копий. Если три окружения используют одну и ту же библиотеку, разумнее положить её в общий датасет и смонтировать туда, где нужно.
- Дедупликация на уровне приложения. Репозиторий пакетов или система управления артефактами умеет не хранить одинаковые файлы дважды, и это дешевле, чем таблица DDT.
Дедупликация оправдана там, где повторяются целые блоки: десятки клонов одной виртуальной машины, слои контейнеров, копии наборов библиотек одного дистрибутива между средами. Для разнородных релизов и кэшей сборок выгоднее компрессия вместе с клонами.
Практический сценарий: настройка ZFS для хранилища артефактов
Пример рассчитан на сервер с двумя NVMe-дисками под пул и OpenZFS 2.x в Linux или TrueNAS SCALE. Команды те же в FreeBSD, отличается только путь к модулю ядра. Версия ZFS важна для функций: zstd доступен с OpenZFS 2.0, project quota с 0.8, fast dedup с 2.3.
Создание пула и датасетов
zpool create -o ashift=12 -O compression=zstd-3 -O atime=off tank mirror /dev/nvme0n1 /dev/nvme1n1 zpool status tank zfs create tank/artifacts zfs create tank/artifacts/dev zfs create tank/artifacts/prod zfs create tank/tools zfs create tank/tools/terraform
Тип vdev выбирают по нагрузке: mirror даёт высокий IOPS и переживает отказ одного диска из пары, RAID-Z2 на шести дисках даёт около 67% полезной ёмкости и терпит два отказа. Для склада артефактов с миллионами мелких файлов предпочтителен mirror или несколько mirror-групп, поскольку RAID-Z плохо переносит случайное чтение мелких блоков. Разбор конфигураций с расчётом ёмкости и IOPS есть в статье об архитектуре пулов и vdev.
Флаг -O задаёт свойства, которые наследуют все дочерние датасеты, поэтому компрессия и atime настраиваются один раз. ashift=12 соответствует сектору 4 КБ и обязателен почти для всех современных дисков: при ошибочном ashift=9 пул теряет в производительности на порядок.
Настройка свойств: квоты, компрессия, снапшоты
zfs set quota=200G tank/artifacts/dev zfs set refquota=500G tank/artifacts/prod zfs set compression=zstd-3 tank/artifacts zfs set recordsize=1M tank/artifacts/prod/archive zfs set userquota=50G@ci tank/artifacts/dev zfs snapshot tank/artifacts/prod@baseline-2026-09-11 zfs list -o name,used,avail,quota,compressratio
Порядок действий практичнее начинать с квот, иначе первая же массовая сборка займёт весь пул. Дальше компрессия, затем снапшот-базовая точка, от которой удобно считать изменения. Если ZFS настраивается впервые, пошаговый разбор создания пула, включения сжатия и автоснапшотов есть в руководстве по настройке ZFS с нуля.
Для тестового стенда, где нужно проверить схему датасетов и квоты на реальной нагрузке, подойдёт арендованный сервер с несколькими дисками: Timeweb Cloud выдаёт облачные серверы и блочные тома, на которых удобно прогнать ту же последовательность команд перед покупкой железа.
Автоматизация и мониторинг
Ежедневные снапшоты и репликацию проще поручить sanoid и syncoid: конфигурация описывает политику хранения по часам, дням и месяцам, а syncoid автоматически передаёт инкременты на второй хост. Минимальный вариант на cron выглядит так:
0 2 * * * /sbin/zfs snapshot -r tank/artifacts@daily-$(date +%Y-%m-%d) 15 3 * * * /usr/bin/find /tank/artifacts -name '*.tmp' -mtime +7 -delete 0 4 * * 0 /sbin/zpool scrub tank 0 6 * * * /sbin/zfs send -i tank/artifacts@2026-09-01 tank/artifacts@2026-09-11 | ssh backup01 zfs receive -F backup/artifacts
Контроль состояния складывается из четырёх команд: zpool status показывает ошибки и результат scrub, zpool iostat -v 5 даёт нагрузку по дискам, zfs list -o space показывает расход места и снапшотами, arc_summary (или arcstat) помогает понять, хватает ли памяти под метаданные и DDT. Scrub запускают не реже раза в месяц: он находит повреждённые блоки и восстанавливает их из избыточности, пока она доступна.
Подводные камни и типичные ошибки
Большинство проблем на ZFS-хранилище артефактов связано не с дисками, а с настройками, которые включили один раз и забыли. Ниже те, что встречаются чаще всего.
Дедупликация на больших объёмах: риски
Таблица DDT растёт вместе с числом уникальных блоков и уезжает в своп при нехватке памяти. При 50 ТБ разнородных данных таблица легко занимает 50-100 ГБ оперативной памяти, а попытка уложиться в 32 ГБ приводит к постоянной подкачке и падению производительности в разы. Симптомы: загрузка пула при импорте измеряется десятками минут, запись упирается в дисковые операции чтения DDT, zpool status -D показывает большой размер таблицы, нагрузка на swap растёт.
Порядок безопасной проверки: сначала zdb -S на живом пуле, затем тестовый датасет на 1-2 ТБ с dedup=on, замер IOPS и задержек до и после, и только потом решение для всего склада артефактов. Отключение работает не так, как ожидают: команда zfs set dedup=off останавливает дедупликацию только для новых данных, уже дедуплицированные блоки остаются общими, и место не освобождается, пока старые записи не будут перезаписаны или удалены. Разбор частых ошибок проектирования хранилищ, включая дедупликацию, приведён в материале о возможностях и ограничениях ZFS.
Ошибки в снапшотах и откатах
- Rollback без страховки. Команда zfs rollback удаляет снапшоты новее целевого. Откат на состояние недельной давности сносит релизные снимки, сделанные вчера. Перед откатом создайте снимок с меткой before-rollback.
- Снапшоты вместо бэкапа. Снимок живёт в том же пуле и исчезнет при отказе железа. Копии делают через zfs send на другой сервер или в другую систему хранения.
- Растущие снапшоты. Снимок занимает место по мере изменения данных. Датасет с активным кэшем сборок и годовой историей снимков может занять вдвое больше, чем кажется по du.
- Клоны без учёта зависимостей. Клон держит снапшот удаляемым только после promote. Брошенные тестовые клоны тихо занимают место годами.
- Забытый scrub и отсутствие мониторинга. Ошибки чтения обнаруживаются только при scrub: без него повреждение выяснится в момент восстановления из бэкапа.
- Отключённая компрессия. Иногда её выключают ради "чистой" производительности, не замечая, что выигрыш в скорости незаметен, а объём вырастает вдвое.
Итоги и рекомендации
Для склада инструментов и артефактов ZFS даёт три рабочих механизма и один рискованный. Датасеты изолируют окружения и проекты, снапшоты фиксируют версии и ускоряют откат, компрессия zstd-3 экономит место почти без нагрузки на CPU. Дедупликация нужна там, где блоки повторяются буквально: десятки однотипных ВМ, слои контейнеров, копии наборов библиотек. Для разнородных релизов она обходится дороже, чем экономит.
Чек-лист перед запуском:
- Посчитать чистую ёмкость пула с учётом типа vdev и служебных расходов.
- Создать пул с ashift=12, задать наследуемые compression=zstd-3 и atime=off.
- Разложить артефакты по датасетам: окружение, проект, отдельный датасет для инструментов.
- Поставить квоты на кэш сборок и refquota на продакшен-артефакты.
- Настроить ежедневные снапшоты через sanoid или cron и репликацию через zfs send.
- Проверить compressratio через сутки после включения zstd.
- Прогнать zdb -S перед включением дедупликации и замерить IOPS на тестовом датасете.
- Поставить scrub раз в месяц и мониторить zpool status, zfs list -o space и arcstat.
Начните с одного пула и трёх датасетов на небольшом объёме: схему именования, квоты и расписание снапшотов проще поменять на тестовых 200 ГБ, чем на рабочем складе артефактов, от которого зависят сборки всей команды.