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).