Устойчиво размещенный, управляемый F/OSS Объектное хранилище, Redis, Prometheus на Hetzner, OVH и LeaseWeb **tl;dr — Построчное объяснение моего скрипта установки ZFS с поддержкой ansible для использования на специализированном оборудовании Hetzner (Ubuntu 20.04 — Focal) â «Это не идеально/минимально, но мне подходит» Некоторое время назад я начал использовать ZFS на всем своем выделенном железе, размещенном в Hetzner, чтобы спорить с подключенными жесткими дисками и твердотельными накопителями. В пространстве есть много вариантов (стандартный LVM, мдраид btrfs и т. д.), но я выбрал ZFS из-за ее функциональности и эргономики. Я не буду никого утомлять объяснением почему, но одна из вещей, которые мне нужно было понять, это как установить более новые версии ZFS на Ubuntu 20.04 (одна из поддерживаемых операционных систем в Hetzner). Я столкнулся с некоторыми проблемами (особенно после установки) при настройке ZFS в своих системах, поэтому я хотел рассказать, как я это сделал. Этот пост представляет собой подробное объяснение моих сценариев Ansible, которые устанавливают ZFS. Прежде чем вы узнаете, стоит ли переходить на ZFS (или любую файловую систему), вы, вероятно, захотите перейти на RTFM. У ZFS много тонкостей и я конечно не спец в этом, но знать как и почему, а также терминологию и даже историю проекта очень важно. Несколько ссылок для начала: Наряду с ZFS (которая, вероятно, самая неизвестная здесь величина) вы, вероятно, захотите познакомиться с Ubuntu и общим системным администрированием. Это довольно обширная область, но вот несколько ссылок: Это почти само собой разумеется, но если вы еще не очень хорошо знакомы с системным администрированием Linux, вам, вероятно, не следует пытаться это сделать. Я лично предпочитаю иметь свои диски в несколько индивидуальной конфигурации âÂÂ У меня есть настройка RAID1 (зеркалирование) через mdraid вместе с разделами на каждом диске, которыми я могу управлять ZFS. В идеале я бы отдал ZFS целые диски, но разделы тоже работают. установка ОС( installimage), который включает установку диска, запускается из режима восстановления Hetzner и может управляться файлом, подобным следующему: # Объявления дисков, используемые installimage DRIVE0 /dev/nvme0n1 DRIVE1 /dev/nvme1n1 # Включить программный RAID (для диска ОС) SWRAID 1 SWRAIDLEVEL 1 # Загрузчик (обычно grub) BOOTLOADER grub # Имя хоста машины HOSTNAME machine01 # Раздел конфигурация PART swap swap 32G PART /boot ext4 1G PART / ext4 128G # Этот последний раздел будет создан (удален)& позже воссоздан как ZFS) ЧАСТЬ /root-disk-remaining ext4 all # Вы можете указать образы, которые использует Hetzner, обратившись к их общему сетевому ресурсу IMAGE /root/.oldroot/nfs/images/Ubuntu-2004-focal-64-minimal.tar. гз Это, очевидно, довольно специфично для Hetzner, но делайте все, что вам нужно, в своих собственных системах, чтобы иметь разделы / диски, доступные для использования ZFS. zfsutils-linuxand zfs-zed В Ansible YAML шаг выглядит так: - имя: Хранить все исходящие пакеты, связанные с zfs. ansible.builtin.shell: | apt-mark удерживать zfsutils-linux apt-mark удерживать zfs-zed На момент написания этого поста версия zfs-linux в Ubuntu Focal — 0.8.3. Поскольку наша цель здесь — установить и продолжать использовать более новую версию, мы хотим убедиться, что любая Обновления apt не заменяют нашу установленную версию более старой версией. Пока мы здесь, давайте также очистим пакеты. - имя: Очистить исходные пакеты ZFS (ubuntu), если они установлены ignore_errors: yes ansible.builtin.command: | apt purge --allow-change-hold-packages zfsutils-linux zfs-zed Вы можете прочитать документы на способный увидеть, что purge делает â это похоже на remove, но также удаляет файлы конфигурации, если они есть. Вы можете установить зависимости для OpenZFS следующим образом: - name: Требования к установке для сборки ZFS ansible.builtin.apt: name:packagesupdate_cache: yes state: present vars: packages: - build-essential - autoconf - automake - libtool - gawk - Alien - fakeroot - dkms - libblkid-dev - uuid -dev - libudev-dev - libssl-dev - zlib1g-dev - libaio-dev - libattr1-dev - libelf-dev # Строка ниже требует вывода `uname -r`, например "5.16.5-arch1- 1"для моей арочной системы # поэтому строка будет иметь вид "linux-headers-5.16.5-arch1-1"(конечно, не подходит для Ubuntu, но в качестве примера) - linux-headers uname_r.stdout }} - python3 - python3-dev - python3-setuptools - python3-cffi - libffi-dev - упаковка python3 - git - libcurl4-openssl-dev Может быть несколько зависимостей, которые не являются строго необходимыми, но почти все они должны быть обязательными. /опт/zfs Вот ЗФС: - имя: Создать /opt/zfs ansible.builtin.file: путь: /opt/zfs состояние: каталог Если вы хотите скачать ZFS: - name: Download zfs ansible.builtin.get_url: url: "httpsgithub.com/openzfs/zfs/releases/download/zfs _zfs_version zfs _zfs_version tar.gz"контрольная сумма: _zfs_tarball_sha256_checksummode: 0755 dest: "/opt/zfs/zfs _zfs_version tar.gz".gz"Здесь замена {{ _zfs_версия }} ( 2.1.1. Вы также захотите выполнить загрузку самостоятельно и собрать/сгенерировать контрольную сумму для использования. Никогда не скачивайте из Интернета то, что не должно изменяться без контрольной суммы! {{является синтаксисом шаблонов Ansible) И если вы хотите скопировать его с компьютера, на котором работает Ansible (это то, что я делаю): - имя: Копировать пакет zfs (избегайте ограничения скорости) Независимо от того, как вы решите получить исходный код, вам, конечно же, нужно его разархивировать: - name: Unzip zfs code ansible.builtin.unarchive: src: "/opt/zfs/zfs _zfs_version tar.gz"dest: "/opt/zfs"remote_src: yes Чтобы начать процесс сборки: - имя: Настройка и сборка ZFS ansible.builtin.shell: | ./autogen../configure make clean make -j args: chdir: "/opt/zfs/zfs _zfs_version Если вы знакомы с созданием вещей из исходного кода, различные проекты используют этот общий набор инструментов (какой-то вариант создавать, автоматически генерировать и настраивать сценарии). Как и следовало ожидать, это может занять некоторое время, поэтому дайте ему некоторое время. После сборки ZFS - имя: Установить ZFS ansible.builtin.shell: | make install args: chdir: "/opt/zfs/zfs _zfs_version Обычно вы думаете, что мы закончим *прямо здесь*, но этот пост существует, потому что этого обычно недостаточно! Давайте нажмем вперед. Сначала принудительно выгрузите модули ZFS, если они работают: - имя: Принудительно выгрузить модуль (ss) ZFS ansible.builtin.shell: | ./scripts/zfs.-u аргументы: chdir: "/opt/zfs/zfs _zfs_version Было бы идеально *не* запускать какие-либо рабочие нагрузки, когда это происходит, как вы могли ожидать. Пока модули выгружены, мы можем установить некоторые помощники: - имя: Послеустановочные помощники ZFS ansible.builtin.shell: | ./scripts/zfs-helpers.-i args: chdir: "/opt/zfs/zfs _zfs_version После установки помощников давайте принудительно перезагрузим модуль ZFS: - имя: Принудительная перезагрузка модуля ZFS ansible.builtin.shell: | ./scripts/zfs.args: chdir: "/opt/zfs/zfs _zfs_version Я нашел это здание и с помощью Модуль deb (формат, используемый Debian для установки пакетов) также помог добиться того, чтобы установка закрепилась и не была заменена стандартными пакетами Ubuntu. Сначала соберите deb-пакет ZFS из исходного кода: - имя: Сборка ZFS deb-пакета ansible.builtin.shell: | make deb args: chdir: "/opt/zfs/zfs _zfs_version А затем установите его - имя: Установка пакетов ZFS deb ansible.builtin.shell: | да | dpkg -i --force-overwrite deb apt install -f -y аргументы deb: chdir: "/opt/zfs/zfs _zfs_version Теоретически этот шаг *не должен* быть необходимым (или должен использоваться отдельно), так как мы уже запустили процесс установки, но я обнаружил, что при выполнении того или иного действия я У меня возникали ситуации, когда перезапуск приводил к использованию более старой версии ZFS (несмотря на apt purge) — особенно на уровне ядра. модзонд Если вы хотите включить модуль ZFS, который был установлен сразу, используйте модзонд: - имя: Блок модуля Modprobe zfs: - имя: Установить модуль ядра zfs community.general.modprobe: имя: Состояние zfs: присутствует Установка ZFS означает установку модуля ядра, но, поскольку мы не совсем встроили его с помощью поддержки динамического модуля ядра, нам нужно разрешить использование локально установленных модулей ядра: - имя: убедитесь, что дополнительные находятся впереди. Это гарантирует, что файл, управляющий путем поиска модуля ядра, В файле /etc/modules-load.d/modules.conf есть строка с искать дополнительные обновления, указанные. Обычно есть по крайней мере одна строка, начинающаяся с поиск, и мы хотим убедиться, расположение дополнительных модулей ищется в начале процесса. Мы хотим установить через подсистему DKMS: - имя: dkms install zfs ansible.builtin.shell: | dkms install zfs _zfs_version }} args: chdir: "/opt/zfs/zfs _zfs_version На этом этапе вы сможете проверить текущую активную версию ZFS, и она должна показать вам что-то вроде этого: root@machine01 ~ # версия zfs zfs-2.1.1-1 zfs-kmod-2.1.1-1 Есть несколько модулей SystemD (как обычно, у Digital Ocean есть отличная документация), которые созданы, но их необходимо включить для использования ZFS: - имя: убедитесь, что блоки systemd, связанные с zfs, включены. - zfs-share.service - zfs-zed.service - zfs-volume-wait.service - zfs.target Зацикливаемые переменные аналогичны запуску systemd start and systemd enable . The security-minded reader at home is no doubt cringing into the atmosphere by now, but making sure my kernel upgrades are manual is the only way I’ve found to ensure that the installed version of ZFS was not disabled/unexpectedly altered/broken by kernel upgrades: - name: Hold all kernel upgrades to prevent custom built ZFS from doing fallback ansible.builtin.shell: | apt-mark hold linux-headers-generic apt-mark hold linux-image-generic apt-mark hold {{ uname_r.stdout }} # you'll need that `uname -r` output again here I personally prefer to hold back any kernel upgrades and instead perform them at machine setup rather than during operation. DKMS *should* ensure that with the building of any new kernel the ZFS code is rebuilt but it’s been flaky in the past so I find it hard to trust. Along with all the kernel changes we’ve made so far (good and questionable), one thing we’ll want to do is add the configuration necessary to ensure the kernel module is loaded: - name: Ensure zfs kernel module comes up with next restart tags: [ "zfs:post-install:config" ] block: - name: Add zfs module load file ansible.builtin.template: src: templates/kernel-modules/zfs.conf.j2 dest: /etc/modules-load.d/zfs.conf owner: root group: root mode: 0644 Now that we have ZFS installed (restart once to check I’m going to leave the rest of the cluster setup to you. There are a *lot* of ways to use ZFS and setup zpools (RAID0/1/5/Z Actually using ZFS properly is out of scope (I can only hope I’ve covered *installing* it properly, at least), but please refer to the usual manuals there to set up your ZFS pool and storage requirements. Trying to cover even the basics of how to setup ZFS for your drives once it’s been installed is certainly a lot of reading so we’ll leave it there for today. This is the part where you “draw the rest of the owl” (sorry). txg_timeout While experimenting with some write modes for Postgres on ZFS, I looked into an optimization modes that involved reducing the txg_timeout from it’s default of 5 seconds to 1 second. While I won’t get into the tradeoffs implied by that move (please read my post on Postgres + ZFS), here is the setup that I’ve used for modifying txg_timeout per-machine (some machines might use the optimization some might not): - name: Add systemd unit to support txg timeout customization tags: [ "zfs:post-install:config" ] block: - name: Set current ZFS txg_timeout (to 1) ansible.builtin.shell: | echo 1 > /sys/module/zfs/parameters/zfs_txg_timeout - name: install zfs-config-txg-timeout service ansible.builtin.template: src: templates/zfs-config-txg-timeout.service.j2 dest: /etc/systemd/system/zfs-config-txg-timeout.service owner: root group: root mode: 0644 - name: start & enable zfs-config-txg-timeout service ansible.builtin.systemd: name: zfs-config-txg-timeout.service state: started enabled: yes daemon_reload: yes The template that goes with that looks like this: [Unit] Description=Set ZFS txg_timeout After=zfs.target ConditionPathIsDirectory=/sys/module/zfs [Service] Type=oneshot RemainAfterExit=yes ExecStart=/usr/bin/bash -c 'echo {{ _zfs_txg_timeout_seconds | default(zfs_txg_timeout_seconds) }} > /sys/module/zfs/parameters/zfs_txg_timeout' # Not needed since we want it to always be whatever the setting is # ExecStop=/usr/bin/bash -c 'echo {{ _zfs_txg_timeout_seconds | default(zfs_txg_timeout_seconds) }} > /sys/module/zfs/parameters/zfs_txg_timeout' [Install] WantedBy=multi-user.target Obviously you’ll want to define {{ _zfs_txg_timeout_seconds }} and the zfs_txg_timeout_seconds. In general, this will make sure that the txg_timeout is set to what you want it to be upon startup after ZFS has started. Wasn’t that easy? If you’re answering no, it wasn’t easy for me to figure out either! I spent a lot of time wondering why DKMS wasn’t working properly (which is why the hack of preventing kernel upgrades is still included) and dealing with other issues. The OpenZFS documentation is pretty great but seems to be missing a guide somewhat like this, so hopefully this post will save people some time going forward when trying to experiment with new ZFS setups on Hetzner. Another thing I’m hoping for with this post is to be corrected – if you see something glaringly wrong in my setup, please reach out. The more people can use low cost infrastructure providers like Hetzner, the more cool software we get and the more interesting/innovative products can be built. To that end I’m working on NimbusWS (I’m behind on this but should launch by end of Q1 2022) – if you’re interested please sign up for the service and kick the tires (free tier usage will be available).