Pool, vdev, snapshot, scrub, send/receive e "perché il RAID hardware è morto". Il filesystem che fa tutto, tranne il caffè.
"Ho un RAID 5 con controller hardware, i miei dati sono al sicuro" — famose last words di chi non ha mai verificato un backup.
Un filesystem, un volume manager, un RAID controller e un sistema di backup — tutto in uno. Il coltellino svizzero dello storage che i tuoi dischi si meritano.
Un filesystem tradizionale (ext4, XFS) è un catalogo cartaceo: sa dove stanno i libri, ma non se qualcuno ha strappato delle pagine. ZFS è un bibliotecario paranoico: sa dove sta ogni libro, controlla che ogni pagina sia intatta ad ogni lettura (checksum), tiene copie di sicurezza automatiche (ridondanza), e quando trovi un libro corrotto te lo ripara da solo usando la copia buona. Se poi vuoi fotografare lo stato esatto della biblioteca in un istante (snapshot), lo fa in un microsecondo senza spostare nemmeno un libro.
Creato da Sun Microsystems nel 2005 per Solaris. Dopo l'acquisizione Oracle, il progetto open source si è spostato su OpenZFS, che oggi gira su Linux, FreeBSD, macOS (sperimentale). È il filesystem di default su FreeBSD e il cuore di TrueNAS. Su Linux è un modulo kernel (DKMS) perché la licenza CDDL non è compatibile con la GPL — dramma legale, non tecnico.
Ogni blocco ha un hash. Bit rot? Lo rileva. Silent data corruption? Lo corregge (se hai ridondanza).
Copy-on-write: lo snapshot non copia nulla, segna solo "da qui non toccare". Zero costo iniziale.
RAIDZ1/2/3 senza controller hardware. Niente write hole. Niente batteria BBU da cambiare.
LZ4 quasi gratis in termini di CPU. Risparmi spazio E guadagni throughput (meno I/O).
Backup incrementali a livello di blocco. Tipo rsync ma 10x più veloce e consistente.
128 bit. Il limite teorico è 256 quadrilioni di zettabyte. Non li raggiungerai. Mai.
ZFS VUOLE vedere i dischi raw. Il controller RAID hardware è il nemico: nasconde errori e aggiunge latenza.
Con mirror o RAIDZ: legge un blocco corrotto, lo ricostruisce dalla copia buona, lo riscrive. Da solo.
zpool destroy digitato dal collega, incendio nel datacenter, o controller difettoso che scrive spazzatura su tutti i dischi contemporaneamente. Serve sempre un backup offsite. Sempre.
Pool, vdev, dataset, zvol. Se non capisci questi quattro concetti, il resto è rumore.
| Concetto | Cos'è | Analogia |
|---|---|---|
| Pool (zpool) | Il contenitore di storage più alto. Raggruppa uno o più vdev. È il tuo "volume di storage". | L'armadio intero |
| vdev | Virtual device: un gruppo di dischi con una policy di ridondanza (mirror, RAIDZ, single). Il pool è fatto di vdev. | Un ripiano dell'armadio |
| Dataset | Un filesystem all'interno del pool. Ha proprietà proprie (compressione, quota, mountpoint). Ereditabili. | Una cartella con regole proprie |
| Zvol | Un blocco device virtuale dentro il pool. Serve per iSCSI, VM disk, swap. Non è un filesystem, è un disco raw. | Un cassetto chiuso a chiave |
| Snapshot | Foto istantanea e immutabile di un dataset/zvol in un momento preciso. Copy-on-write, costo iniziale zero. | Una polaroid della cartella |
| Clone | Un dataset scrivibile nato da uno snapshot. Condivide i blocchi con l'originale finché non li modifichi. | Una fotocopia su cui puoi scrivere |
| Tipo | Ridondanza | Dischi minimi | Quando usarlo |
|---|---|---|---|
| stripe | Nessuna | 1 | Mai in produzione. Perdi un disco, perdi tutto. Lab/temp. |
| mirror | N-1 dischi possono morire | 2 | Piccoli pool, massima resilienza, metà capacità utile. |
| raidz1 | 1 disco | 3 (meglio 4-5) | Equivalente RAID5. Dischi piccoli, rischio accettabile. |
| raidz2 | 2 dischi | 4 (meglio 6-8) | Equivalente RAID6. Lo standard per dischi grandi. |
| raidz3 | 3 dischi | 5 (meglio 8+) | Paranoia giustificata con dischi >8TB e tempi di resilver lunghi. |
| dRAID | Configurabile | Molti | Pool enormi (>20 dischi). Resilver più veloce. Complessità in più. |
pool: tank state: ONLINE config: NAME STATE tank ONLINE raidz2-0 ONLINE sda ONLINE sdb ONLINE sdc ONLINE sdd ONLINE sde ONLINE sdf ONLINE raidz2-1 ONLINE sdg ONLINE sdh ONLINE sdi ONLINE sdj ONLINE sdk ONLINE sdl ONLINE logs nvme0n1p1 ONLINE cache nvme1n1 ONLINE
Il momento in cui scegli come i tuoi dischi passeranno il resto della loro vita. Niente pressione.
# Mirror semplice (2 dischi)
zpool create tank mirror /dev/sda /dev/sdb
# RAIDZ1 con 4 dischi
zpool create tank raidz1 /dev/sd{a,b,c,d}
# RAIDZ2 con 6 dischi — il setup consigliato per dischi grossi
zpool create tank raidz2 /dev/sd{a,b,c,d,e,f}
# Due vdev mirror (4 dischi totali, simile a RAID10)
zpool create tank mirror /dev/sda /dev/sdb mirror /dev/sdc /dev/sdd
# IMPORTANTE: usa sempre gli ID persistenti, non sdX
zpool create tank mirror \
/dev/disk/by-id/ata-WDC_WD40EFRX-001 \
/dev/disk/by-id/ata-WDC_WD40EFRX-002
/dev/sda, /dev/sdb cambiano ad ogni riavvio a seconda dell'ordine di discovery. Se i dischi si scambiano lettera, ZFS importa il pool con i dischi sbagliati e hai un bel problema. Usa sempre /dev/disk/by-id/ o /dev/disk/by-path/. Sempre.
zpool create \
-o ashift=12 # settori 4K (tutti i dischi moderni)
-o autotrim=on # TRIM automatico per SSD
-O compression=lz4 # compressione per tutti i dataset
-O atime=off # non aggiornare access time (meno I/O inutile)
-O xattr=sa # extended attributes in-inode (Linux)
-O dnodesize=auto # dnode più grandi se serve
-O normalization=formD # normalizzazione Unicode (evita problemi con nomi file)
-O relatime=on # alternativa ad atime=off se servono gli atime
-O mountpoint=/mnt/tank # dove montarlo
tank raidz2 /dev/disk/by-id/{disco1,disco2,disco3,disco4,disco5,disco6}
ashift=12 (settori 4K) è corretto per qualsiasi disco moderno. Se sbagli e metti 9 (512 byte), le performance saranno disastrose e non puoi cambiarlo senza ricreare il pool. Nel dubbio, 12. Sempre 12.
# stato del pool
zpool status tank
zpool status -v # verbose, mostra errori per disco
# spazio usato
zpool list
# I/O in tempo reale (tipo iostat)
zpool iostat tank 2 # refresh ogni 2 secondi
# cronologia operazioni
zpool history tank
# esportare (smonta tutto, prepara per spostamento)
zpool export tank
# importare (su questa macchina o un'altra)
zpool import # mostra pool disponibili
zpool import tank # importa "tank"
# distruggere (IRREVERSIBILE)
zpool destroy tank # addio dati, è stato bello
I dataset sono il vero potere di ZFS. Ogni dataset ha le sue regole, le eredita dal padre, e puoi avere una gerarchia profonda quanto vuoi.
# crea un dataset
zfs create tank/documenti
zfs create tank/vm
zfs create tank/backup
# dataset con proprietà specifiche
zfs create -o compression=zstd -o quota=500G tank/media
# gerarchia (il figlio eredita le proprietà del padre)
zfs create tank/vm/proxmox
zfs create tank/vm/docker
# elenca tutti i dataset
zfs list
zfs list -r tank # ricorsivo sotto "tank"
# distruggere
zfs destroy tank/temp # se ha snapshot, aggiungere -r
| Proprietà | Valore | Note |
|---|---|---|
compression | lz4 o zstd | LZ4: velocissimo, buon rapporto. ZSTD: migliore rapporto, più CPU. Attivalo sempre. |
quota | es. 500G | Limite massimo di spazio per il dataset (figli inclusi). |
reservation | es. 100G | Spazio garantito. Nessun altro dataset può rubarlo. |
recordsize | 128K (default) | Dimensione blocco. Database: 8K-16K. Media/VM: 1M. Non toccare se non sai cosa fai. |
atime | off | Non aggiornare l'access time ad ogni lettura. Meno I/O, più performance. |
dedup | off | Lascialo OFF. Mangia RAM come un buco nero (5GB per TB di dati). Vedi sezione dedicata. |
snapdir | visible | Rende .zfs/snapshot visibile nel filesystem. Utile per ripristini rapidi. |
mountpoint | path o none | Dove montare il dataset. none per zvol o dataset che non vuoi montare. |
# cambia compressione
zfs set compression=zstd tank/backup
# imposta quota
zfs set quota=1T tank/media
# vedi tutte le proprietà di un dataset
zfs get all tank/documenti
# vedi solo compressione e quanto sta risparmiando
zfs get compressratio,compression tank/documenti
# eredita dal padre (rimuove override locale)
zfs inherit compression tank/documenti
compression=zstd su un dataset che aveva lz4, i vecchi blocchi restano in LZ4. Solo i nuovi verranno compressi con ZSTD. Per ricomprimere tutto devi copiare i dati fuori e dentro (o fare send/receive). Di solito non vale la pena.
La feature che da sola giustifica l'uso di ZFS. "Oops, ho cancellato tutto" diventa "tranquillo, faccio rollback".
ZFS è copy-on-write (CoW): quando modifichi un blocco, ne scrive uno nuovo e aggiorna il puntatore. Il vecchio blocco resta lì. Uno snapshot dice semplicemente: "non liberare i vecchi blocchi da questo punto in poi". Costo iniziale: praticamente zero. Costo nel tempo: proporzionale a quanto cambi dopo lo snapshot.
# crea snapshot
zfs snapshot tank/documenti@prima-del-casino
# snapshot ricorsivo (tutti i figli)
zfs snapshot -r tank@backup-2024-01-15
# elenca snapshot
zfs list -t snapshot
zfs list -t snapshot -r tank/documenti # solo di un dataset
# quanto spazio occupa (dati unici non più nel dataset attivo)
zfs list -t snapshot -o name,used,refer
# accedi ai file dello snapshot (senza rollback!)
ls /mnt/tank/documenti/.zfs/snapshot/prima-del-casino/
# ripristina un singolo file
cp /mnt/tank/documenti/.zfs/snapshot/prima-del-casino/report.pdf \
/mnt/tank/documenti/report.pdf
# rollback — riporta il dataset a quello stato
zfs rollback tank/documenti@prima-del-casino
# rollback a snapshot non più recente (distrugge quelli intermedi)
zfs rollback -r tank/documenti@snapshot-vecchio
# diff tra snapshot e stato attuale
zfs diff tank/documenti@prima-del-casino
# distruggere uno snapshot
zfs destroy tank/documenti@prima-del-casino
-r). E tutti i dati scritti dopo A spariscono. Per recuperare un singolo file, meglio accedere via .zfs/snapshot/ o fare un clone.
# /etc/sanoid/sanoid.conf
[tank/documenti]
use_template = production
recursive = yes
[template_production]
hourly = 24
daily = 30
monthly = 12
yearly = 2
autosnap = yes
autoprune = yes
# Sanoid gira via cron/systemd timer
# ogni 15 minuti crea snapshot e pulisce i vecchi
Replica incrementale a livello di blocco, via pipe, via SSH, verso un altro pool locale o remoto. Il backup che rsync sogna di essere.
# send completo di uno snapshot (prima volta)
zfs send tank/documenti@snap1 | zfs receive backup/documenti
# send incrementale (solo le differenze tra snap1 e snap2)
zfs send -i tank/documenti@snap1 tank/documenti@snap2 | \
zfs receive backup/documenti
# send ricorsivo con tutte le proprietà
zfs send -R tank@snap1 | zfs receive -F backup
# send via SSH verso un'altra macchina
zfs send -i tank/documenti@snap1 tank/documenti@snap2 | \
ssh backupserver zfs receive backup/documenti
# send compresso (risparmia banda su SSH)
zfs send -i @snap1 tank/documenti@snap2 | \
zstd | ssh backupserver 'zstd -d | zfs receive backup/documenti'
# send con progress bar (perché aspettare alla cieca è ansia)
zfs send -v tank/documenti@snap1 | pv | zfs receive backup/documenti
syncoid (parte di sanoid) automatizza tutto il ciclo send/receive: crea lo snapshot, fa il send incrementale, pulisce. Un comando.
# replica locale
syncoid tank/documenti backup/documenti
# replica remota via SSH
syncoid tank/documenti root@backupserver:backup/documenti
# replica ricorsiva di tutto il pool
syncoid -r tank root@backupserver:backup
# in un cron job (ogni ora)
# 0 * * * * /usr/sbin/syncoid -r tank root@backup:tank-replica
zfs send lavora a livello di blocchi: sa già cosa è cambiato, non deve fare nessun confronto. 10 milioni di file, 50MB cambiati? rsync: 20 minuti di scanning. zfs send: 2 secondi.
La compressione è gratis e va sempre attivata. La dedup è una trappola che divora RAM. Non confonderle.
ZFS comprime ogni blocco al volo. Se il blocco compresso non è più piccolo dell'originale, lo salva non compresso. Nessun rischio, solo vantaggi.
| Algoritmo | Velocità | Rapporto | Quando |
|---|---|---|---|
| lz4 | Fulmineo | Buono | Default consigliato. Overhead quasi zero. |
| zstd | Veloce | Ottimo | Backup, archivi, dati freddi. |
| zstd-fast | Fulmineo | Buono | Alternativa a LZ4 su hardware recente. |
| gzip-9 | Lento | Massimo | Archivi che non rileggi mai. Forse. |
| off | — | — | Mai. Serio, mai. A meno che siano dati già compressi (video, immagini). |
La dedup elimina blocchi duplicati nel pool. Sembra magico. Non lo è.
# rapporto di compressione globale
zfs get compressratio tank
# spazio logico (prima) vs fisico (dopo compressione)
zfs list -o name,logicalused,used tank/documenti
# simulare la dedup PRIMA di attivarla
zdb -S tank
# se "dedup ratio" < 2.0, non vale la pena. Mai.
Lo scrub è il check-up annuale che i tuoi dischi non sanno di aver bisogno. Il resilver è l'operazione a cuore aperto quando un disco muore.
Legge ogni blocco del pool, verifica il checksum, e se trova corruzione la ripara automaticamente usando la copia ridondante (mirror/RAIDZ). Se non hai ridondanza, ti dice almeno quali file sono corrotti.
# lancia scrub
zpool scrub tank
# stato dello scrub
zpool status tank
# annulla scrub in corso
zpool scrub -s tank
Frequenza consigliata: ogni 1-2 settimane per dischi in uso. Mensile per archivi freddi. Mettilo in cron e dimenticatene.
Quando sostituisci un disco guasto, ZFS ricostruisce i dati sul nuovo disco. È come il rebuild di un RAID, ma più intelligente: ricostruisce solo i blocchi effettivamente usati, non l'intero disco.
# trova il disco guasto
zpool status tank
# cerchi la riga con DEGRADED o FAULTED
# sostituisci con il nuovo disco
zpool replace tank \
/dev/disk/by-id/ata-VECCHIO_MORTO \
/dev/disk/by-id/ata-NUOVO_BELLO
# monitora il resilver
zpool status tank
# vedrai: "scan: resilver in progress, XX% done"
# /etc/cron.d/zfs-scrub
# scrub ogni domenica alle 2 di notte
0 2 * * 0 root /sbin/zpool scrub tank
# su systemd (molte distro hanno già il timer)
systemctl enable zfs-scrub-weekly@tank.timer
systemctl start zfs-scrub-weekly@tank.timer
Gli accessori che rendono ZFS più veloce. Utili se sai cosa fai, inutili o dannosi se non lo sai.
Separate Log device. Accelera le scritte sincrone (NFS, database, ESXi). Non accelera le scritte asincrone (la maggior parte). Richiede SSD con capacitor/PLP.
Cache di lettura su SSD. Estende l'ARC (cache in RAM). Utile solo se la RAM non basta e le letture sono ripetitive. Mangia 70 byte di RAM per blocco cachato.
Un vdev SSD per metadati e piccoli file. Accelera ls, find, listing di directory. Game changer con molti file piccoli. Va in mirror!
# aggiungere un SLOG (ZIL separato) — DEVE avere PLP
zpool add tank log mirror /dev/nvme0n1p1 /dev/nvme1n1p1
# aggiungere L2ARC (cache lettura)
zpool add tank cache /dev/nvme2n1
# aggiungere special vdev (metadati) — SEMPRE in mirror
zpool add tank special mirror /dev/nvme3n1 /dev/nvme4n1
# verificare la topologia
zpool status tank
ls ci mette un'eternità.Il setup pratico: installazione, import automatico, kernel update che rompono tutto, e la RAM che sparisce (spoiler: è l'ARC).
# Debian / Ubuntu
apt install zfsutils-linux
# RHEL / Rocky / Alma (serve EPEL + ZFS repo)
dnf install https://zfsonlinux.org/epel/zfs-release-2-3.el9.noarch.rpm
dnf install zfs
modprobe zfs
# Arch (AUR o archzfs repo)
pacman -S zfs-dkms zfs-utils
# verifica che il modulo sia caricato
lsmod | grep zfs
zfs version
# abilita import e mount automatico
systemctl enable zfs-import-cache.service
systemctl enable zfs-mount.service
systemctl enable zfs-import.target
systemctl enable zfs.target
# aggiorna la cache (dopo aver importato manualmente)
zpool set cachefile=/etc/zfs/zpool.cache tank
ZFS usa la RAM libera come cache di lettura (ARC — Adaptive Replacement Cache). Il kernel la riporta come "usata" ma è liberabile: se un'app chiede RAM, ZFS la rilascia. Non è un memory leak, è una feature.
# stato ARC
cat /proc/spl/kstat/zfs/arcstats | grep -E 'size|hits|miss'
# oppure in modo leggibile:
arc_summary
# limitare ARC a 8 GB (utile se il server fa anche altro)
# /etc/modprobe.d/zfs.conf
options zfs zfs_arc_max=8589934592
# applica senza reboot
echo 8589934592 > /sys/module/zfs/parameters/zfs_arc_max
# regola base: lascia almeno 1-2 GB liberi per il sistema
# ARC ideale = RAM totale - RAM per app - 2 GB
Le situazioni in cui ti troverai, e dove guardare prima di paniccare.
| Sintomo | Causa probabile |
|---|---|
| "pool DEGRADED" | Un disco ha errori o è morto. zpool status -v per vedere quale. Sostituiscilo con zpool replace. |
| "no space left" ma il pool non è pieno | Gli snapshot occupano spazio. zfs list -t snapshot -o name,used -s used per trovare i colpevoli. Oppure la dedup table mangia metadati. |
| Performance pessime | Pool oltre l'80% di utilizzo: ZFS fatica a trovare blocchi liberi. Oppure ashift sbagliato alla creazione. Oppure ARC troppo piccolo. |
| "cannot import pool" | Dischi cambiati nome (/dev/sdX shuffled). Prova zpool import -d /dev/disk/by-id. Oppure il pool era in uso da un'altra macchina: zpool import -f. |
| RAM tutta usata | È l'ARC. Controlla con arc_summary. Non è un problema, ma se vuoi limitarla: zfs_arc_max. |
| "checksum errors" dopo scrub | Bit rot rilevato. Se hai ridondanza: ZFS ha già corretto. Se single disk: i file sono corrotti, ripristina da backup. |
| Pool non si monta al boot | Modulo ZFS non caricato (DKMS fallito dopo kernel update). Controlla dkms status. Oppure manca il service zfs-import-cache. |
| "resilver in progress" da giorni | Normale per dischi grandi. Un disco da 16 TB con RAIDZ2 può impiegare 2-5 giorni. Non spegnere il server. |
# stato di tutto
zpool status -v
# spazio reale
zpool list -v
zfs list -o name,used,avail,refer,compressratio
# snapshot ordinati per spazio occupato
zfs list -t snapshot -o name,used -s used
# errori disco con SMART
smartctl -a /dev/sda | grep -E 'Reallocated|Current_Pending|Offline_Uncorrectable'
# I/O in tempo reale
zpool iostat -v tank 2
# eventi ZFS recenti
zpool events -v | tail -50
# chi sta usando spazio? (dataset + snapshot)
zfs list -r -o name,used,usedbysnapshots,usedbydataset tank
I comandi che userai davvero e le regole che ti eviteranno notti insonni.
# === POOL ===
zpool create tank mirror sda sdb # crea pool mirror
zpool status # stato
zpool list # spazio
zpool scrub tank # verifica integrità
zpool replace tank old-disk new-disk # sostituisci disco
zpool export tank # smonta per spostare
zpool import tank # monta/importa
# === DATASET ===
zfs create tank/data # crea dataset
zfs list # elenca
zfs set compression=lz4 tank/data # imposta proprietà
zfs get all tank/data # vedi proprietà
# === SNAPSHOT ===
zfs snapshot tank/data@oggi # scatta
zfs list -t snapshot # elenca
zfs rollback tank/data@oggi # torna indietro
zfs destroy tank/data@oggi # elimina
# === SEND/RECEIVE ===
zfs send tank/data@snap | zfs receive backup/data # full
zfs send -i @snap1 tank/data@snap2 | zfs receive backup/data # incr
ashift=12 alla creazione.