SSD Nodes Learn 🎉 VPS от $5.50/мес
Руководства Matt ConnorАвтор: Matt Connor · Обновлено 2026-08-13

ZFS на VPS: настройка ARC и потребление оперативной памяти

Узнайте, как ZFS использует ARC для кэширования и как ограничить потребление RAM на VPS с 2 или 4 ГБ памяти. Разбираем настройку zfs_arc_max для стабильной работы системы.

Что дает ZFS и чего она требует

ZFS в FreeBSD и Linux теперь представляет собой единую кодовую базу — OpenZFS, поэтому набор функций в обеих системах идентичен. Сервер под управлением ZFS получает контрольные суммы данных, моментальные снимки (snapshots), которые не занимают места до изменения данных, репликацию с помощью zfs send и сжатие, включаемое одной настройкой. ZFS требует оперативной памяти: ARC (адаптивный кэш замещения) по умолчанию занимает значительную часть RAM, а на VPS (виртуальном выделенном сервере) с 2 ГБ или 4 ГБ памяти этот объем критически важен для работы ваших приложений.

Данное руководство рассматривает ZFS с точки зрения арендованного VPS с одним или двумя виртуальными дисками, а не специализированного хранилища с сорока отсеками для накопителей. Внимание стоит уделить тем функциям, которые полезны в таких условиях. О тех аспектах, которые не подходят для подобных конфигураций, следует знать до создания пула.

OpenZFS в FreeBSD и Linux: одна кодовая база, два подхода к дистрибуции

В FreeBSD файловая система ZFS входит в состав базовой системы начиная с версии 7.0 в 2008 году, изначально в качестве экспериментальной функции. С момента выхода OpenZFS 2.0 в декабре 2020 года FreeBSD и Linux собираются из одного и того же дерева исходного кода, поэтому zfs и zpool работают одинаково в обеих ОС, а пул, созданный в одной системе, успешно импортируется в другой.

Причина, по которой в Linux ZFS является отдельным пакетом, а в FreeBSD — частью базовой системы, заключается в лицензировании. OpenZFS распространяется под лицензией CDDL (Common Development and Distribution License). Ядро Linux использует лицензию GPL (General Public License) версии 2. Проект ядра считает эти лицензии несовместимыми, поэтому код ZFS не включается в основную ветку Linux, и каждый дистрибутив самостоятельно решает, как его поставлять. У FreeBSD такого конфликта нет, поэтому ZFS просто присутствует в системе. Это вся практическая суть вопроса: разница заключается только в способе упаковки, и вам не нужно принимать чью-либо сторону.

SSD Nodes не предоставляет образы FreeBSD, поэтому на арендованном здесь сервере применимы инструкции для Linux из этого руководства. Если вы используете FreeBSD на другом хостинге, сервер на FreeBSD получит ZFS без необходимости сборки модулей или беспокойства о совместимости при обновлении ядра.

Установка ZFS и создание пула

В Ubuntu модуль поставляется в составе пакетов ядра, поэтому вам нужно установить только утилиты управления.

sudo apt update
sudo apt install -y zfsutils-linux
zfs version

zfs version выводит две строки: версию пользовательских утилит и версию модуля ядра. Если выводится только одна строка, значит, модуль не загрузился. Пакет находится в компоненте universe, который в образах Ubuntu server включен по умолчанию; если apt не находит пакет, сначала выполните sudo add-apt-repository universe.

В Debian пакеты находятся в компоненте contrib, а модуль собирается на вашей машине с помощью DKMS (dynamic kernel module support). Добавьте contrib в строку Components: в файле /etc/apt/sources.list.d/debian.sources, выполните sudo apt update, а затем:

sudo apt install -y linux-headers-$(dpkg --print-architecture) zfs-dkms zfsutils-linux

Установка запускает компиляцию модуля и выводит Building initial module for 6.12.0-..., что занимает несколько минут. Помните, что это означает: при каждом обновлении ядра модуль пересобирается, и если сборка завершится с ошибкой, пул не будет импортирован, пока вы не устраните проблему.

Во FreeBSD ничего устанавливать не нужно. Включите службу и запустите её.

sysrc zfs_enable=YES
service zfs start

Теперь перейдем к пулу. Сначала изучите стабильные пути к устройствам, так как /dev/vdb назначаются в порядке обнаружения и могут измениться при подключении другого тома.

ls -l /dev/disk/by-id/
sudo zpool create -o ashift=12 tank /dev/disk/by-id/virtio-abc123def456
zpool status tank

zpool status должен вывести state: ONLINE, где ваше устройство будет указано в столбце tank. Параметр ashift=12 устанавливает минимальный размер блока пула равным 4 KiB, что соответствует современным SSD и не может быть изменено после создания.

Большинство арендуемых образов загружаются с раздела ext4, поэтому ZFS здесь будет использоваться как пул данных на втором томе, а не как корневая файловая система. Убедитесь, что устройство именно то, которое вам нужно, прежде чем создавать на нем пул, так как проверка приобретенного NVMe-диска занимает одну минуту, а пересборка — целый день.

Контрольные суммы позволяют восстановить данные только при наличии избыточности в пуле

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

В пуле из одного диска ZFS сообщает о проблеме и останавливается. zpool status -v отображает это следующим образом:

status: One or more devices has experienced an error resulting in data
        corruption.
action: Restore the file in question if possible.  Otherwise restore the
        entire pool from backup.
errors: Permanent errors have been detected in the following files:

        /tank/data/archive.tar

Поврежденный файл будет указан по имени. Файловая система ext4 вернула бы эти байты без каких-либо уведомлений, поэтому даже такое поведение является преимуществом. Однако ZFS не может исправить ошибку, так как в пуле нет второй копии для восстановления.

При использовании зеркала чтение выполняется с исправной стороны, поврежденный блок перезаписывается, а событие отображается в столбце CKSUM команды zpool status. Это и есть самовосстановление, для которого требуется два устройства.

sudo zpool create -o ashift=12 tank mirror /dev/disk/by-id/DISK1 /dev/disk/by-id/DISK2

На VPS хранилище хоста обычно уже обладает избыточностью, часто это RAID 10 на уровне гипервизора. Это защищает от выхода диска из строя. Однако такая система не сообщает о том, что блок данных был прочитан некорректно, так как массив не знает, какая копия является верной. ZFS знает это, так как сравнивает данные с контрольной суммой, которую записала сама.

Если у вас один виртуальный диск и вы хотите обеспечить возможность восстановления, sudo zfs set copies=2 tank/important позволяет хранить две копии каждого блока набора данных на том же диске. Это удваивает занимаемое место, позволяет пережить повреждение отдельного блока, но не помогает, если пропадает весь том целиком.

Операция scrub считывает все данные в пуле и проверяет их.

sudo zpool scrub tank
zpool status tank

Исправный пул завершает проверку строкой вида scan: scrub repaired 0B in 00:04:11 with 0 errors. Настройте выполнение этой операции по расписанию; для небольшого пула достаточно одного раза в месяц.

systemctl list-unit-files 'zfs-scrub*'
sudo systemctl enable --now zfs-scrub-monthly@tank.timer

Наборы данных как единица политики

Набор данных (dataset) — это файловая система внутри пула. Создание набора данных не требует больших ресурсов, поэтому создавайте отдельный набор для каждой задачи. Свойства наследуются от пула, что позволяет задать значения по умолчанию один раз и переопределять их только там, где это необходимо.

sudo zfs create tank/data
sudo zfs create tank/pg
sudo zfs set compression=lz4 tank
sudo zfs set atime=off tank
sudo zfs set quota=20G tank/data
sudo zfs set recordsize=16K tank/pg
zfs get -r compression,compressratio,quota tank

Сжатие — это свойство, которое часто не включают из осторожности, но это неверный подход. lz4 требует небольшого количества ресурсов CPU и сокращает объем данных, записываемых на диск, поэтому для сжимаемых данных это обычно ускоряет операции чтения и записи. zstd обеспечивает более сильное сжатие ценой больших затрат CPU, что подходит для логов и архивов, к которым редко обращаются. Проверяйте реальную эффективность с помощью zfs get compressratio tank и помните, что коэффициент учитывает только данные, записанные после установки этого свойства.

recordsize — это максимальный размер блока, который записывает набор данных; по умолчанию он составляет 128K. Если база данных записывает страницы по 8 KiB в записи по 128 KiB, одна небольшая операция записи превращается в чтение всей записи, её изменение и обратную запись. Установите recordsize=16K для набора данных базы данных перед загрузкой данных, так как это свойство применяется только к вновь записываемым блокам.

quota — это способ ограничить набор данных, чтобы он не заполнил весь пул. Пул ZFS, заполненный почти на 100%, работает медленно, и его сложно очистить, поэтому всегда оставляйте свободное место намеренно.

Снимки (snapshots) ничего не стоят, пока данные не меняются

ZFS никогда не перезаписывает активный блок. Она записывает новый блок и обновляет указатели — это и есть принцип copy-on-write. Снимок — это пометка «сохранить блоки, на которые этот набор данных (dataset) указывает прямо сейчас», поэтому создание снимка происходит мгновенно и бесплатно.

sudo zfs snapshot tank/data@2026-08-11
zfs list -t snapshot -o name,used,refer -r tank/data

Столбец USED для снимка показывает объем пространства, занимаемый только этим снимком. Он начинается почти с нуля и растет по мере изменения или удаления данных, так как старые блоки больше нельзя освободить.

Для восстановления файла не требуется процедура развертывания (restore).

ls /tank/data/.zfs/snapshot/
cp /tank/data/.zfs/snapshot/2026-08-11/notes.txt /tank/data/notes.txt

Каталог .zfs скрыт даже от ls -a, пока вы не выполните sudo zfs set snapdir=visible tank/data. Создавайте снимок до того, как он понадобится, потому что без него случайная команда rm -rf отправит вас на путь восстановления ext4, который начинается с размонтирования диска и становится только сложнее.

Откат (rollback) отбрасывает всё, что было записано после создания снимка.

sudo zfs rollback tank/data@2026-08-11

Система выдаст отказ, если существуют более новые снимки, а -r удаляет эти новые снимки для продолжения операции. Проверьте имя набора данных дважды, прежде чем нажать Enter.

Снимок — это не резервная копия. Он находится в том же пуле, на том же томе и на том же сервере. Выход из строя тома или zpool destroy уничтожит снимки вместе с данными. Снимки защищают вас от ваших собственных rm и неудачных обновлений, что покрывает множество реальных инцидентов, но они не защищают от проблем, затрагивающих сам пул. Полное обоснование приведено здесь: почему снимок VPS — это не резервная копия.

Отправка и получение: репликация одной командой

zfs send превращает снапшот в поток байтов в стандартный вывод, а zfs receive преобразует этот поток обратно в набор данных. Первая копия — это полная отправка (full send).

sudo zfs snapshot tank/data@daily-2026-08-11
sudo zfs send tank/data@daily-2026-08-11 | ssh backup.example.com "sudo zfs recv -F backup/data"

После этого отправляйте только изменения между двумя снапшотами.

sudo zfs snapshot tank/data@daily-2026-08-12
sudo zfs send -i tank/data@daily-2026-08-11 tank/data@daily-2026-08-12 | ssh backup.example.com "sudo zfs recv backup/data"

На принимающей стороне должен присутствовать снапшот, от которого вы выполняете отправку. Если его нет, процесс получения прерывается с ошибкой cannot receive incremental stream: most recent snapshot of backup/data does not match incremental source, так как у ZFS нет базовой точки для применения изменений. Выполняйте отправку из снапшота, который есть на обеих сторонах, или начните заново с полной отправки.

Предоставьте права на целевой системе вместо использования удаленного root: sudo zfs allow -u backupuser create,mount,receive backup/data.

Это полноценное резервное копирование на удаленную площадку при одном условии. На удаленной стороне должен быть ZFS pool, так как объектное хранилище не может принимать такой поток. Если ваша цель — S3-совместимое хранилище или обычный Linux-хост, используйте соответствующий инструмент, и резервное копирование restic с VPS описывает этот путь.

Почему ZFS потребляет так много оперативной памяти? ARC

ARC (adaptive replacement cache) — это кэш чтения ZFS. Он находится в памяти ядра, а не в обычном page cache Linux, поэтому free -h не учитывает его в показателе buff/cache. Он отображается как используемая память. Если сервер с ZFS выглядит почти полностью загруженным, это обычно означает, что кэш прогрет; именно это является причиной большинства жалоб на то, что «ZFS съела всю оперативную память».

Лимит по умолчанию намеренно сделан высоким. В OpenZFS 2.3 максимальный размер ARC устанавливается как большее из двух значений: объем RAM минус 1 GiB или 5/8 от объема RAM. В OpenZFS 2.2 и более ранних версиях на Linux использовалась половина объема RAM, в то время как во FreeBSD уже применялось новое правило. Выполните zfs version, чтобы узнать, какое правило применяется в вашей системе.

ChartDefault maximum ARC size by instance RAM, from the OpenZFS default rule (GiB)
The data behind this chart
[
  {
    "label": "2 GB VPS",
    "openzfs_2_2_linux_gib": 1,
    "openzfs_2_3_gib": 1.25
  },
  {
    "label": "4 GB VPS",
    "openzfs_2_2_linux_gib": 2,
    "openzfs_2_3_gib": 3
  },
  {
    "label": "8 GB VPS",
    "openzfs_2_2_linux_gib": 4,
    "openzfs_2_3_gib": 7
  },
  {
    "label": "16 GB VPS",
    "openzfs_2_2_linux_gib": 8,
    "openzfs_2_3_gib": 15
  }
]

Эти цифры представляют собой задокументированные правила по умолчанию для стандартных объемов памяти, а не измерения с работающего сервера. На инстансе с 4 GB памяти правило версии 2.3 позволяет ARC занимать 3 GiB. На той же системе с версией 2.2 лимит составит 2 GiB. На инстансе с 2 GB памяти правило 2.3 все равно позволяет использовать 1.25 GiB. Вашему приложению достается то, что осталось.

Смотрите реальные значения на своем сервере, а не полагайтесь на таблицу:

grep -E '^(size|c_max) ' /proc/spl/kstat/zfs/arcstats
arc_summary | head -n 20

Третий столбец содержит значения в байтах. c_max — это действующий на данный момент верхний предел, а size — объем, который ARC занимает в текущий момент.

ARC возвращает память системе. Ядро сигнализирует о нехватке памяти, и ARC уменьшается. Проблема заключается в таймингах: сокращение кэша происходит только под давлением, поэтому процесс, запрашивающий сразу несколько сотен MiB, может столкнуться с OOM (out of memory) killer до того, как ARC успеет освободить память. На сервере с 2 GB памяти, где запущены база данных и веб-сервер, это не редкость. В руководстве OpenZFS сказано то же самое относительно ручного изменения лимитов: снижение предела «не приведет к уменьшению ARC без давления на память, которое вызывает сжатие кэша».

Как ограничить размер ARC на небольшом VPS

Сначала определите объем памяти, необходимый для рабочих нагрузок. Сложите потребности базы данных и приложения, оставьте запас для операционной системы, а остальное выделите под ARC. Для инстанса с 4 GB RAM, на котором работают Postgres и одно веб-приложение, разумной отправной точкой будет от 512 MiB до 1 GiB ARC.

Установите ограничение «на лету» в байтах. В данном примере — 1 GiB.

echo 1073741824 | sudo tee /sys/module/zfs/parameters/zfs_arc_max

Сделайте настройку постоянной после перезагрузки.

echo 'options zfs zfs_arc_max=1073741824' | sudo tee /etc/modprobe.d/zfs.conf
sudo update-initramfs -u

Шаг с initramfs важен, так как модуль может загрузиться из initramfs до монтирования корневой файловой системы, из-за чего файл, который вы только что создали, не будет прочитан. После перезагрузки проверьте результат с помощью строки c_max из arcstats.

Руководство содержит два предостережения. Вы не можете вернуть значение 0 во время работы системы, поэтому для отмены изменений нужно отредактировать файл и перезагрузиться. Кроме того, уменьшение числа не приводит к мгновенному сжатию уже заполненного ARC.

В FreeBSD этот же лимит настраивается через sysctl vfs.zfs.arc. Выполните sysctl vfs.zfs.arc, чтобы увидеть текущие значения и точное имя параметра в вашей версии, а затем запишите максимум в /boot/loader.conf.

Еще два правила работы с памятью для небольшого сервера. Оставьте дедупликацию выключенной, так как таблица дедупликации хранится в оперативной памяти, а общепринятое правило гласит: требуется от 1 до 3 GB RAM на 1 TB уникальных данных. Также не размещайте swap на zvol (блочном устройстве, созданном внутри пула), так как подкачка через файловую систему, которая сама пытается освободить память, может привести к взаимной блокировке (deadlock) системы. Используйте для swap обычный раздел или файл подкачки вне пула.

Когда ext4 или XFS в сочетании с restic — лучшее решение

ZFS оправдывает себя на сервере при наличии свободной оперативной памяти и второго дискового тома. В остальных случаях обычная файловая система в сочетании с полноценным инструментом резервного копирования выигрывает. Выбирайте ext4 или XFS, если:

  • У инстанса 2 GB или 4 GB RAM, и рабочая нагрузка требует весь этот объем.
  • У вас один виртуальный диск без резервной копии, поэтому ZFS обеспечит только обнаружение ошибок без возможности их исправления.
  • Ваше хранилище для бэкапов — это object storage или обычный хост на Linux, которые не могут принять поток zfs send.
  • Вы используете Debian с DKMS и не можете позволить себе обновление ядра, после которого модуль останется несобранным.
  • Вам нужна ZFS на корневой файловой системе, а образы провайдера предлагают только ext4.

Оставляйте ZFS, если у вас есть отдельный том для данных, свободная оперативная память (от 8 GB и выше — комфортный уровень) и план использования снапшотов и zfs send, а не просто их включение. Во всех остальных случаях ext4 с использованием restic для записи зашифрованных и дедуплицированных бэкапов в хранилище, которое не контролируется самим сервером, закрывает те же задачи, не потребляя лишнюю память.

Режимы сбоев и соответствующие сообщения

Пул пропадает после перезагрузки. zpool status выводит no pools available. Служба импорта считывает /etc/zfs/zpool.cache, поэтому пул, отсутствующий в этом файле, не импортируется при загрузке. sudo zpool import показывает список доступных для импорта пулов, sudo zpool import tank выполняет импорт, а sudo zpool set cachefile=/etc/zfs/zpool.cache tank делает его постоянным. Пул, который не был корректно экспортирован из другой системы, сообщает об ошибке cannot import 'tank': pool may be in use from other system, а sudo zpool import -f tank позволяет принудительно импортировать его, если вы уверены, что ни один другой хост его не использует.

modprobe: FATAL: Module zfs not found in directory /lib/modules/6.12.0-... в Debian после обновления ядра. DKMS не выполнил сборку для нового ядра, обычно из-за отсутствия соответствующих заголовков. dkms status показывает, что собрано для конкретного ядра. sudo apt install -y linux-headers-$(uname -r), а затем sudo dkms autoinstall выполняют пересборку, после чего sudo zpool import tank возвращает пул в рабочее состояние.

Пул заполнен, хотя вы удалили файлы. Удаленные данные остаются на диске, пока на них ссылается снапшот, поэтому показатели du и df различаются. zfs list -o space -r tank разделяет использование на USEDDS и USEDSNAP, и большое значение USEDSNAP указывает на причину. Удалите старые снапшоты с помощью sudo zfs destroy tank/data@2026-06-01, и место будет освобождено.

Значения CKSUM растут в zpool status. Уровень ниже ZFS вернул поврежденные данные. В зеркале это значение является предупреждением, а блок был восстановлен автоматически. В пуле на одном диске файл считается потерянным, zpool status -v указывает его имя, и вам необходимо восстановить этот файл из резервной копии, которая не находится в данном пуле.

Сервер работает медленно и использует swap. Ограничьте размер ARC, как описано выше, затем запустите arc_summary и проверьте коэффициент попаданий (hit ratio). Если ARC слишком мал для рабочего набора данных, каждое чтение обращается к диску; в такой ситуации обычная файловая система, использующая page cache, будет работать эффективнее.

FAQ

Сколько оперативной памяти требуется ZFS на VPS?

ZFS работает на инстансах с 2 GB памяти. Главный вопрос заключается в том, сколько ресурсов останется для вашего приложения. Без дополнительной настройки OpenZFS 2.3 позволяет ARC занимать объем, равный большему из двух значений: RAM минус 1 GiB или 5/8 от общего объема RAM. Таким образом, на сервере с 4 GB памяти под кэш может быть выделено 3 GiB. Установите zfs_arc_max в значение, которое может выделить ваша рабочая нагрузка, а затем проверьте его, прочитав строку c_max из /proc/spl/kstat/zfs/arcstats.

Является ли ZFS snapshot резервной копией?

Нет. Снимок (snapshot) находится в том же пуле, что и сами данные. Он защищает от неудачной rm или сбоя при обновлении, но он исчезнет вместе с пулом или инстансом. Чтобы превратить его в резервную копию, отправьте снимок на другую машину с помощью zfs send или используйте инструмент резервного копирования, который записывает данные в хранилище, не контролируемое данным сервером.

Одинаково ли работает ZFS в FreeBSD и Linux?

Используется общая кодовая база начиная с OpenZFS 2.0 (декабрь 2020 года), одинаковые команды, одинаковый формат данных на диске, а пулы можно переносить между системами. Разница заключается в способе упаковки. FreeBSD включает ZFS в базовую систему. В Linux каждая дистрибуция решает это по-своему: Ubuntu собирает модуль в составе своих пакетов ядра, а Debian собирает его на вашей машине с помощью DKMS. Поэтому после обновления ядра вы можете остаться без модуля, пока не завершится его пересборка.

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

Система обнаружит повреждение и укажет на конкретный файл, но не сможет его исправить, так как для восстановления нужна вторая копия блока. Параметр zfs set copies=2 для набора данных (dataset) создаст такую копию, увеличив расход места вдвое. Это поможет при сбое отдельного блока, но не при потере тома. Решением, которое действительно восстанавливает данные, является зеркалирование на два тома.

Замедляет ли сжатие работу сервера?

lz4 обычно ускоряет работу. Сжатые блоки означают меньше записанных и прочитанных байтов, а затраты ресурсов CPU на блок незначительны по сравнению с экономией дискового пространства. Установите compression=lz4 в корне пула, чтобы все наборы данных унаследовали эту настройку, а затем проверьте zfs get compressratio tank после записи реальных данных.