
Soixante conteneurs sur un seul serveur
Une machine bare-metal exécute des dizaines à des centaines de conteneurs Hoody. La dédupplication KSM et BTRFS rend le coût marginal quasi nul.
Votre matrix CI s'étale sur trente runners de test. Chacun a besoin de la même image de 800 Mo. Streamez le tarball dans un seul chemin pipe avec ?n=30. Les trente workers font curl sur la même URL. Les octets passent une fois, le serveur ne retient rien et aucun identifiant de registry n'est rotationné.
# stream the image oncetar c ./image.tar | curl -T - https://pipe.hoody.com/build-12af?n=30UN ÉMETTEUR À GAUCHE. TRENTE RÉCEPTEURS GET À DROITE. LE PIPE ATTEND QUE TOUT LE MONDE ÉCOUTE, PUIS LES OCTETS PASSENT UNE SEULE FOIS.
Le pipe est un routeur de fan-out sans disque. Le POST de l'émetteur sur /api/v1/pipe/[path]?n=30 bloque jusqu'à ce que trente récepteurs se connectent à la même URL avec le même n. Ensuite les octets vont du conteneur de build directement vers chaque runner, simultanément, à la vitesse du récepteur le plus lent.
tar c | curl -T - https://pipe/.../build?n=30Le conteneur de build envoie le tarball directement dans curl. Aucun fichier écrit, aucun registry poussé.
POST /api/v1/pipe/[path]?n=30Le serveur retient l'émetteur jusqu'à ce que les trente récepteurs se connectent. Un n incohérent retourne 400. Les récepteurs déjà connectés sont acceptés.
curl https://pipe/.../build?n=30 | tar xChaque runner reçoit des octets identiques. Le backpressure vient du récepteur le plus lent, pas de la bande passante de l'émetteur.
Rien ne persiste. Rien n'est mis en cache. Le pipe orchestre la connexion, puis s'efface. Quand le runner le plus lent termine, le transfert termine — et l'URL disparaît.
Naïf : trente pulls de registry du même tarball de 800 Mo, trente caches froids, trente allers-retours réseau. Pipe : un egress, un transfert, le récepteur le plus lent fixe le rythme.
12 s
Un seul egress à plein débit. Le récepteur le plus lent fixe le rythme, mais personne ne re-télécharge.
1× / build
Les octets quittent le builder une seule fois, fan-out au pipe. Pas de frais GET S3, pas de pulls Docker Hub.
0 octet
Le pipe ne retient rien sur disque. Pas de registry à nettoyer, pas de clé de cache à invalider.
Le temps réel suppose une matrix à 30 voies sur le même réseau régional que le conteneur de build ; les transferts inter-régions sont limités par la bande passante inter-régions, pas par le pipe.
Une fois que le build est une URL et trente curl, toute une pile d'échafaudage CI disparaît. Pas de stockage d'artefacts à expirer. Pas d'identifiants de registry à rotationner. Pas de cache action à débugger.
Le backpressure est intégré au pipe. Les workers rapides ne gaspillent pas un aller-retour de registry à attendre le lent — ils attendent au pipe, puis boivent au même rythme. Personne ne re-télécharge.
Rien n'est poussé vers un registry, donc rien n'a besoin de s'y authentifier. L'URL elle-même est l'identifiant — éphémère, limitée à un transfert, évincée à la fin du build.
Les octets quittent le builder une seule fois. Le pipe diffuse. Vous payez un egress par build au lieu de trente pulls de registry par exécution de matrix.
Le pipe est par-build, pas par-clé. Pas de cache GitHub Actions à mal-cibler, pas de mystère de couches buildx, pas de tarball obsolète de la main de la semaine dernière.
Le même pattern gère node_modules, .pnpm-store, target/, le cache de wheels, le shard de dataset. Si ça streame, ça fait du fan-out.
Un émetteur. Trente récepteurs. Zéro facture S3.
Un push à 30 voies qui prenait quatre-vingt-dix secondes et un hit S3 prend douze secondes et un seul egress. Personne ne re-télécharge. Aucun identifiant de registry n'est rotationné. L'URL s'évince elle-même quand la matrix se termine.
Les pièces qu'un flux matrix-CI doit normalement assembler — registry, cache action, miroir, étape d'upload custom. Le pipe les regroupe en une seule URL.
Arrêtez de pousser le même tarball trente fois. Poussez-le une fois. Laissez trente curl partager le flux.