Armazenamento para Clusters de IA: NFS, BeeGFS, Lustre e a Questão do Object Store
O armazenamento partilhado é a parte de um cluster de treino distribuído em que ninguém pensa até se tornar a razão pela qual as GPUs ficam a 40% de utilização. A essa altura, substituí-lo é doloroso — todos os scripts de treino têm pressupostos entranhados, todos os checkpoints estão num formato que pode ou não migrar sem problemas, e o cluster está em produção. O momento certo para decidir o armazenamento é antes de o segundo nó de treino entrar no rack.
Esta é a metade dedicada ao armazenamento da série K. É uma visão de comprador e arquiteto, não um guia de administração de sistemas — o objetivo é decidir entre NFS, BeeGFS, Lustre e object stores com os olhos abertos, e ser honesto sobre qual deles a maioria dos clientes Kentino precisa de facto.
Porque é que o armazenamento partilhado é o estrangulamento silencioso
Uma máquina de treino de nó único lê o conjunto de dados do NVMe local e escreve os checkpoints no mesmo sítio. Não há nada a discutir. No momento em que um segundo nó se junta, duas coisas mudam:
- Todos os nós leem o mesmo conjunto de dados. Os bytes são idênticos em cada worker e têm de estar visíveis a partir de todos os nós.
- Todos os nós escrevem checkpoints, e a união tem de sobreviver. Com FSDP ou com sharding do DeepSpeed, cada rank escreve a sua fatia; com DDP, os ranks escrevem de forma redundante. De uma forma ou de outra, o sistema de ficheiros vê uma rajada de escritas grandes a cada poucos minutos ou poucas horas.
Estes padrões de acesso são opostos. O acesso ao conjunto de dados é sustentado, sequencial, dominado por leituras e tolerante à latência. O acesso aos checkpoints é em rajada, de blocos grandes, dominado por escritas, e o treino fica bloqueado à espera dele. Um sistema de armazenamento que é bom num é muitas vezes medíocre no outro.
Além disso, os conjuntos de dados de ML modernos fazem uma terceira coisa: tempestades de metadados. Um conjunto de dados com 50 milhões de JPEG de 4–12 KB é, do ponto de vista do sistema de ficheiros, 50 milhões de ciclos de open/stat/read/close. É o servidor de metadados, e não o caminho dos dados, que cai primeiro. É por isto que "temos largura de banda de sobra" não é uma resposta suficiente.
| Carga de trabalho | Padrão | Do que precisa |
|---|---|---|
| Shuffle / leitura do conjunto de dados | Leituras sequenciais sustentadas, à escala de TB | Largura de banda de leitura agregada |
| Conjunto de dados de ficheiros pequenos | Metadados aleatórios + leituras de ficheiros minúsculos | IOPS de metadados, baixa latência por operação |
| Escrita de checkpoint | Escritas grandes em rajada, todos os nós ao mesmo tempo | Largura de banda de escrita em rajada, sem head-of-line |
| Carregamento do modelo de inferência | Uma única leitura grande no arranque | Tolera qualquer coisa razoável |
NFS — a opção por omissão, e dura mais do que se pensa
O NFS (NFSv3 ou v4) é o caminho de menor resistência. Todas as distribuições Linux o incluem, todos os escalonadores o entendem, e um "nó de armazenamento" não passa de uma máquina Linux com muito disco, uma linha em /etc/exports e uma NIC rápida.
O que um servidor NFS bem construído oferece:
- Um espaço de nomes único, montado de forma idêntica em todos os nós de computação.
- Débito de leitura sequencial decente, sobretudo com
nconnectem kernels modernos (multiplexando uma montagem por várias ligações TCP — o teto do fluxo único desapareceu há anos). - Operacionalmente aborrecido. As falhas são bem compreendidas e os procedimentos de recuperação estão em todos os manuais de administração de sistemas alguma vez escritos.
O que se perde:
- Um servidor, um estrangulamento. O débito agregado do cluster está limitado ao que essa única máquina consegue servir.
- Serialização de locks e metadados. As cargas de trabalho de ficheiros pequenos atingem os limites de metadados antes dos limites de largura de banda.
- Não escala de forma elegante. Quando a máquina enche, constrói-se uma segunda com outro ponto de montagem, e os seus scripts passam a saber da existência dela.
A má fama que o NFS tem nos círculos de HPC prende-se sobretudo com o antigo teto de ligação única e com o sofrimento de metadados em ficheiros minúsculos. Para conjuntos de dados de treino entre 100 GB e 1–2 TB em nós de 4–8 GPUs, um servidor NFS bem construído é genuinamente adequado. Já vimos clientes a treinar afinações do Qwen2.5-VL com conjuntos de dados em NFS sem uma queixa.
O NFS começa a doer quando:
- Os conjuntos de dados ultrapassam os ~2 TB e os shuffles provocam thrashing de cache no servidor.
- Mais de ~8–12 nós de computação atacam a mesma montagem sob carga.
- Dezenas de milhões de ficheiros pequenos dominam a carga de trabalho.
- Os checkpoints por rank ultrapassam 50–100 GB escritos em simultâneo.
Abaixo destes quatro limiares, o NFS é quase de certeza a resposta certa e um sistema de ficheiros paralelo é sobre-engenharia.
BeeGFS — o meio-termo prático
O BeeGFS é o sistema de ficheiros paralelo que as pessoas realmente implementam quando o NFS deixa de chegar. Distribui os dados por vários alvos de armazenamento (discos em vários servidores) e alvos de metadados (servidores NVMe separados). Os clientes veem um espaço de nomes único; as leituras e escritas são distribuídas em paralelo pelos alvos de armazenamento.
Porque aparece em clusters de média escala:
- A instalação demora dias, não semanas. Um engenheiro de Linux competente põe de pé uma instalação de dois nós de armazenamento e um MDS numa tarde. O Lustre não joga nesse campeonato.
- A largura de banda agregada escala linearmente com os alvos de armazenamento. Três nós a 100 GbE entregam perto de 3× o débito de leitura de um.
- RDMA nativo em InfiniBand ou RoCE — evita a sobrecarga do TCP/IP.
- Código aberto, sem a armadilha do licenciamento por TB. Suporte comercial da ThinkParQ opcional.
Aquilo em que, honestamente, é menos bom:
- Metadados em cargas de trabalho com muitíssimos ficheiros pequenos. Um único MDS torna-se o estrangulamento quando dominam ficheiros abaixo de 4 KB. É possível acrescentar alvos de metadados, mas a arquitetura não é tão agressiva como o DNE do Lustre.
- Sem tiering incorporado. A gestão do que é quente e frio fica do seu lado.
- A alta disponibilidade é acrescentada, não nativa. O espelhamento funciona, mas não é tão transparente como o failover do Lustre.
Zona ideal: 4–32 nós de treino, 10–100 TB de conjunto de dados, ficheiros entre 1 MB e 1 GB. É onde cai a maioria das configurações Kentino multinó.
Uma implementação BeeGFS de base para um cluster Kentino de 4 nós:
| Componente | Especificação |
|---|---|
| Nó de armazenamento 1 | 2U, EPYC de 24 núcleos, 256 GB de RAM, 12× NVMe de 7.68 TB, 2× 100 GbE |
| Nó de armazenamento 2 | Idêntico |
| Nó de metadados | 1U, EPYC de 16 núcleos, 192 GB de RAM, 2× NVMe de 3.84 TB (RAID-1), 2× 100 GbE |
| Rede | 100 GbE leaf-spine ou switch único (com suporte RoCE) |
| Clientes | 4× nós de treino Kentino AI, cada um com 1× 100 GbE |
A leitura agregada sustentada para os quatro nós de treino situa-se tipicamente entre 30 e 60 GB/s, o suficiente para alimentar 32 GPUs na maior parte do treino de visão e de LLM. Isto deixa de escalar quando o servidor de metadados satura — acrescente um segundo alvo de metadados ou comece a pensar em Lustre.
Lustre — padrão de ouro, complexidade real
O Lustre corre a camada de armazenamento de praticamente todos os supercomputadores do top-500 que precisam de POSIX, escalando até centenas de petabytes e débitos agregados de vários TB/s. A arquitetura tem três papéis: MGS/MDS (gestão + metadados), OSS (servidores de armazenamento de objetos que detêm os OST) e clientes. Os clientes falam com o MDS para operações de espaço de nomes e depois falam diretamente com o OSS que detém os dados — o caminho dos dados contorna por completo o servidor de metadados. É esta separação que permite ao Lustre escalar, e o DNE (Distributed Namespace) deixa os metadados escalarem também na horizontal.
O custo é operacional: curva de aprendizagem mais íngreme do que o BeeGFS, requisitos estritos de kernel e controladores, dezenas de parâmetros de afinação (o desempenho de origem não costuma ser o que o hardware consegue entregar) e procedimentos de recuperação que exigem um engenheiro de armazenamento competente.
O limiar a partir do qual o Lustre faz sentido fica grosso modo em 16–32 ou mais nós de treino, ou em conjuntos de dados à escala do PB. Abaixo disso, o BeeGFS satura a rede muito antes de as vantagens arquitetónicas do Lustre fazerem diferença. A gama Kentino, na sua maioria, não chega a esse limiar — construímos um cluster Lustre se nos pedirem, mas dizemos primeiro que o BeeGFS com um MDS robusto leva o cliente a 80% do caminho com 20% do sofrimento operacional.
Armazenamento de objetos — MinIO e Ceph para data lakes de ML
Os sistemas de ficheiros paralelos acima são a escolha certa quando a framework espera ficheiros. Uma fatia crescente dos pipelines de ML modernos não espera — os conjuntos de dados vivem como objetos S3, o data loader puxa-os por HTTP, e o sistema de ficheiros local é apenas rascunho transitório.
O MinIO é um armazenamento de objetos compatível com S3 e de propósito único. O modo distribuído corre em 4–32 ou mais nós com erasure coding. É código aberto, operacionalmente mais simples do que qualquer sistema de ficheiros paralelo, e os data loaders modernos (PyTorch com fsspec/s3fs, WebDataset, NVIDIA DALI) leem-no diretamente.
O Ceph é mais abrangente — bloco (RBD), ficheiro (CephFS) e objeto (RGW) sobre a mesma camada RADOS. O Ceph ganha quando se quer um só sistema de armazenamento para várias cargas de trabalho (máquinas virtuais, partilhas de ficheiros, objetos de ML). Perde na simplicidade operacional — o Ceph é um sistema com que se assume um compromisso, com a sua própria monitorização, afinação e disciplina de piquete.
O que o armazenamento de objetos traz ao ML: capacidade barata (nós densos de HDD com erasure coding dão valores de €/TB que a flash não consegue tocar), escalabilidade horizontal operacionalmente simples, sharding por conceção (sem estrangulamento de metadados) e boa compatibilidade com data loaders baseados em shards. O que se perde: latência por operação na casa das dezenas de ms (não microssegundos), ausência de POSIX direto (reescrever o loader ou aceitar a penalização do FUSE) e ausência de escritas no local (os objetos são imutáveis — ótimo para checkpoints, mau para tudo o que mude).
A arquitetura que funciona na prática: armazenamento de objetos como data lake duradouro, grande e barato; um sistema de ficheiros paralelo ou NFS como conjunto de trabalho rápido, preparado para a execução em curso. O conjunto de dados vive no MinIO. Um job coloca os shards relevantes no BeeGFS ou em rascunho NVMe local no arranque. Os checkpoints finais voltam para o MinIO para arquivamento.
WekaIO e VAST — o patamar empresarial (em breve resumo)
A WekaIO e a VAST Data são a resposta criada de propósito e com preço empresarial: metadados extremos para ficheiros pequenos, largura de banda agregada muito elevada, S3 e POSIX nativos, tiering integrado e caminhos GPU-direct. Fazem todo o sentido para clusters de 100 ou mais GPUs, em que o armazenamento tem de acompanhar o investimento em GPUs. Não são a escolha certa para um servidor de 4 ou 8 GPUs, nem para um cluster de quatro máquinas dessas — o custo do armazenamento dominaria a configuração. Ficam aqui mencionadas por honestidade sobre a gama alta, não como recomendação para o comprador a quem esta série se dirige.
Padrões de acesso ao conjunto de dados e porque importam
Dois padrões dominam o treino de ML. Padrão A — shards grandes: conjuntos de dados pré-empacotados em tarballs WebDataset, parquet ou LMDB, de 100 MB a 10 GB por shard, lidos sequencialmente e baralhados internamente. Todos os sistemas de armazenamento são bons nisto. Padrão B — muitos ficheiros pequenos: milhões de JPEG/PNG/JSON, um por amostra, a martelar o caminho dos metadados. Isto castiga o NFS, prejudica o BeeGFS em escala e é a razão de existirem a Weka e a VAST.
A decisão com maior alavancagem no armazenamento para treino é passar o Padrão B para o Padrão A. Se o conjunto de dados for de ficheiros pequenos, reempacote-o em shards antes do treino. O custo é uma passagem de pré-processamento; o benefício é que qualquer sistema de armazenamento passa a ser adequado. Já vimos pipelines acelerarem 3–5× sem qualquer alteração de hardware de armazenamento, apenas com o reempacotamento.
Se não for possível reempacotar — conjunto de dados constantemente alterado, framework que exige mesmo ficheiros por amostra — dimensione o armazenamento em função dos IOPS de metadados, não da largura de banda. Isso empurra-o para BeeGFS com vários alvos MDS, para Weka/VAST ou para Lustre com DNE.
Estratégia de checkpoints — escrever depressa, escrever mais tarde, escrever menos
As escritas de checkpoint são o pior padrão possível: todos os ranks escrevem em simultâneo, as escritas são grandes e o treino fica bloqueado até os dados estarem seguros. Um checkpoint síncrono ingénuo num modelo de 100 B de parâmetros pode travar o treino durante dezenas de minutos.
A melhor prática atual é uma abordagem em três fases, nativa no torch.distributed.checkpoint (DCP) do PyTorch, no NVIDIA NeMo e na maioria das frameworks modernas:
- Cada rank escreve a sua fatia em rascunho NVMe local. A largura de banda total de escrita do cluster é a soma das taxas NVMe de cada nó (5–50 GB/s cada). O treino desbloqueia em segundos.
- Um processo assíncrono em segundo plano copia para o armazenamento partilhado. A esta altura o treino já recomeçou.
- Os checkpoints mais antigos são eliminados ou movidos para armazenamento de objetos segundo um calendário. Guarde os últimos 2–3 em armazenamento rápido e o resto no MinIO/Ceph.
A implicação: o armazenamento partilhado não precisa de absorver todos os checkpoints à velocidade máxima. Precisa de ingerir a cópia assíncrona sem ficar atrás da cadência. Para um modelo de 70 B com checkpoint a cada 30 minutos, uma escrita sustentada de poucos GB/s chega — bem dentro do alcance de um BeeGFS competente ou de um bom servidor NFS.
O erro a evitar: checkpoints síncronos e sem sharding escritos diretamente para NFS. Isso era normal em 2022. Já não é. Se o seu pipeline faz isto, corrigir o código dos checkpoints tem mais alavancagem do que melhorar o armazenamento.
A questão do uplink do cluster
O tráfego de armazenamento partilha a fabric com o treino, ou tem uma só para si?
A fabric partilhada serve bem clusters de 4–8 nós com checkpoints assíncronos; a fabric separada justifica-se acima de 8 nós ou com E/S síncrona intensa.
Para clusters Kentino de 4–8 nós, a fabric partilhada chega quando se usam checkpoints assíncronos e a carga de armazenamento é sobretudo de leitura — a leitura sustentada coexiste com as coletivas NCCL sem grande prejuízo. A fabric separada justifica-se em treino de LLM de grande dimensão com FSDP em graus elevados de TP/PP, com tráfego de armazenamento síncrono intenso, ou quando o orçamento permite a folga. O padrão prático: 100 GbE partilhada até 8 nós, com uma VLAN de armazenamento como isolamento suave.
Recomendações honestas por escala
| Escala | Conjunto de dados | Camada de armazenamento | Notas |
|---|---|---|---|
| 1 nó | Qualquer | NVMe local | Dispense por completo o armazenamento partilhado. |
| 2–3 nós de treino | < 1 TB | NFS num nó de armazenamento robusto |
nconnect, RAM generosa para a cache de páginas. |
| 4–8 nós de treino | 1–10 TB | NFS, ou a primeira implementação de BeeGFS | BeeGFS se o conjunto de dados for dominado por ficheiros pequenos. |
| 4–16 nós de treino | 10–100 TB | BeeGFS, 2–3 alvos de armazenamento, MDS dedicado | Zona ideal. Acrescente MinIO para arquivamento frio. |
| 16–32+ nós de treino | 100 TB–1 PB | BeeGFS no limite, ou Lustre | Ponto de decisão para o Lustre. |
| 32+ nós, escala PB, 24/7 | PB+ | Lustre, ou Weka/VAST se o orçamento permitir | Para lá da configuração Kentino típica. |
| Data lake grande, treino episódico | PB+ frio | MinIO/Ceph, com staging para a camada rápida por job | "Muitos dados, treino ocasional." |
A maioria dos clientes Kentino cai na segunda e na terceira linhas. Construímos o resto se nos pedirem, mas dizemos ao cliente quando está sobredimensionado.
O que corre mal
Modos de falha que já vimos, por ordem da frequência com que mordem:
-
Esgotamento de metadados com carga de ficheiros pequenos. Sintoma: GPUs a 30–50%, rede parada, CPU do armazenamento parada, cada
open()lento. Solução: reempacotar em shards, ou aumentar os alvos MDS. - Checkpoints síncronos a travar o treino. Solução: checkpoints assíncronos com sharding (PyTorch DCP ou NeMo).
- Um disco lento a afundar o cluster. Um SSD em fim de vida não falha de forma limpa — limita-se a ficar lento. Solução: monitorizar a latência por dispositivo e alarmar acima de > 2× a referência.
- Thrashing da cache de páginas no nó de armazenamento. O conjunto de dados não cabe na RAM e cada época despeja a anterior. Solução: mais RAM.
-
nconnectnão ativado nos clientes NFS. O NFS de fluxo único por omissão fica muito abaixo da velocidade nominal da NIC.nconnect=8ou=16dá 4–8× de débito no mesmo hardware. - MinIO em escala sem afinação do erasure coding. Os valores por omissão privilegiam a durabilidade sobre a latência; a latência em objetos pequenos sofre. Não aceite os valores por omissão às cegas.
Nada disto é exótico. Tudo isto é previsível depois de se ver uma vez.
O que fazer a seguir
Se está a especificar armazenamento para um cluster de treino novo ou em crescimento, responda a estas perguntas antes de assinar a compra do hardware:
- Forma do conjunto de dados — shards grandes ou muitos ficheiros pequenos? É o maior determinante de qual sistema de armazenamento encaixa.
- Conjunto de trabalho ativo, não o tamanho do data lake. O orçamento de armazenamento vai para o conjunto de trabalho; o lake vive em armazenamento de objetos mais barato e mais lento.
- Cadência dos checkpoints, tamanho e tolerância a paragens. Dimensiona a capacidade de escrita em rajada e diz-lhe se os checkpoints assíncronos com sharding são obrigatórios (acima de ~13 B de parâmetros, normalmente são).
- Nós de treino hoje e projeção realista a 12 meses. Se vai ficar em 4 nós durante um ano, construa para 4 nós. Não compre Lustre por antecipação.
- Armazenamento na fabric de treino, ou separado? Decida antes de cablar, não depois.
- Plano de arquivamento frio. Modelos, versões do conjunto de dados, artefactos de execuções concluídas — isso pertence a algum sítio barato. MinIO ou Ceph é normalmente a resposta.
O armazenamento recompensa o planeamento antecipado mais do que quase qualquer outra parte de um cluster de IA, porque o custo de errar paga-se em cada execução de treino durante toda a vida do sistema. O objetivo honesto não é "o armazenamento mais rápido que conseguimos comprar" — é "o armazenamento mais simples que não estrangula as GPUs que já temos", e para a maioria das configurações à escala da Kentino isso é bem menos exótico do que o marketing dos fabricantes dá a entender.
Artigos relacionados: K01 (nó único vs multinó), K02 (mecânica do treino distribuído), K03 (clusters de inferência), N08 (RDMA + uplink do cluster), W06 (camadas de armazenamento dentro de um único servidor).
Isto faz parte da Kentino Wiki, uma série de referência sobre computação de IA, robótica e os sistemas que as ligam. Comentários e correções são bem-vindos em info@kentino.com.