Escolha do sistema de ficheiros para servidores de IA: XFS, ZFS, ext4 e porque o Btrfs não está na lista

Se acabou de gastar €40k em GPUs, o sistema de ficheiros onde vai colocar os seus dados merece mais do que cinco minutos de reflexão. O instalador do Ubuntu espalha alegremente ext4 por tudo; para muitas cargas de trabalho de IA essa é a resposta certa, para outras não. Escolha o sistema de ficheiros errado para um volume de dados de 50 TB e perde débito, perde dados ou fica a ver o seu DataLoader parado atrás de um estrangulamento de metadados.

Este artigo trata de que sistema de ficheiros colocar em cada sítio num servidor de IA de classe Kentino (4–8 GPUs, arranque em NVMe, scratch em NVMe, camada lenta de grande capacidade). Ubuntu 22.04 / 24.04, kernel 6.x.

A versão curta

Ponto de montagem Carga de trabalho Sistema de ficheiros Porquê
/ (arranque, raiz) SO, pacotes, registos ext4 Aborrecido, comprovado, recuperável
/scratch (NVMe RAID0) Shards de treino ativos, escritas efémeras XFS Débito, E/S paralela em diretórios
/data (RAID10) Conjuntos de dados de treino, checkpoints de modelos XFS ou ZFS XFS para velocidade bruta; ZFS se quiser snapshots + checksums
/archive (RAID-Z2/6) Execuções concluídas, armazenamento frio de conjuntos de dados ZFS Compressão, checksums, snapshots
/home Pastas dos utilizadores, notebooks ext4 Ficheiros pequenos, pouca contenção

Versão num parágrafo: ext4 para o SO, XFS para o scratch quente, ZFS onde a integridade importa mais do que os últimos 10% de débito, nada de Btrfs em produção.

ext4 — a predefinição aborrecida que normalmente está certa

O ext4 é a predefinição no Ubuntu, Debian, Fedora e RHEL há mais de uma década. Bem conhecido, recuperável com fsck, 16 TB por ficheiro, 1 EB por volume. Nada de entusiasmante — e é esse o objetivo.

Use ext4 na raiz (qualquer live USB do Ubuntu sabe repará-lo; muito poucos sabem importar um zpool), em /home e em repositórios de imagens de VM de pequena a média escala.

Onde o ext4 deixa de ser a escolha certa: E/S paralela em larga escala (o seu journal serializa as atualizações de metadados — vinte workers de DataLoader num único volume ext4 ficam presos no journal muito antes de saturarem o NVMe); ficheiros únicos enormes (são suportados, mas a alocação de extents é menos favorável do que no XFS para ficheiros sequenciais >100 GB — shards de treino, checkpoints, corpora de vídeo); snapshots (nenhum de forma nativa; os snapshots thin do LVM funcionam, mas são desajeitados).

mkfs.ext4 -L root /dev/nvme0n1p2

Se dá por si a fazer afinações exóticas de ext4 para uma carga de trabalho de IA, escolheu o sistema de ficheiros errado.

XFS — a resposta certa para o scratch quente e os dados de treino

O XFS nasceu na SGI como sistema de ficheiros para E/S sequencial de alto débito em grandes arrays. Essa herança encaixa quase na perfeição nas cargas de trabalho de IA: ficheiros grandes (shards de treino, parquet, tars de webdataset, checkpoints), leituras sequenciais, muitos leitores em paralelo e tolerância ao princípio de que "a carga de trabalho regenera os dados".

O que o XFS faz e o ext4 não: grupos de alocação — o volume divide-se em 4–32 grupos independentes que leem e escrevem em paralelo sem um bloqueio global de journal, exatamente o que um DataLoader com vários workers quer; pré-alocação atrasada e especulativa para que as grandes escritas sequenciais obtenham extents contíguos; sem limite prático de tamanho de ficheiro (8 EB); e xfs_growfs para uma extensão fiável numa só linha depois de crescer o RAID.

O que não faz: sem snapshots nativos, sem checksums dos dados dos ficheiros (os metadados estão protegidos por CRC desde 2014), sem encolhimento — só crescimento.

Um mkfs.xfs razoável para um volume de scratch em RAID0 com 8× NVMe e chunk de 64 KiB:

# su = unidade de stripe = tamanho do chunk; sw = número de discos de dados
mkfs.xfs -f -d su=64k,sw=8 -l size=512m -L scratch /dev/md0

# largeio + swalloc: comportamento adequado para E/S >1 MB
# allocsize=1g: pré-aloca de forma agressiva para grandes escritas sequenciais
# noatime: poupa uma escrita por leitura sob carga pesada do DataLoader
mount -o noatime,largeio,swalloc,allocsize=1g /dev/md0 /scratch

noatime e largeio são os que mais contam. O XFS com ficheiros pequenos é aceitável, mas não é o seu forte — para centenas de milhões de imagens <16 KB, reempacote em webdataset / parquet / lmdb em vez de trocar de sistema de ficheiros (ver abaixo).

ZFS — a resposta certa quando a integridade, os snapshots ou a compressão importam

O ZFS é, ao mesmo tempo, sistema de ficheiros, gestor de volumes e camada de RAID por software:

  • Checksums ponta a ponta. O bit rot é detetado na leitura e corrigido silenciosamente havendo redundância. Para um conjunto de dados de 50 TB em discos mecânicos durante dois anos, isto importa; para scratch efémero em NVMe durante seis semanas, não.
  • Snapshots e clones. Atómicos, COW, praticamente gratuitos. Tire um snapshot antes de uma execução, crie um clone, reverta — é este o fluxo de trabalho para o qual o ZFS existe.
  • Compressão integrada. lz4 ativo por predefinição; zstd para melhores rácios. Em texto / JSON / parquet, a compressão normalmente aumenta o débito efetivo, porque o custo de CPU é inferior à E/S poupada.
  • ARC — a sua própria cache de leitura em RAM, separada da page cache do Linux. A famosa armadilha.
  • RAID-Z nativo — sem mdadm.

O que não faz bem para IA: devora RAM (por predefinição o ARC fica com ~50% da memória do sistema — num servidor de 512 GB são 256 GB que o kernel de repente deixa de ter, e os trabalhos de treino que contam com a page cache ou com huge pages levam OOM-kill; limite o ARC); não é o mais rápido em leituras sequenciais brutas (um XFS bem afinado em NVMe RAID0 bate o ZFS em 15–30% — os checksums e o COW custam ciclos reais); ignora o O_DIRECT (toda a E/S passa pelo ARC, por desenho — aceitável para a maioria das cargas de trabalho, incompatibilidade total com o GPUDirect Storage).

Afinação do ARC — o único parâmetro que tem mesmo de definir

Numa instalação nova de Ubuntu + ZFS numa máquina com 512 GB, o ZFS reclama 256 GB para o ARC. Defina zfs_arc_max na instalação, e não depois do primeiro OOM.

# Limitar o ARC a 64 GB. Em bytes, não em GB. 64 * 1024^3 = 68719476736
echo "options zfs zfs_arc_max=68719476736" | sudo tee /etc/modprobe.d/zfs.conf
echo 68719476736 | sudo tee /sys/module/zfs/parameters/zfs_arc_max   # aplicar agora
sudo update-initramfs -u                                             # persistir

Regra prática: limite o ARC a 10–20% da RAM do sistema num servidor de IA. O clássico dimensionamento "2 GiB de base + 1 GiB por TiB" é para máquinas de armazenamento; numa máquina de IA, a maior parte da RAM está reservada para pesos de modelos, ativações e a page cache da framework.

Um layout ZFS sensato para a camada de dados de um servidor de IA

# 8 × 16 TB SAS em dois vdevs RAID-Z2 (6+2 cada) — dois vdevs dão 2× IOPS
zpool create -o ashift=12 \
  -O compression=zstd -O atime=off -O xattr=sa -O recordsize=1M \
  data \
  raidz2 /dev/sd[a-h] \
  raidz2 /dev/sd[i-p]

# Datasets dimensionados para a respetiva carga de trabalho
zfs create -o recordsize=1M   data/datasets       # shards de treino grandes
zfs create -o recordsize=128k data/checkpoints    # tamanho misto
zfs create -o recordsize=16k  data/metadata       # ficheiros pequenos (json, yaml)

zfs snapshot data/datasets@pre-run-2026-05-14
zfs rollback data/datasets@pre-run-2026-05-14

ashift=12 = blocos de 4 KiB — se errar isto na criação da pool, não há como corrigir sem destruir a pool. recordsize=1M para cargas de trabalho com ficheiros grandes (a predefinição de 128 KiB é demasiado pequena para shards de vários GB). xattr=sa guarda os xattrs inline, muitíssimo mais rápido do que a predefinição.

Btrfs — em geral, evitar em produção de IA

O Btrfs tem o mesmo conjunto conceptual de funcionalidades do ZFS — COW, snapshots, checksums, RAID integrado. No papel, é a escolha certa; na prática, não o recomendamos em servidores Kentino. O RAID5/6 tem bugs conhecidos de perda de dados que o próprio projeto assinala como não estando prontos para produção (o RAID1/10 é estável, os modos de paridade não); o desempenho degrada-se muito sob o padrão de fragmentação que as cargas de trabalho de IA produzem (checkpoints de gradientes, caches); o desempenho dos snapshots cai a pique acima de algumas centenas de snapshots, o que um fluxo de trabalho por experiência atinge num trimestre; e as ferramentas são menos maduras do que as do ZFS ou do XFS para replicação, monitorização e agendamento de scrubs. Serve bem num portátil. Para um servidor multi-GPU com dezenas de TB de dados de treino, escolha XFS ou ZFS.

Layouts RAID — com o que emparelhar o sistema de ficheiros

Nível RAID Caso de uso Redundância Notas
RAID0 Scratch (efémero, regenerável) Nenhuma Só para dados que consiga reconstruir
RAID10 Conjuntos de dados de treino, checkpoints 1 por espelho Melhor velocidade + segurança para dados quentes
RAID-Z1 / RAID5 Evitar em configurações novas 1 disco Os tempos de reconstrução em discos de 16 TB+ tornam o Z1 arriscado
RAID-Z2 / RAID6 Arquivo, armazenamento frio de conjuntos de dados 2 discos A escolha certa para grandes volumes
RAID-Z3 Arrays largos (12+ discos) 3 discos Só quando a largura do array o obriga

Em discos de 16 TB+, a reconstrução de um único disco pode demorar 24–72 horas, e uma segunda falha em discos do mesmo lote nessa janela não é negligenciável. A paridade simples já não é a predefinição segura. Use Z2/RAID6 ou espelhos.

Ficheiros pequenos versus ficheiros grandes

A questão que apanha mais gente do que qualquer escolha de sistema de ficheiros: como se comporta o seu sistema de ficheiros com 50 milhões de imagens de 4 KB numa árvore de diretórios? A resposta, para todos os sistemas de ficheiros aqui referidos: mal.

Os metadados por ficheiro (inode, dentry, timestamps, xattrs) ocupam 200–500 bytes, independentemente do tamanho do ficheiro — 50 milhões de ficheiros minúsculos consomem 10–25 GB de metadados antes de guardarem um único byte de píxeis. O padrão abrir/ler/fechar satura o lado dos metadados e bloqueia os workers do DataLoader.

A solução não está no sistema de ficheiros. Não guarde ficheiros pequenos como ficheiros. Empacote em webdataset / tar / parquet / lmdb / hdf5 / safetensors e leia em fluxo sequencial — tanto o WebDataset do PyTorch como o NVIDIA DALI contam com isso. O sistema de ficheiros passa então a ver um pequeno número de ficheiros grandes, algo em que todos os sistemas de ficheiros aqui referidos são bons. Se tiver mesmo de manter ficheiros individuais à escala, o ZFS com recordsize=16k e xattr=sa é a opção menos má, mas a verdadeira resposta é reempacotar.

E/S direta, page cache e O_DIRECT

O PyTorch apoia-se na page cache do Linux para épocas repetidas sobre o mesmo conjunto de dados — a época 1 vem do disco, as épocas 2..N vêm da RAM. Algumas cargas de trabalho usam O_DIRECT para contornar a cache em leituras grandes cujos dados são lidos uma só vez. O GPUDirect Storage da NVIDIA vai mais longe, fazendo DMA de NVMe → GPU diretamente.

O ext4 e o XFS respeitam corretamente o O_DIRECT — E/S direta alinhada, sem page cache. O ZFS ignora o O_DIRECT (toda a E/S passa pelo ARC, por desenho): aceitável para a maioria das cargas de trabalho, incompatibilidade total com o GPUDirect Storage. O alinhamento conta — o buffer e o offset têm de estar alinhados ao tamanho de bloco do dispositivo (tipicamente 4 KB).

Se tenciona usar o GPUDirect Storage para alimentar uma máquina de 8 GPUs à largura de banda máxima, o /data tem de ser XFS em NVMe. Se não sabe o que é o GPUDirect Storage e o treino está limitado pela computação, ainda não precisa dele.

Multipath NVMe

Os NVMe U.2 / E1.S de porta dupla apresentam dois caminhos PCIe cada. O controlador NVMe do Linux faz multipath nativo (nvme_core.multipath=Y, predefinido nos kernels modernos), proporcionando failover e balanceamento de carga round-robin. É invisível na camada do sistema de ficheiros, mas conta para o failover (sem ele, uma falha de caminho ou de HBA faz o disco desaparecer e o sistema de ficheiros entra em pânico) e para a largura de banda (um disco U.2 com multipath atinge ~14 GB/s, contra 7 GB/s num só caminho).

cat /sys/module/nvme_core/parameters/multipath   # esperado: Y
nvme list-subsys

Em NVMe M.2 de consumo (máquinas de secretária com 5090) o multipath é irrelevante — há só um caminho. Em configurações EPYC de 8 GPUs com U.2 / E1.S empresariais num backplane de controlador duplo, deixe-o ligado.

Custo dos checksums

Operação Custo de CPU por GB, um núcleo Notas
ZFS fletcher4 (predefinição) ~50–100 ms Checksum predefinido dos blocos de dados
Compressão ZFS lz4 ~150–250 ms Normalmente compensa pela redução de E/S
Compressão ZFS zstd ~400–800 ms Melhor rácio, custo mais elevado
XFS / ext4 (sem checksum de dados) 0 Apenas metadados

Num host EPYC de 64 núcleos o custo é invisível. Num Xeon de 16 núcleos sob carga sustentada, o ZFS pode roubar 5–10% do CPU efetivo — se isso importa depende de o treino estar limitado pelo CPU no pré-processamento (muitas vezes sim em visão) ou pela GPU (muitas vezes sim em LLM de grande dimensão).

O enquadramento honesto: o ZFS apanha bit rot que de outra forma só notaria quando o seu modelo já tivesse treinado sobre dados subtilmente corrompidos. Para um conjunto de dados que levou seis meses a construir, 5–10% vale a pena. Para scratch efémero que regenera todas as semanas, não.

NFS para conjuntos de dados partilhados entre nós do cluster

A partir do momento em que tem mais do que um servidor, o sítio onde o conjunto de dados vive torna-se interessante. Três padrões:

  1. Copiar para todos os nós. Simples, desperdiça capacidade. Viável para <1 TB e ≤4 nós.
  2. NFS a partir de um servidor de armazenamento dedicado. Uma cópia canónica. A rede é o estrangulamento — 25 GbE no mínimo, 100 GbE para treino multinó.
  3. Sistema de ficheiros paralelo (BeeGFS, Lustre, WekaFS). Dá mais trabalho, mas escala para lá do ponto em que o NFS cede.

Para 1–4 nós, o NFS é a escolha certa. Servidor: XFS em RAID10 NVMe, exportado por NFSv4. Cliente: monte com nconnect=8 para paralelizar entre ligações TCP.

# Servidor /etc/exports
/data/shared 10.0.10.0/24(rw,async,no_subtree_check,no_root_squash)

# Cliente
mount -t nfs -o vers=4.2,nconnect=8,proto=tcp,rsize=1048576,wsize=1048576 \
  storage01:/data/shared /mnt/shared

NFS sobre 100 GbE com nconnect=8 entrega 8–10 GB/s sustentados — suficiente para 16 GPUs em treino típico de visão ou de LLM. Acima disso, BeeGFS / Lustre — e isso é outro artigo.

Exemplo completo: EPYC com 8 GPUs, 24 NVMe + 8 SAS

2× 480 GB NVMe  (arranque) → md RAID1   → ext4 → /
8× 7.68 TB NVMe (scratch)  → md RAID0   → XFS  → /scratch
8× 7.68 TB NVMe (dados)    → md RAID10  → XFS  → /data
8× 16 TB SAS    (arquivo)  → 2× RAID-Z2 → ZFS  → /archive

ARC do ZFS: 64 GB de 512 GB de RAM. Multipath NVMe: ligado. Exportação NFS: /data sobre 100 GbE.
mkfs.ext4 -L root /dev/md0
mkfs.xfs -f -d su=64k,sw=8 -l size=512m -L scratch /dev/md1
mkfs.xfs -f -d su=64k,sw=4 -l size=512m -L data    /dev/md2

zpool create -o ashift=12 \
  -O compression=zstd -O atime=off -O xattr=sa -O recordsize=1M \
  archive raidz2 /dev/sd[a-h]

echo "options zfs zfs_arc_max=68719476736" > /etc/modprobe.d/zfs.conf
update-initramfs -u

Débito NVMe bruto onde precisa dele, redundância onde os dados são insubstituíveis e volume comprimido com checksums para tudo o resto.

O que fazer a seguir

Antes de formatar o que quer que seja, responda:

  1. Qual é o maior conjunto de dados, em TB? Dimensiona o /data e diz-lhe se precisa de uma camada de arquivo.
  2. Os dados de treino podem ser regenerados a partir de uma fonte de verdade? Se sim, scratch em RAID0 chega. Se não, RAID10 no mínimo e pondere ZFS em /data.
  3. Quantas GPUs leem o mesmo volume em simultâneo? Acima de ~8 processos DataLoader em paralelo, os grupos de alocação do XFS compensam face ao ext4.
  4. Precisa de snapshots? "Criar um fork de um conjunto de dados e reverter" → ZFS. Caso contrário, o XFS é mais rápido e mais simples.
  5. Qual é o orçamento de RAM e quanto pode o ZFS levar? Decida antes de instalar, não depois do primeiro OOM.

A escolha do sistema de ficheiros não é a decisão mais interessante que vai tomar sobre um servidor de IA, mas é uma das poucas genuinamente difíceis de reverter. Escolha de forma deliberada, formate uma vez e siga em frente.


Isto faz parte da Kentino Wiki, uma série de referência sobre computação de IA, armazenamento e os sistemas que os ligam. Comentários e correções são bem-vindos em info@kentino.com.