ZFS на VPS: настройка ARC и потребление оперативной памяти
Узнайте, как ZFS использует RAM для кэширования ARC и как ограничить потребление памяти на VPS с 2-4 ГБ RAM. Разбор настройки zfs_arc_max для стабильной работы системы.
Что дает ZFS и чего она требует
ZFS в FreeBSD и Linux теперь использует общую кодовую базу OpenZFS, поэтому функциональность систем идентична. Сервер под управлением ZFS получает контрольные суммы данных, моментальные снимки (snapshots), которые не занимают места до изменения данных, репликацию с помощью zfs send и сжатие, включаемое одной настройкой. ZFS требует оперативной памяти: ARC (adaptive replacement cache) по умолчанию занимает значительную часть RAM. На VPS (virtual private server) с 2 ГБ или 4 ГБ памяти этот объем критически важен для работы ваших приложений.
Данное руководство рассматривает ZFS применительно к арендованному VPS с одним или двумя виртуальными дисками, а не к серверу хранения данных с сорока отсеками для дисков. Внимание стоит уделить тем функциям, которые полезны в таких условиях. О тех частях, которые не подходят для подобных конфигураций, следует знать до создания пула.
OpenZFS в FreeBSD и Linux: одна кодовая база, два подхода к упаковке
FreeBSD включает ZFS в базовую систему начиная с версии 7.0 в 2008 году, изначально в качестве экспериментальной функции. С момента выхода OpenZFS 2.0 в декабре 2020 года FreeBSD и Linux собираются из одного дерева исходного кода, поэтому zfs и zpool работают одинаково в обеих ОС, а пул, созданный в одной системе, импортируется в другой.
Причина, по которой ZFS в Linux является пакетом, а во 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 versionzfs 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 tankzpool 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 всё ещё не может исправить ошибку, так как в пуле нет второй копии для восстановления.
При использовании зеркала (mirror) чтение выполняется с исправной стороны, поврежденный блок перезаписывается, а событие отображается в столбце 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Наборы данных (datasets) как единица политики
Набор данных — это файловая система внутри пула. Создание набора данных выполняется быстро, поэтому рекомендуется создавать отдельный набор для каждой задачи. Свойства наследуются от пула, что позволяет один раз задать значения по умолчанию и переопределять их только там, где это необходимо. В FreeBSD именно так обычно запускаются jails: один набор данных на один jail. Это позволяет создавать снимки (snapshots) и откатывать состояние конкретного jail независимо от других, что является частью того, чем jail отличается от Docker-контейнера.
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. Снимок — это пометка «сохранить блоки, на которые этот набор данных указывает прямо сейчас», поэтому создание снимка происходит мгновенно и бесплатно.
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 превращает этот поток обратно в набор данных. Первая копия является полной отправкой.
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 устанавливается как большее из двух значений: объем ОЗУ минус 1 GiB или 5/8 от общего объема ОЗУ. В OpenZFS 2.2 и более ранних версиях для Linux использовалась половина объема ОЗУ, в то время как во FreeBSD уже применялось новое правило. Выполните zfs version, чтобы узнать, какое правило действует у вас.
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 на каждый TB уникальных данных. Не размещайте swap на zvol (блочном устройстве, созданном внутри пула), так как подкачка через файловую систему, которая пытается освободить память, может привести к взаимной блокировке (deadlock) системы. Используйте для swap обычный раздел или файл подкачки вне пула.
Когда ext4 или XFS в сочетании с restic — лучшее решение
ZFS оправдывает себя на сервере при наличии свободной оперативной памяти и второго тома. В остальных случаях обычная файловая система в сочетании с полноценным инструментом резервного копирования выигрывает. Выбирайте ext4 или XFS, если:
- На инстансе 2 ГБ или 4 ГБ оперативной памяти, и рабочая нагрузка требует её целиком.
- Имеется только один виртуальный диск без второй копии, поэтому ZFS обеспечит лишь обнаружение ошибок без возможности их исправления.
- Ваше хранилище для резервных копий — это объектное хранилище или обычный хост на Linux, которые не могут принять поток
zfs send. - Вы используете Debian с DKMS и не можете позволить себе обновление ядра, после которого модуль останется несобранным.
- Вам нужна ZFS на корневой файловой системе, а образы провайдера предлагают только ext4.
Оставляйте ZFS, если у вас есть отдельный том для данных, свободная оперативная память (от 8 ГБ и выше — комфортный уровень) и план использования снимков (snapshots) и 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 после записи реальных данных.