02 ноября 2014

ZFS FreeBSD

Это учебная статья посвященная ZFS, из которой вы узнаете, что это такое, как использовать и как лучше настроить.
Для тех, кто не в курсе это локально-распределенная файловая система нового поколения, которая объединяет отдельные диски в пулы с возможностью создания RAID 0, 1, 10, 5. Это 128-битная файловая система с переменным размером блока (или кластера, как в терминологии fat) до 128 КБ. Таким образом каждый пул может адресовать огромный массив данных, в идеале (2^128)*128 КБ. В качестве полезных функций имеются встроенные механизмы квотирования, резервирования, контроля целостности данных и некоторые др. Отличительные черты - быстродействие, надежность и удобство.

Сразу стоит сказать, что ZFS требовательна к ресурсам, минимально для комфортной работы ей нужно не менее 1 Гб оперативной памяти на любой архитектуре, но чем больше тем лучше. Поэтому 64-битные системы предпочтительней, у них нет жестких ограничений адресного пространства.

Журналируемость, транзакционность и контрольные суммы каждого блока в совокупности с основным приемуществом ZFS - методом записи "Copy On Write" обеспечивают уверенную отказоустойчивость при нештатных отключениях питания.

По технологии "CoW" новые данные не перезаписывают старые, а пишутся в свободное пространство и меняется только указатель. Правда у нее есть и обратная сторона медали, при чрезмерном заполнении пула происходит падение производительности. Поэтому  рекомендуется активно пользоваться квотированием и не допускать заполнения больше, чем на 80%.

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

И ради любопытства я провел дилетантское сравнение быстродействия с UFS (журнал + soft update) при операции записи. Я выполнял копирование с помощью dd if=/dev/zero of=file bs=1M count=2000. Как и следовало ожидать на моем слабом железе ZFS оказался медлительней.

Действительно, что ZFS может сделать на стареньком P4 3Гц, c 2ГБ памяти и одним жестким диском SATA.  Хотя в процессе тестирования я и пытался настраивать разные параметры производительности ZFS (их мы рассмотрим чуть ниже), но лучшие результаты все равно оказались следующими ZFS - 26, 35 сек против UFS - 19,67 сек.

Поэтому отдаем себе отчет, что ZFS предназначена для достаточно мощного и современного оборудования с 64-битной архитектурой и с не менее, чем 4 Гб памяти. Но на старом железе конечно тоже будет работает, проверено лично. Но какой результат и зачем мучить свой старенький комп?

И так, приступим к препарированию.
 
B ZFS нет понятий томов и слайсов, как в традиционных ФС. Есть пул, внутри которого создаются файловые системы или как их еще называют датасеты. Размер файловых систем не фиксируется и может быть легко увеличен в результате присоединения нового диска к пулу. Поэтому здесь не может наступить критической ситуации полного заполнения диска.

Уменьшить размер ZFS пула уже не возможно! К примеру нельзя изъять диск, если это не зеркало. Это сразу приведет в выходу из строя всего пула! Заменить диск на другой, большего размера, всегда пожалуйста. Поэтому единственное, как можно уменьшить пул, это сохранить все данные на внешнем носителе, разрушить пул и создать новый.

Установка FreeBSD на ZFS
Нужно загрузиться с CD-диска или USB Memstick и начать установку как обычно. В FreeBSD 10 установка на ZFS уже полностью автоматизирована через bsdinstaller. На этапе разметки дисков достаточно выбрать пункт ZFS и система все сделает за вас. Ну а если хотите сделать это руками, то заходите в shell.
Здесь следует обратить внимание на следующие настройки: тип пула (stripe, mirror или raidz1, raidz2, raidz3), далее выбрать нужные диски для его создания. Пункт "forse 4k sectors" устанавливает выравнивание по 4К для каждого созданного раздела. Это важно, если у вас новые жесткие диски большого объема с размером сектора 4K, а не 512Б. Если нет, то укажите NO.
Давайте разберем, какие шаги выполняет установщик и заодно научимся работать с пулами. Описанные команды так же можно выполнять в ручном режиме, результат будет одинаковый.

Установщик автоматически создает три раздела GPT: один для загрузчика, раздел swap и основной раздел под пул.
Посмотрим для начала на структуру диска
# gpart show ada0 
Так удаляется старая таблица разделов
# gpart destroy ada0
Создается новая таблица GPT
# gpart create -s GPT ada0
Добавляются три раздела
# gpart add -s 512 -a 4k -t freebsd-boot -l boot0 ada0 
# gpart add -s 4g -a 4k -t freebsd-swap -l swap0 ada0 
# gpart add -a 4k -t freebsd-zfs -l disk0 ada0
Здесь с помощью опции -a задается выравнивание разделов по 4Кб. Если у вас старые винты с физическим сектором 512б, то опцию пропускаем.

Устанавливается загрузчик
# gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 ada0
Здесь используются два загрузчика, первый pmbr в области Protective MBR перед GPT, он передает управление второму gptzfsboot, он на первом секторе раздела freebsd-boot. А уже на третьем этапе вызывается loader.

Если вдруг вы решите не использовать ZFS, а вернетесь к UFS, то надо использовать другой загрузчик
# gpart bootcode -b /boot/pmbr -p /boot/gptboot -i 1 da0
Для работы с ZFS загружаем модуль
# kldload zfs
При использовании дисков с сектором 4К важно задать размер блока ZFS.  Поскольку диск для совместимости рапортует о секторе 512Б. И по умолчанию такой размер выбирается автоматически для блока при создании пула.  Поэтому обязательно перед созданием пула нужно сделать следующее!

Поверх диска создается устройство gnop с размером сектора 4К
# gnop create –S 4096 /dev/gpt/disk0
Создается пул
# zpool create -o altroot=/mnt -O canmount=off -m none zroot /dev/gpt/disk0.nop
Отсоединяется пул
# zpool export zroot
Удаляется устройство gnop
# gnop destroy /dev/gpt/disk0.nop
Снова присоединяется пул
# zpool import zroot
Проверяем, если ashift=12, то блок установлен правильно 2^12=4К. Если 9, то 2^9=512Б.
# zdb –C tank | grep ashift
Так же в FreeBSD 10  размер блока ZFS можно задать проще, с помощью 
# sysctl vfs.zfs.min_auto_ashift=12 
Сделать это нужно так же перед созданием пула и добавить в /etc/sysctl.conf, что бы не делать это вручную каждый раз в будущем.

Задаем алгоритм проверки контрольных сумм fletcher4, по умолчанию используется fletcher2.
# zfs set checksum=fletcher4 zroot
Запрещаем обновление времени доступа при каждом обращении к файлу.  
# zfs set atime=off zroot
Создаем файловые системы
# zfs create -o mountpoint=none zroot/ROOT 
# zfs create -o mountpoint=/ zroot/ROOT/default 
# zfs create -o mountpoint=/tmp -o compression=lzjb -o setuid=off zroot/tmp 
# chmod 1777 /mnt/tmp
# zfs create -o mountpoint=/usr zroot/usr zfs create zroot/usr/local
# zfs create -o mountpoint=/home -o setuid=off zroot/home
# zfs create -o compression=lzjb -o setuid=off zroot/usr/ports

# zfs create -o compression=off -o exec=off -o setuid=off zroot/usr/ports/distfiles 

# zfs create -o compression=off -o exec=off -o setuid=off zroot/usr/ports/packages
# zfs create -o compression=lzjb -o exec=off -o setuid=off zroot/usr/src 

# zfs create zroot/usr/obj
# zfs create -o mountpoint=/var zroot/var 

# zfs create -o compression=lzjb -o exec=off -o setuid=off zroot/var/crash 
# zfs create -o exec=off -o setuid=off zroot/var/db 
# zfs create -o compression=lzjb -o exec=on -o setuid=off zroot/var/db/pkg 
# zfs create -o exec=off -o setuid=off zroot/var/empty 
# zfs create -o compression=lzjb -o exec=off -o setuid=off zroot/var/log 
# zfs create -o compression=gzip -o exec=off -o setuid=off zroot/var/mail 
# zfs create -o exec=off -o setuid=off zroot/var/run 
# zfs create -o compression=lzjb -o exec=on -o setuid=off zroot/var/tmp 
# chmod 1777 /mnt/var/tmp
Указываем корневую ФС.
# zpool set bootfs=zroot/ROOT/default zroot
Добавляем swap раздел в fstab
# vi /tmp/bsdinstall_etc/fstab
--# Device        Mountpoint FStype Options Dump Pass# 

/dev/gpt/swap0  none       swap   sw      0    0
После того как будут созданы все файловые системы, если выполняли в ручном режиме, то нужно выйти из shell, пишем exit. Далее установка продолжается, как обычно.

Так же для ручного режима, перед перезагрузкой нужно еще раз зайти в shell и прописать автоматическое монтирование файловых систем.
# mount -t devfs devfs /dev 
# echo 'zfs_enable="YES"' >> /etc/rc.conf 
# echo 'zfs_load="YES"' >> /boot/loader.conf
Для порядка зададим только чтение для /var/empty, поскольку она всегда должна быть пустой.
# zfs set readonly=on zroot/var/empty 
На этом установка завершена.  Но, если вам хочется добиться лучшей производительности можно настроить еще некоторые параметры.

Способы оптимизации сильно зависят от назначения вашей системы и будут отличаться для БД, вэб-сервера и файлового сервера. В числе основных рекомендаций, можно найти такие, отключение обновления времени доступа при каждом обращении atime (мы уже это сделали), иметь как можно меньше снимков состояний,  использовать для ZIL и L2ARC отельные SSD диски. Использовать для файловых систем разные recordsize, соразмерно хранимым на них файлам. Если много мелких файлов, то recordsize лучше сделать 16Кб, а по умолчанию он 128Кб.
# zfs create -o recordsize=8k tank/mysql
Есть конечно и параметры, влияющие работу в целом. Рассмотрим их.

Как вы знаете на i386 архитектуре ядро FreeBSD по умолчанию  резервирует под все свои нужды всего 1Гб адресного пространства из 4 возможных, а остальное оставляет процессам. Но, если в ядре есть такой большой потребитель оперативной памяти, как ZFS, то его может и не хватить!  Поэтому нужно увеличить это значение хотя бы до 2Гб, добавив в ядро опцию.
options KVA_PAGES=512 
(размер страницы — 4 Кб)*512=2Гб.
Но после пересборки ядра, во время загрузки, у меня начались "double fault panic". Как оказалось, проблема распространенная и многие пользователи ZFS на i386 архитектуре наблюдают переполнение стека ядра. Это начало происходить с того момента, как Clang сделали компилятором по умолчанию. Для того, что бы этого избежать нужно добавить еще одну опцию.
options KSTACK_PAGES=4
Или задать в /etc/sysctl.conf параметр
kern.kstack_pages=4
На amd64 проблем с нехваткой адресного пространства нет. Там по умолчанию отвели ядру 512 Гб  виртуальных адресов, поэтому вышеописанное не потребуется.

Теперь, когда виртуальное адресное пространство ядра KVА (Kernel Virtual Address space) увеличено, надо этим поделиться с ZFS. В /boot/loader.conf добавляем параметры
vm.kmem_size="1G" 
vm.kmem_size_max="1G"
kmem - это участок KVA, который используется для динамического выделения памяти malloc, и соответственно он будет использоватьcя ZFS.

ARC
Еще одним важным параметром влияющим на быстродействие ZFS является размер кэша адаптивной замены ARC (Adjustable Replacement Cache). Это очень быстрый кэш, расположенный в ОЗУ, построенный на двух алгоритмах MRU им MFU. Общий смысл его заключается в том, что бы обеспечить доступ к наиболее часто используемым данным прямо из памяти, а не с медленного диска. И поэтому, чем у вас больше оперативной паями, тем лучше для ZFS. 

По умолчанию ARC разрешается использовать всю свободную память по необходимости, кроме 1Гб зарезервированного ядром. Но если если у вас категорическая не хватка паями и ее обязательно надо оставить для других важных задач, то имеет смысл ограничивать размер кэша
vfs.zfs.arc_max="512M"
Если этого не сделать, то может произойти kernel panic по причине отсутствия свободного пространства. Поэтому проверяйте
# sysctl vm.kvm_free
Так же есть дешевый способ увеличить размер кэша при помощи обычной флешки. Изначально для этого задумано использовать SATA SSD-диски. Это называется вторым уровнем кэша L2ARC. В этой статье человек делится опытом подключения флешки 1G в качестве L2ARC.
# zpool add pool cache device
# zpool remove pool device
Но в виду того, что пропускная способность шины USB 2.0 ниже, чем у механических жестких дисков: 30 против 100 Мбайт/сек, то в качестве полноценного кэша старая флешка не годиться. Да и быстро выйдет из строя из-за постоянной перезаписи и отсутствия механизма выравнивания износа. Тем не менее сгодится для домашнего использования при малой нагрузке. Ведь время поиска (seek time) y флешек гораздо меньше, чем у дисков и составляет от 1 до 3 мсек, против 12 мсек. Поэтому прирост скорости можно получить только на произвольном (не последовательном) чтении данных. И лучше хранить на ней только метаданные, это позволит прослужить ей дольше.
# zfs set secondarycache=metadata pool
Что касается usb 3.0, то там полезная скорость возросла до 500 Мбайт/с и возможно такие флешки уже пригодны, но надо тестировать.

ZIL
На случай потери данных при сбоях и отключении питания некоторые приложения (например, NFS и БД) используют синхронную запись данных. Для этих целей у ZFS имеется целевой журнал ZIL (ZFS Intent log). Когда приложение хочет записать данные на диск, оно отправляет запрос ОС и ждет ответа. ОС сначала кэширует данные в ROM,  затем сохраняет их в ZIL и только после этого возвращает управление приложению. При этом ZIL обязательно должен находиться в энергонезависимой памяти и по умолчанию располагается на диске внутри пула. Вот это и является узким местом при увеличении производительности. Но ZFS позволяет использовать в качестве log-устройства отдельные диски или даже несколько дисков одновременно.  Поэтому обязательно обзаведитесь  быстрым  SSD-диском.
# zpool add pool log c2d0
В особых случаях, если нет SSD, то ZIL лучше отключить, производительность возрастет, но появится опасность потери данных при записях во время сбоев.
В /boot/loader.conf добавляем
vfs.zfs.zil_disable="1"
Еще можно отключать zil отдельно для каждой файловой системы.
# zfs set sync=disabled pool/name

Теперь рассмотрим функциональность и познакомимся с основными командами для управления ZFS.

Здесь все просто, для работы с пулами используется утилита zpool, а с файловыми системами - zfs. Они имеют смысловые команды типа create, desptoy, attach, deattach и т.д.

Работа с пулами
Пулы создается командой 
# zpool create pool /dev/ada0
Если пул не загрузочный, то можно использовать весь диск целиком без GPT разбивки.
Удаляется пул командой 
# zpool destroy pool
Вывести список подключенных пулов
# zpool list
Узнать состояние
# zpool status pool
Отсоединить пул
# zpool export pool
Присоединить
# zpool import pool
Если использовать команду import без параметров, то получите список доступных пулов.

Добавить дополнительный диск к пулу
# zpool add pool ada3
Присоединяем зеркало. Диски должны быть одинакового размера, иначе по меньшему. После этого начнется resilvering, процесс синхронизации.
# zpool attach pool /disk /new-disk
Отсоединяем зеркало
# zpool detach pool /disk
Замена одного устройства на другое
# zpool replace pool /disk /new-disk
После завершения выполнения команды диск отключается от конфигурации и может быть удален из системы.

Посмотреть все  параметры пула можно так
# zpool get all
Файловые системы
Создание файловой системы в пуле осуществляется командой
# zfs create pool/name
По умолчанию каждая созданная ФС монтируется в одноименный каталог. Но точку монтирования можно указать и другую.
# zfs set mountpoint=/new/puth pool/name
Все созданные файловые системы монтируюся автоматически при присоединении пула, поэтому их не надо прописывать в fstab.

Монтирование файловой системы в пул
# zfs mount myzfs/bob
# zfs unmount myzfs/bob
Файловая система созданная внутри другой наследует ее свойства. К ним относится алгоритм сжатия и алгоритм вычисления контрольных сумм, размер блока, квота и мн. др. Посмотреть все свойства  можно по команде
# zfs get all
Зарезервировать место для файловой системы
# zfs set reservation=100G pool/name
Задать квоту
# zfs set quota=50G pool/name
Квоты можно задавать отдельно для групп и пользователей
# zfs set userquota@user=5G pool/name
# zfs set groupquota@group=10G pool/name 
Смотреть общую информацию по использованию квот можно так
# zfs userspace pool/name
# zfs groupspace pool/name
или конкретно для каждого
# zfs get userused@user pool/name
# zfs get groupused@group pool/name
ZFS предлагает  очень удобный механизм резервного копирования. Для этого используются снимки и клоны.

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

Получить прямой доступ к снимкам нельзя. Их можно только клонировать, создавать резервные копии, выполнять откат и др. Создается снимок так
# zfs snapshot pool/name@snap_name
если указать параметр рекурсии -r, то создаются снимки всех дочерних ФС. Располагаются в директории .zfs/snapshot в корне каждой файловой системы.
# zfs snapshot -r pool/name@now
Удалить снимок 
# zfs destroy pool/name@snap_name
Можно переименовывать снимки
# zfs rename pool/name@old_name new_name
Вывести список всех снимков 
# zfs list -t snapshot
Выполнить откат к снимку
# zfs rollback pool/name@snap_name
ФС предварительно должна быть размонтирована. Все изменения, а так же созданные снимки после будут удалены и система вернется к состоянию на момент снимка.

На основе снимков можно создавать клоны.
# zfs clone pool/name@yesterday pool/clone
Уничтожить клон
# zfs destroy pool/clone
Можно выполнить замену исходной файловой системы на клон.
# zfs promote pool/clone
NFS
Есть удобный механизм добавления ресурсов в NFS. Для этого сперва нужно убедиться, что у вас запущены службы NFS. В /etc/rc.conf пишем

nfs_server_enable="YES"
nfs_server_flags="-u -t -n 4"
rpcbind_enable="YES"
mountd_enable="YES"
mountd_flags="-r"
Что бы добавить общий ресурс используется следующий синтаксис.
# zfs set sharenfs=on pool/name
Ресурс будет доступен всем для записи и чтения и свойства автоматически наследуются для всех дочерних файловых систем. В качесте параметров sharenfs можно использовать все опции доступные в exports.

Что бы задачть только для чение, то указывается опция ro
# zfs set sharenfs=ro pool/name 
Если у ресурса установлен признак sharenfs, то его можно включать и отключать командами share и unshare.
# zfs share -a
# zfs unshare pool/name
# zfs unshare -a
Как только опция sharenfs меняется, служба mountd перезагружается автоматически.

Если нужно разрешить доступ только конкретным сетям или адресам, то так


# zfs set sharenfs="-maproot=root -alldir -network 10.0.0.0 -mask 255.255.255.0 192.168.15.2 192.168.10.5" zroot/share
Посмотеть все расшаренные ресурсы
# zfs get sharenfs 
На стороне клиента ресурс монтируется следующим образом
# mount -t nfs server_ip:/pool/name /mnt
Для втоматического монтирования в /etc/fstab нужно добавить
server_ip:/pool/name /mnt nfs rw 2 2
Еще бывает полезно посмотреть активность дисков в реальном времени.
# gstat -a 

6 комментариев:

  1. Всесто: gnop destroy ada0.nop
    нало: gnop destroy/dev/gpt/disk0.nop

    ОтветитьУдалить
    Ответы
    1. Да, вы правы, спасибо за исправление!

      Удалить
  2. "Получить прямой доступ к снимкам нельзя. Их можно только клонировать, создавать резервные копии, выполнять откат и др."


    прямо с венды захожу в шару в \.zfs\snapshot
    и вижу их и файло которое можно копировать и да же удалять )

    ОтветитьУдалить
    Ответы
    1. Имелось же ввиду содержимое снимков, а не ссылки на них!

      Удалить
  3. Все понятно - но вот вопрос
    Присоединяем зеркало. Диски должны быть одинакового размера, иначе по меньшему. После этого начнется resilvering, процесс синхронизации.
    # zpool attach pool /disk /new-disk
    Отсоединяем зеркало
    # zpool detach pool /disk

    Что потом с этим диском disk отсоединенным делать? Как его можно примаунтить и работать?

    ОтветитьУдалить
    Ответы
    1. Первое, что вы можете сделать это удалить диск из зеркальной конфигурации detach с целью последующей замены.
      # zpool replace tank disk
      Второе, вы можете импортировать пул в другую систему, но перед этим должны выполнить экспорт, поскольку попытка импорта пула, который не был явно экспортирован, отклоняется!

      Удалить