[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

ZFS, философское



Я тут в процессе установки stretch в позу root on ZFS произвел некоторое переосмысление привычек управления файловой системой, и хочу поделиться. Если кто-нибудь напишет что-нибудь умное в ответ, как концептуальное, так и практическое, я как минимум с благодарностью прочитаю, а если будет что обсудить, то и обсужу. Извините, многабукф.

В качестве материала были скрипт Hajo Noerenberg https://github.com/hn/debian-stretch-zfs-root и вики-статья Richard Laager https://github.com/zfsonlinux/zfs/wiki/Debian-Jessie-Root-on-ZFS

Я так понимаю, статья первична, она с тех пор правилась неоднократно. Работа Нёренберга ценна в первую очередь тем, что некоторые технические, но важные моменты из вики-статьи там доведены до работающего кода. Или не работающего, если автору не свезло его протестировать :), я ему push request отправил. Некоторые из них действительно надо скриптовать, потому что шансов ошибиться там немало. Однако, в плане выделения файловых систем он, на мой взгляд, пошел по пути простому, но неправильному.

Лаагер, создавая файловые системы в пуле, явно говорит: "я стараюсь отделить систему от пользовательских данных, чтобы, если потребовалось после неудачного апгрейда откатывать /, не откатился заодно /var/log, где могут быть логи неудач". Да, /var/log он рассматривает как пользовательские данные.

И вот тут я словил осознание. В ZFS комбинация дешевых снапшотов и отсутствия неизбежно зарезервированного места для FS (недоступного другим FS) дала больше, чем каждое из них по отдельности. По отдельности второе тривиально (вообще не делить FS), а первое есть, например, в LVM. Но вместе они дают возможность плодить много файловых подсистем каждая со своей политикой резервирования и восстановления. И сразу начинаешь иначе оценивать, что с чем стоит объединять, а что нет.

Добавляя к философии практику, он

- При создании пула dataset'у пула прописывает canmount=off
  mountpoint=/. Штатное употребление canmount=off, очень удобно для
  наследования точек монтирования у подсистем. Нёренберг в скрипте не
  использует этого, а зря. Отмечу как важный момент.

- Делает по образцу и для возможной будущей совместимости с утилитами
  исходного соляриса контейнер rpool/ROOT (mountpoint=none, кажется) для
  корневых FS и внутри него уже создает rpool/ROOT/debian с mountpoint=/ и
  canmount=noauto — типа, монтирует его initrd. Мне canmount=noauto в этом
  месте не понравилось — если придется пул импортировать со стороны, придется
  цеплять его в три команды, а не в одну, и это если не забыть сразу
  импортировать с -N. С другой стороны, это пока у нас там один рут, а
  остальные — снапшоты. Как я подозреваю, в исходном солярисе их реально может
  быть несколько. У нас потенциально тоже ничто не мешает, и даже иметь их от
  разных дистрибутивов, а при некоторой аккуратности и разных ОС. Тогда noauto
  станет нужным.

- Делает rpool/var немонтируемым (canmount=off) dataset'ом с
  exec=off. Т.е. поддиректории /var сами по себе расположены в
  rpool/ROOT/debian, кроме явно созданных датасетов. Тут с точки зрения
  администрирования интересно exec=off, которое потом отдельно оверрайдится
  для rpool/var/tmp, а остальные датасеты наследуют. Хотелось бы понять, есть
  ли в этом смысл с учетом того, что изрядная часть /var расположена в
  корневом датасете, где exec вполне себе yes. И для /var/lib/dpkg/info,
  например, он нужен. А если идти дальше, то, например, LXC формирует образы
  систем тоже под /var/lib. Понятно, что для них отдельные датасеты, но в них,
  опять, exec надо. Тут вот нужен бы некий практический опыт на тему того, что
  еще у нас живет под /var, и нужно ли ему exec.

- Выделяет из rpool/var rpool/var/tmp, rpool/var/log, rpool/var/spool,
  rpool/var/cache. Последнему отключает автоматические снапшоты, что
  логично. Не вполне понятно, забыл он отключить их спулу, или намеренно не
  отключил. К спулу в гораздо большей степени, чем к кэшу, относится "это из
  бэкапа/снапшота не восстанавливают". Если восстановление кэша хотя бы
  безвредно, то спула — вредно. С занудной кочки зрения, снапшоты могут
  оказаться полезны для разбирательства, фиг ли письмо доставлено через две
  недели, но этого проще превентивно добиться тупо анализом возраста файлов в
  спуле. Я у себя спулу автоснапшоты тоже выключил. Для rpool/var/tmp включает
  exec=yes, оно без этого не живет. /tmp, к сожалению, тоже. Лаагер /tmp не
  выделяет, Нёренберг выделяет, и я у себя тоже выделил. setuid у Лаагера,
  кажется, не отключается. Нёренберг отключает для rpool/var/tmp, но почему-то
  не для tmp, если я правильно помню. Я для обеих tmp отключил — там exec
  включен и world-writable.

- Говорит о выделении таких мест, как /var/lib/postgresql, если он у кого есть
  (а где-то мне попадалось выделение еще и отдельного датасета под его xlog,
  там типа другие настройки), /var/games, если у кого стоят игры,
  /var/lib/nfs, у кого используется NFS, для локов, и т.п. Короче, о
  художественном выпиливании лобзиком по /var и /var/lib.

- Выделяет rpool/home с setuid=off. В нем создает rpool/home/root с
  mountpoint=root. Есть ли смысл домик рута держать в том же датасете, у меня
  большие сомнения. В нем и setuid запрещать смысла нет, и под общую квоту,
  если она будет выставлена на весь /home, тоже загонять его не стоит.

- Что интересно: после установки системы делает zfs set devices=off rpool. Все
  остальные наследуют. Интересно тем, что реальный /dev и прочие места, где
  штатно создаются устройства, у нас на tmpfs, и даже на / создавать
  устройства нужно только во время debootstrap для заполнения статического
  /dev, а потом оно даже там не нужно.

На выходе я сделал для себя вывод, что если админ хочет иметь возможность
отката к прежнему состоянию системы после неудачного апгрейда, ему нужно
хорошо представлять, какие задачи и где решает система. Особенно художественна
эта задача для /var и /var/lib. Если кто еще сможет что умное сказать про то, как под разные задачи следует там выпиливать, будет ценно.

Нёренберг в этом месте пошел по пути простому, но неправильному. Выделил
rpool/var и rpool/var/tmp, и не справившись с управлением (дело было еще на
jessie), решил, что их надо монтировать через fstab, типа иначе слишком
поздно. Лаагер без этого обходится, но rpool/var у него не выделена. Я,
однако, напарывался, что в каких-то ситуация (race condition?) под /tmp,
/var/log и даже, кажется, /var/cache кто-то успевает создать поддиректории до
монтирования датасетов, и zfs mount ругается. Но не всегда. Вот при переносе
винта из машины, где шел начальный сетап, на машину, где ему потом стоять,
налетел. А после ручной чистки и перезагрузки взлетело.

Рискну предположить, что у варианта Нёренберга не столько монтирование происходит раньше, сколько mount по умолчанию легко монтирует новую FS поверх непустой директории, а ZFS по умолчанию нет. Что аккуратнее, но менее надежно. Вот и думаю: то ли разрешить некоторым датасетам монтироваться поверх непустого дерева (есть у ZFS и такая настройка), то ли разбираться, какая зараза успевает их создать, когда что-то идет не так...

Как я уже писал, в stretch скриптов для ZFS для sysvinit нет вообще, а юниты
для systemd, _казалось бы_, написаны правильно, но вот опыт с неудачным
взлетом (как минимум два раза в процессе установки и настройки конструкции)
намекает нам, что авотфиг. Какие-то шибко умные подсистемы таки поднимаются в
параллель, и иногда успевают раньше.

Отдельный вопрос к опытным гражданам: я правильно понимаю, что никакого
способа аккуратно экспортировать пул, на котором корень, не существует? Вроде
как ключ force у zpool export есть, но с этой ситуацией он не
справляется. Хотя, казалось бы, выпили его из файла кэша (оный файл на оном же
корне по умолчанию расположен), установи датасет (да хоть все датасеты пула) в
readonly, и экспортируй на здоровье. Так нет...


Reply to: