
Sessenta contêineres em um servidor
Uma caixa bare-metal executa dezenas a centenas de contêineres Hoody. KSM e BTRFS dedup fazem o custo marginal próximo a zero.
Sua matriz de CI se distribui em trinta runners de teste. Cada um precisa da mesma imagem de 800 MB. Envie o tarball para um caminho de pipe com ?n=30. Os trinta workers fazem curl na mesma URL. Os bytes passam uma vez, o servidor não retém nada e nenhuma credencial de registry é rotacionada.
# envie a imagem uma veztar c ./image.tar | curl -T - https://pipe.hoody.com/build-12af?n=30UM REMETENTE À ESQUERDA. TRINTA RECEPTORES GET À DIREITA. O PIPE ESPERA ATÉ TODOS ESTAREM ESCUTANDO; AÍ OS BYTES PASSAM UMA VEZ.
O pipe é um roteador de fan-out sem disco. O POST do remetente em /api/v1/pipe/[path]?n=30 bloqueia até trinta receptores se conectarem na mesma URL com o mesmo n. Aí os bytes fluem do contêiner do build direto para cada runner, simultaneamente, na velocidade do receptor mais lento.
tar c | curl -T - https://pipe/.../build?n=30O contêiner do build envia o tarball direto pelo curl. Nenhum arquivo escrito, nenhum push para registry.
POST /api/v1/pipe/[path]?n=30O servidor mantém o remetente até todos os trinta receptores se conectarem. n divergente retorna 400. Receptores pré-conectados estão ok.
curl https://pipe/.../build?n=30 | tar xCada runner recebe os mesmos bytes. O backpressure flui do receptor mais lento, não da banda do remetente.
Nada persiste. Nada é cacheado. O pipe intermedia a conexão e sai do caminho. Quando o runner mais lento termina, a transferência termina — e a URL deixa de existir.
Ingênuo: trinta pulls do registry do mesmo tarball de 800 MB, trinta caches frios, trinta round-trips de rede. Pipe: um egress, uma transferência, o receptor mais lento dita o ritmo.
12s
Um egress na velocidade da linha. O receptor mais lento dita o ritmo, mas ninguém baixa de novo.
1× / build
Os bytes saem do builder uma vez e fazem fan-out no pipe. Sem taxas de GET no S3, sem pulls do Docker Hub.
0 bytes
O pipe não retém nada em disco. Sem registry para limpar, sem chave de cache para invalidar.
O número de tempo de execução assume uma matriz de 30 vias na mesma rede regional do contêiner do build; transferências entre regiões são limitadas pela banda inter-regional, não pelo pipe.
Quando o build vira uma URL e trinta curls, uma pilha de andaimes de CI desaparece. Sem armazenamento de artefatos para envelhecer. Sem credenciais de registry para rotacionar. Sem cache action para depurar.
O backpressure já vem embutido no pipe. Os workers rápidos não desperdiçam um round-trip no registry esperando o lento — eles esperam no pipe e bebem na mesma velocidade. Ninguém baixa de novo.
Nada é enviado para um registry, então nada precisa se autenticar em um. A URL em si é a credencial — efêmera, escopada para uma transferência, descartada quando o build termina.
Os bytes saem do builder uma vez. O pipe transmite. Você paga um egress por build em vez de trinta pulls de registry por execução de matriz.
O pipe é por build, não por chave. Não tem cache do GitHub Actions para errar, mistério de camada do buildx, nem tarball obsoleto da main da semana passada.
O mesmo padrão lida com node_modules, .pnpm-store, target/, o cache de wheels, o shard de dataset. Se faz streaming, faz fan-out.
Um remetente. Trinta receptores. Zero contas de S3.
Um push de 30 vias que levava noventa segundos e um hit no S3 leva doze segundos e um único egress. Ninguém baixa de novo. Nenhuma credencial de registry é rotacionada. A URL se descarta quando a matriz termina.
As peças que um fluxo de matriz de CI normalmente precisa montar — registry, cache action, mirror, etapa de upload customizada. O pipe junta tudo em uma URL.
Pare de empurrar o mesmo tarball trinta vezes. Empurre uma vez. Deixe trinta curls dividirem o stream.