Hållbart värd, hanterad F/OSS Objektlagring, Redis, Prometheus på Hetzner, OVH och LeaseWeb **tl;dr - Rad för rad förklaring av mitt ansible-drivna ZFS-installationsskript för användning på HetznerâÂÂs dedikerade hårdvara (Ubuntu 20.04 - âÂÂFocalâÂÂ) â  det är inte perfekt/minimalt, men det fungerar för mig För ett tag sedan började jag använda ZFS på all min dedikerade hårdvara som var värd hos Hetzner för att bråka de bifogade hårddiskarna och SSD:erna. Det finns många val i utrymmet (standard LVM, mdraid btrfs, etc), men jag valde ZFS för dess funktionsuppsättning och ergonomi. Jag kommer inte att tråka ut någon med varför, men en av sakerna jag behövde för att navigera var hur man installerar nyare versioner av ZFS på Ubuntu 20.04 (ett av de operativsystem som stöds hos Hetzner). Jag stötte på några problem (särskilt efter installationen) när jag ställde in ZFS på mina system så jag ville ge en genomgång av hur jag gjorde det. Det här inlägget är en strof för strof förklaring av mina Ansible-skript som installerar ZFS. Innan du vet om ZFS (eller något filsystem) är värt att byta till, kommer du förmodligen att vilja RTFM. ZFS har många ins och jag är verkligen ingen expert på det, men att veta hur och varför, såväl som terminologi och till och med projektets historia är mycket viktigt. Några länkar för att komma igång: Tillsammans med ZFS (som sannolikt är den mest okända kvantiteten här) vill du förmodligen vara bekant med Ubuntu och allmän systemadministration. Det är ett stort område att täcka, men här är några länkar: Det är nästan självklart, men om du inte är så bekant med linux-systemadministration vid det här laget, borde du förmodligen inte försöka detta. Jag personligen väljer att ha mina diskar i en något anpassad konfiguration - jag har RAID1 (spegling) inställning via mdraid, tillsammans med partitioner på varje disk som jag kan ge till ZFS för att hantera. Helst skulle jag ha hela diskar att ge till ZFS men partitioner fungerar också. OS-inställningar ( installimage) som inkluderar diskinstallation körs från HetznerâÂÂs räddningsläge och kan guidas med en fil som följande: # Enhetsdeklarationer som används av installimage DRIVE0 /dev/nvme0n1 DRIVE1 /dev/nvme1n1 # Aktivera Software RAID (för OS-disken) SWRAID 1 SWRAIDLEVEL 1 # Bootloader (vanligtvis grub) BOOTLOADER grub # Maskinvärdnamn HOSTNAME machine01 # Partition konfiguration PART swap swap 32G PART /boot ext4 1G PART / ext4 128G # Den här sista partitionen kommer att göras (rensas& återskapad som ZFS senare) DEL /root-disk-resterande ext4 all # Du kan specificera bilder som Hetzner använder genom att komma åt deras nätverksresurs IMAGE /root/.oldroot/nfs/images/Ubuntu-2004-focal-64-minimal.tar. gz Detta är uppenbarligen ganska Hetzner-specifikt, men gör vad du än behöver göra på dina egna system för att ha partitioner/enheter tillgängliga för ZFS att använda. zfsutils-linuxand zfs-zed I Ansible YAML ser steget ut så här: - namn: Innehåller alla zfs-relaterade uppströmspaket ansible.builtin.shell: | apt-mark hold zfsutils-linux apt-mark hold zfs-zed När det här inlägget skrevs versionen av zfs-linux i Ubuntu Focal är 0.8.3. Eftersom vårt mål här är att installera och fortsätta använda en nyare version vill vi se till att eventuella apt-uppdateringar ersätter inte vår installerade version med en äldre version. Medan vi är här, låt oss rensa paketen också. - namn: Rensa ZFS uppströms (ubuntu)-paket om installerade ignore_errors: yes ansible.builtin.command: | apt purge --allow-change-held-packages zfsutils-linux zfs-zed Du kan läsa dokumenten på benägen att se vad rensa liknar det att ta bort men tar också bort konfigurationsfiler om sådana finns. Du kan installera beroenden för OpenZFS så här: - namn: Installationskrav för att bygga ZFS ansible.builtin.apt: name:packagesupdate_cache: ja tillstånd: present vars: paket: - 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 # Raden nedan kräver utdata av `uname -r`, till exempel "5.16.5-arch1- 1"för mitt arch system # så raden skulle lösas till något i stil med "linux-headers-5.16.5-arch1-1"(inte giltigt på Ubuntu förstås, men som ett exempel) - linux-headers uname_r.stdout }} - python3 - python3-dev - python3-setuptools - python3-cffi - libffi-dev - python3-packaging - git - libcurl4-openssl-dev Det kan finnas några beroenden som inte är strikt nödvändiga men nästan alla av dem bör krävas. /opt/zfs Här är ZFS: - namn: Skapa /opt/zfs ansible.builtin.file: sökväg: /opt/zfs state: katalog Om du vill ladda ner ZFS: - namn: Ladda ner zfs ansible.builtin.get_url: url: "httpsgithub.com/openzfs/zfs/releases/download/zfs _zfs_version zfs _zfs_version tar.gz"checksum:_zfs_tarball_sha256_checksummode/ "tarzf5 dest_: 075/sf5 dest: 07sfsf5 .gz"Här ersättningen {{ _zfs_version }} ( 2.1.1. Du vill också göra nedladdningen själv och samla in/generera en kontrollsumma att använda. Ladda aldrig ner saker från internet som inte ska ändras utan en checsum! {{är Ansible-mallsyntaxen) är Och om du vill kopiera in det från datorn där Ansible körs (så här gör jag): - namn: Kopiera zfs-paketet (undvik hastighetsgräns) ansible.builtin.copy: src: files/zfs/zfs _zfs_version tar.gz"läge: 0755 dest: "/opt/zfs/zfs _zfs_version tar.gz"Oavsett hur du väljer att hämta källan måste du naturligtvis packa upp den: - namn: Packa upp zfs-kod ansible.builtin.archive: src: "/opt/zfs/zfs _zfs_version tar.gz"dest: "/opt/zfs"remote_src: ja Så här startar du byggprocessen: - namn: Installera och bygga ZFS ansible.builtin.shell: | ./autogen../configure make clean make -j args: chdir: "/opt/zfs/zfs _zfs_version Om du är bekant med att bygga saker från källan använder olika projekt denna vanliga verktygsuppsättning (någon variant av skapa, autogenera och konfigurera skript). Som du kanske förväntar dig kan detta ta ett tag så ge det lite tid. Efter att ha byggt ZFS - namn: Installera ZFS ansible.builtin.shell: | gör installera args: chdir: "/opt/zfs/zfs _zfs_version Normalt skulle du tro att vi skulle göras *här*, men det här inlägget finns för att det i allmänhet inte räcker! Låt oss trycka vidare. Tvinga först avlastningen av ZFS-modulerna om de körs: - namn: Tvinga urladdning av ZFS-modul(ss) ansible.builtin.shell: | ./scripts/zfs.-u args: chdir: "/opt/zfs/zfs _zfs_version Det skulle vara idealiskt att *inte* köra några arbetsbelastningar när detta händer, som du kan förvänta dig. Medan modulerna är urladdade kan vi installera några hjälpare: - namn: Post-Install ZFS Helpers ansible.builtin.shell: | ./scripts/zfs-helpers.-i args: chdir: "/opt/zfs/zfs _zfs_version Efter att hjälparna har installerats, låt oss tvinga omladda ZFS-modulen: - namn: Tvinga omladdning av ZFS-modulen ansible.builtin.shell: | ./scripts/zfs.args: chdir: "/opt/zfs/zfs _zfs_version Jag har hittat den byggnaden och använder den deb-modulen (formatet som används av Debian för paketinstallation) hjälpte också till att få installationen att fastna och inte ersättas av Ubuntu-paketets standardinställningar. Bygg först ZFS deb-paketet från källkoden: - namn: Bygg ZFS deb-paket ansible.builtin.shell: | make deb args: chdir: "/opt/zfs/zfs _zfs_version Och sedan installera det - namn: Installera ZFS deb-paket ansible.builtin.shell: | ja | dpkg -i --force-overwrite deb apt install -f -y deb args: chdir: "/opt/zfs/zfs _zfs_version Teoretiskt *borde inte* detta steg vara nödvändigt (eller bör användas ensamt), eftersom vi redan har kört en installationsprocess, men jag har upptäckt att när jag gör det ena eller det andra  skulle ha situationer där omstarter skulle uppmana en äldre version av ZFS att användas (trots apt purge) â särskilt på kärnnivån. modprobe Om du vill aktivera ZFS-modulen som installerades omedelbart, använd modprobe: - namn: Modprobe zfs modulblock: - namn: Installera zfs kärnmodul community.general.modprobe: namn: zfs tillstånd: närvarande Att installera ZFS innebär att installera en kärnmodul, men eftersom vi inte riktigt har bakat in den via Dynamic Kernel Module Support, måste vi aktivera lokalt installerade kärnmoduler för att användas: - namn: Se till att extra finns framför ansible.builtin.lineinfile: sökväg: /etc/modules-load.d/modules.conf regexp: '^search'rad: "sök efter extra uppdateringar ubuntu inbyggd"tillstånd: närvarande Vad detta gör är att se till att filen som hanterar kärnmodulens sökväg /etc/modules-load.d/modules.conf har en rad i sig som har sök extra uppdateringar som anges. Normalt finns det minst en rad som börjar med söka, och vad vi vill göra är att se till extra modulplatser söks tidigt i processen. Vi vill installera via DKMS-delsystemet: - namn: dkms installera zfs ansible.builtin.shell: | dkms installera zfs _zfs_version }} args: chdir: "/opt/zfs/zfs _zfs_version Vid det här laget bör du kunna kontrollera den aktuella aktiva ZFS-versionen, och den bör visa dig något så här: root@machine01 ~ # zfs version zfs-2.1.1-1 zfs-kmod-2.1.1-1 Det finns några SystemD-enheter (som vanligt har Digital Ocean några fantastiska dokument) som skapas men som måste aktiveras för att ZFS ska kunna använda: - namn: Se till att zfs-relaterade systemd-enheter är aktiverade block: - ansible.builtin.systemd: name:itemstate: started enabled: yes loop: - zfs-import-cache.service - zfs-import.target - zfs-mount.service - zfs-share.service - zfs-zed.service - zfs-volume-wait.service - zfs.target Variablerna som loopas över liknar körning 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).