La panne la plus chère de ton cluster ne lève aucune erreur

Ton entraînement distribué s’est terminé sans accroc. Aucune erreur dans les logs, le modèle est sorti, les checkpoints sont là. Il tournait juste à une fraction de la vitesse prévue, et personne ne te l’a dit. Voilà le genre de panne que l’IA n’a pas inventée mais qu’elle adore : l’échec silencieux, celui qui ne casse rien et qui coûte quand même une fortune en heures de GPU.

AWS vient de publier un billet qui décrit, sans jamais le dire comme ça, la mécanique exacte de ce piège. Le sujet apparent, c’est un conteneur : la Ray Train DLC, une image taillée pour l’entraînement PyTorch sur Kubernetes. Le vrai sujet, c’est que NCCL, la bibliothèque qui fait causer les GPU entre eux, ne plante pas quand sa configuration réseau est mauvaise. Elle retombe sur des sockets TCP et le job va au bout, à une fraction de la vitesse. AWS le formule tranquillement dans son billet. Relis la phrase deux fois.

Le conteneur est le vrai chantier

Ce qui bloque un passage à l’échelle, c’est rarement le code du modèle. C’est l’empilement en dessous : un runtime CUDA identique partout, un build PyTorch compilé contre ce runtime, NCCL, les bibliothèques userspace de l’Elastic Fabric Adapter, le plugin AWS OFI NCCL qui relie NCCL à EFA, et une version de Ray sur laquelle l’opérateur du cluster est d’accord. Chacune de ces briques bouge à son propre rythme, et reconstruire l’image à chaque mouvement n’a rien à voir avec le modèle que tu voulais entraîner.

La Ray Train DLC, c’est cette image, assemblée et testée comme un bloc par AWS, publiée sur Amazon ECR Public et maintenue à jour. Le tag utilisé dans l’exemple est public.ecr.aws/deep-learning-containers/ray:train-ml-cuda-v1.1. Une seule image sert à la fois de tête Ray et de worker, que KubeRay la lance sur EKS ou que tu la démarres à la main sur EC2. Tout ce qui change d’un job à l’autre se règle autour : type d’instance, nombre de GPU par pod, transport réseau, paquets Python supplémentaires.

Le sample complet enchaîne la fine-tune de Qwen2.5-1.5B en Fully Sharded Data Parallel sur huit GPU NVIDIA L4 répartis sur deux instances g6.12xlarge, quatre GPU chacune, 48 vCPU, 192 GiB de mémoire, 40 Gbps de réseau et une interface EFA. Un nœud système m7i.xlarge héberge l’opérateur KubeRay et la tête Ray, sans GPU, avec le serveur de jobs sur le port 8265 et le control store sur 6379. Les jobs se soumettent depuis l’intérieur du pod de tête avec kubectl exec via l’API EKS, donc le 8265 ne sort jamais du cluster. Tout est en sous-réseau privé, sortie internet par NAT Gateway, le temps de tirer l’image depuis ECR Public et les poids depuis Hugging Face. Aucune image construite à aucun moment.

Le silence réseau, seule chose que l’image ne peut pas régler à ta place

La ligne du tableau qu’aucune équipe ne veut posséder, c’est le réseau. Et c’est précisément celle où une erreur ne se voit pas. Un mismatch entre NCCL et EFA ne lève rien. Le job finit. Il finit juste mal.

L’image arrive avec FI_PROVIDER=efa par défaut et une configuration NCCL qui exclut le loopback et le bridge Docker. Sur EKS, tu pointes NCCL_SOCKET_IFNAME vers l’eth0 du pod. Le sample écrit quand même les quatre variables réseau dans le manifeste, pour que le comportement soit visible dans un seul fichier au lieu de dépendre des défauts de l’image. Défaut de conception assumé, et honnête : celui qui lit le manifeste n’a pas à deviner.

Sur le reste, AWS déplace la personnalisation hors de l’image. Les paquets dont un job a besoin et que l’image ne porte pas s’installent au moment du submit via runtime_env, sans nouveau tag. Les variables réseau étant de simples variables d’environnement, un job peut les surcharger de la même façon. Et les tags jouent sur deux niveaux : les vX.Y bougent à chaque rebuild de sécurité, les vX.Y.Z restent figés sur le build d’origine. AWS conseille de pin un digest quand il faut un run identique au bit près. L’image existe aussi en URI ECR privé.

Ce que l’image ne te dit pas

Le confort a un prix, et le billet d’AWS ne le cache pas vraiment : chaque release de la DLC passe un gate de tests multi-nœuds EFA et multi-nœuds KubeRay, exactement les chemins du sample. C’est l’argument de vente. C’est aussi la formulation polie d’une dépendance : c’est AWS qui décide quelles combinaisons de briques sont soutenues, à quel rythme elles sont reconstruites et ce qui est testé. Si ta pile sort des sentiers du sample, le gate ne te protège plus de grand-chose.

Deuxième chose : le billet parle d’« une fraction de la vitesse » sans jamais donner de chiffre. Pas de comparaison EFA contre TCP, pas de courbe. Donc toi, tu n’as pas de chiffre non plus, et tu ne peux pas en fabriquer un. La seule façon de savoir ce que ta configuration réseau te coûte, c’est de la mesurer toi-même. Personne ne le fera à ta place, et surtout pas le job qui vient de se terminer avec succès.

C’est le prolongement exact de ce que je disais sur le temps que l’IA te fait gagner : quelqu’un le paie, et ici le quelqu’un, c’est ton budget GPU qui tourne en TCP pendant que les dashboards affichent du vert. Le temps gagné sur l’assemblage du conteneur (le temps que l’IA te fait gagner, quelqu’un d’autre le paie) est réel. Il n’est juste pas gratuit, et il se paie en visibilité perdue sur une couche que tu ne construis plus.

La prochaine fois que ton job multi-GPU se termine plus tôt que prévu, demande-toi s’il a accéléré ou s’il a renoncé.

Personne ne viendra te prévenir. Il n’y a pas d’alarme pour un succès trop lent.


Sources :

Categories

Comments are closed

Latest Comments

Aucun commentaire à afficher.