ZFS na Proxmoxie — projekt i optymalizacja pul

ZFS w Proxmox krok po kroku: mirror czy RAIDZ, ashift, kompresja, volblocksize, limit ARC (zfs_arc_max), snapshoty z Sanoid i alerty o awariach dysków.

System stoi na mirrorze rpool utworzonym przez instalator. Maszyny wirtualne dostaną osobną pulę tank — dzięki temu reinstalacja Proxmoxa nie dotyka danych, a pulę można w razie potrzeby przenieść na inny serwer jednym poleceniem zpool import.

Większość parametrów ZFS ustawia się raz, przy tworzeniu puli lub datasetu. Część z nich (ashift, układ vdevów) nie da się zmienić później bez przeniesienia danych. Dlatego najpierw projekt, potem komendy.

ZFS czy LVM w Proxmoxie?

Instalator Proxmoxa domyślnie proponuje ext4 na LVM, a maszyny wirtualne trafiają wtedy na LVM-thin. To lżejsze rozwiązanie — mniej RAM-u i brak narzutu na sumy kontrolne. ZFS daje w zamian:

  • sumy kontrolne wykrywające ciche uszkodzenia danych i samonaprawianie na mirrorze,
  • natychmiastowe snapshoty i wbudowaną w Proxmoxa replikację maszyn między węzłami klastra (działa tylko na ZFS),
  • kompresję w locie, która zwykle przyspiesza dyski zamiast je spowalniać.

Na serwerze z pamięcią ECC i kilkoma dyskami wybór jest prosty: ZFS. LVM ma sens na maszynie z małą ilością RAM-u albo z jednym dyskiem, gdzie i tak nie ma z czego odtwarzać uszkodzonych danych.

ZFS mirror czy RAIDZ?

Pula ZFS składa się z vdevów (grup dysków), a wydajność losowych operacji rośnie z liczbą vdevów, nie dysków. Dla czterech dysków SSD:

Układ Pojemność użyteczna Losowe IOPS Odporność Rozbudowa
2× mirror (RAID10) 50% ~2 vdevy 1 dysk w każdej parze Dodajesz kolejną parę
RAIDZ1 ~75% ~1 vdev 1 dysk Rozszerzanie RAIDZ (OpenZFS 2.3+), wolne
RAIDZ2 ~50% ~1 vdev 2 dowolne dyski Jak wyżej

Pod maszyny wirtualne wybierz mirrory. Dysk VM to wolumen (zvol) z małym blokiem, a na RAIDZ małe bloki marnują miejsce na parzystość i wyrównanie (padding). W praktyce RAIDZ1 z blokiem 16K daje niewiele więcej miejsca niż mirror, a wyraźnie mniej IOPS.

RAIDZ2 ma sens dla dużych plików: mediów, backupów, archiwum. Wtedy warto zrobić osobną pulę na dyskach HDD, ale R620 z zatokami 2,5” do tego nie służy.

Tworzenie puli

Zawsze adresuj dyski przez /dev/disk/by-id/. Nazwy sda, sdb mogą zamienić się miejscami po restarcie:

Okno terminala
ls -l /dev/disk/by-id/ | grep -v -- -part | grep -E 'ata-|scsi-|nvme-'

Utwórz pulę z dwóch mirrorów. Poniższe identyfikatory zamień na te z wyniku polecenia powyżej:

Pula tank: 2× mirror
zpool create \
-o ashift=12 \
-o autotrim=on \
-O compression=lz4 \
-O atime=off \
-O xattr=sa \
-O acltype=posixacl \
-O dnodesize=auto \
tank \
mirror /dev/disk/by-id/ata-SAMSUNG_MZ7L3960HCJR-00A07_S6KMNA0T100001 /dev/disk/by-id/ata-SAMSUNG_MZ7L3960HCJR-00A07_S6KMNA0T100002 \
mirror /dev/disk/by-id/ata-SAMSUNG_MZ7L3960HCJR-00A07_S6KMNA0T100003 /dev/disk/by-id/ata-SAMSUNG_MZ7L3960HCJR-00A07_S6KMNA0T100004
zpool status tank

Co oznaczają te opcje:

  • ashift=12 — bloki 4 KiB. Dyski często deklarują sektory 512 B, choć fizycznie zapisują 4 KiB. Za małe ashift zmusza dysk do cyklu odczyt–modyfikacja–zapis przy każdym małym zapisie, a tej wartości nie da się zmienić po utworzeniu vdeva.
  • autotrim=on — ZFS na bieżąco informuje SSD o zwolnionych blokach.
  • compression=lz4 — kompresja jest praktycznie darmowa dla CPU, a zwykle przyspiesza I/O, bo na dysk trafia mniej danych. Włącz ją zawsze.
  • atime=off — brak zapisu przy każdym odczycie pliku.
  • xattr=sa, acltype=posixacl, dnodesize=auto — wydajne rozszerzone atrybuty. Mają znaczenie dla kontenerów LXC i udziałów Samba.

Datasety i storage w Proxmoxie

Zamiast wrzucać wszystko do korzenia puli, podziel ją na datasety — każdy może mieć własne parametry, limity i harmonogram snapshotów:

Struktura datasetów
# Dyski maszyn wirtualnych i kontenerów LXC
zfs create tank/vm
# Kopie zapasowe vzdump — duże, sekwencyjne pliki
zfs create -o recordsize=1M -o compression=zstd tank/backup
# Obrazy ISO i szablony
zfs create -o recordsize=1M tank/iso

Dodaj je jako magazyny Proxmoxa:

Rejestracja storage w Proxmox
# Dyski VM (zvol) i rootfs kontenerów — thin provisioning, blok 16K
pvesm add zfspool tank-vm --pool tank/vm --content images,rootdir --sparse 1 --blocksize 16k
# Backupy i ISO jako zwykłe katalogi
pvesm add dir tank-backup --path /tank/backup --content backup --is_mountpoint yes
pvesm add dir tank-iso --path /tank/iso --content iso,vztmpl --is_mountpoint yes
pvesm status

--blocksize 16k to volblocksize nowo tworzonych dysków VM. 16K to domyślna wartość w nowszych Proxmoxach i rozsądny kompromis dla systemów plików gości. Dla maszyny z bazą danych o stronie 8K (PostgreSQL) możesz utworzyć osobny storage z 8k. Wartość obowiązuje tylko dla nowych dysków.

--is_mountpoint yes chroni przed zapisem do pustego katalogu, gdy pula się nie zaimportuje. Proxmox po prostu oznaczy storage jako niedostępny.

Limit ARC (zfs_arc_max): ile RAM-u dla ZFS

ARC to cache ZFS w pamięci RAM. Im większy, tym więcej odczytów obsłużonych bez dotykania dysków — ale każdy gigabajt ARC to gigabajt mniej dla maszyn wirtualnych. ARC oddaje pamięć pod presją, jednak robi to z opóźnieniem, a maszyny z dużym przydziałem RAM-u mogą w tym czasie nie wystartować.

Praktyczna reguła dla puli SSD: 2 GiB + 1 GiB na każdy TiB danych, z górną granicą na tyle, żeby wszystkie maszyny zmieściły się w pamięci jednocześnie. Dla serwera ze 128 GiB i pulą 2 TiB to np. 8 GiB:

Stały limit ARC — 8 GiB
# Wartość w bajtach: 8 × 1024³
echo "options zfs zfs_arc_max=$((8 * 1024**3))" > /etc/modprobe.d/zfs.conf
# Root na ZFS — limit musi trafić do initramfs
update-initramfs -u -k all
# Zastosuj od razu, bez restartu
echo $((8 * 1024**3)) > /sys/module/zfs/parameters/zfs_arc_max

Skuteczność cache sprawdzisz poleceniem:

Okno terminala
arc_summary -s arc | head -30
arcstat 5

Kolumna hit% w arcstat stale powyżej 90% oznacza, że ARC jest wystarczający. Jeśli spada poniżej 80% przy normalnej pracy, a masz wolną pamięć — zwiększ limit.

SLOG, L2ARC i special vdev — zwykle nie

To najczęściej polecane „przyspieszacze” ZFS, które w domowym labie rzadko pomagają:

  • SLOG przyspiesza tylko zapisy synchroniczne i tylko wtedy, gdy dysk SLOG jest szybszy od dysków puli i ma ochronę zasilania. Przy puli z SSD z PLP zysk jest znikomy.
  • L2ARC to cache odczytu na SSD, ale każdy blok w L2ARC zajmuje miejsce w RAM-ie na nagłówek. Najpierw zwiększ ARC.
  • Special vdev przechowuje metadane i jest krytyczny — jego utrata oznacza utratę całej puli. Ma sens przy dużych pulach HDD, nie SSD.

Snapshoty automatyczne z Sanoid

Snapshot ZFS powstaje w ułamku sekundy i zajmuje miejsce tylko na zmienione później bloki. Sanoid tworzy je według harmonogramu i sam usuwa stare:

Okno terminala
apt install -y sanoid
/etc/sanoid/sanoid.conf
[tank/vm]
use_template = vm
recursive = yes
process_children_only = yes
[tank/backup]
use_template = backup
[template_vm]
frequently = 0
hourly = 24
daily = 14
weekly = 4
monthly = 3
autosnap = yes
autoprune = yes
[template_backup]
hourly = 0
daily = 7
monthly = 1
autosnap = yes
autoprune = yes

Pakiet w Debianie instaluje timer systemd, który uruchamia Sanoida co 15 minut. Sprawdź, czy działa:

Okno terminala
systemctl status sanoid.timer
sanoid --cron --verbose
zfs list -t snapshot -o name,used,creation -s creation | tail

Przywrócenie dysku maszyny wirtualnej do stanu sprzed godziny (maszyna musi być wyłączona):

Okno terminala
# Snapshoty dysku VM 110
zfs list -t snapshot tank/vm/vm-110-disk-0
# Cofnięcie — -r usuwa nowsze snapshoty, zastanów się dwa razy
zfs rollback -r tank/vm/vm-110-disk-0@autosnap_2026-09-08_10:00:01_hourly

Scrub i alerty o awariach

Scrub czyta wszystkie dane i porównuje sumy kontrolne, więc wykrywa ciche uszkodzenia, zanim zabraknie drugiej kopii do naprawy. Pakiet zfsutils-linux w Debianie uruchamia go automatycznie w drugą niedzielę miesiąca (/etc/cron.d/zfsutils-linux). Ręcznie:

Okno terminala
zpool scrub tank
zpool status tank

O zdarzeniach (degradacja puli, błędy odczytu, wynik scrubu) informuje ZED — demon zdarzeń ZFS. W Proxmoxie wystarczy, żeby trafiały do roota:

/etc/zfs/zed.d/zed.rc (fragment)
ZED_EMAIL_ADDR="root"
ZED_NOTIFY_VERBOSE=1
Okno terminala
systemctl restart zfs-zed

Poczta do roota przechodzi przez system powiadomień Proxmoxa. Adres e-mail ustawisz w Datacenter → Permissions → Users → root@pam, a cel powiadomień (SMTP, Gotify, webhook) w Datacenter → Notifications. ZED_NOTIFY_VERBOSE=1 wysyła raport także po udanym scrubie — dzięki temu wiesz, że alerty w ogóle działają.

Szybka diagnostyka

Ściąga
zpool status -x # „all pools are healthy” albo lista problemów
zpool list -v # zajętość i fragmentacja per vdev
zpool iostat -v tank 5 # obciążenie dysków na żywo
zfs get compressratio tank # ile oszczędza kompresja
zfs list -o space -r tank # gdzie znika miejsce (snapshoty!)
smartctl -a /dev/disk/by-id/ata-SAMSUNG_MZ7L3960HCJR-00A07_S6KMNA0T100001 | grep -iE 'wear|reallocated|hours'

Pula ZFS nie powinna przekraczać ~80% zajętości — powyżej tego progu rośnie fragmentacja i wyraźnie spada wydajność zapisu. Ustaw sobie alert albo rezerwację:

Okno terminala
# Rezerwacja 15% pojemności puli „dla nikogo” — zapisy zatrzymają się, zanim będzie za późno
zfs create -o refreservation=$(( $(zpool get -Hp -o value size tank) * 15 / 100 )) tank/rezerwa

Pula jest gotowa. W następnej części postawisz na niej maszynę wirtualną z Dockerem — od szablonu cloud-init do działającego stosu Compose.