
1 つのサーバーで 60 のコンテナ
1 つのベアメタルボックスで数十から数百の Hoody コンテナを実行。KSM と BTRFS のデデュプでマージナルコストはほぼゼロ。
マトリックス CI が 30 のテストランナーにファンアウト。それぞれが同じ 800 MB のイメージを必要とする。tarball を ?n=30 で 1 つのパイプパスにストリーミング。30 のワーカーすべてが同じ URL を curl。バイトは一度通り、サーバーは何も保持せず、レジストリの認証情報はローテーションされない。
# stream the image oncetar c ./image.tar | curl -T - https://pipe.hoody.com/build-12af?n=30左に 1 つの送信側。右に 30 の GET 受信側。パイプは全員がリスニングするまで待ち、その後バイトが一度通る。
パイプはディスクのないファンアウトルーター。送信側の /api/v1/pipe/[path]?n=30 への POST は、同じ URL に同じ n で 30 の受信側が接続するまでブロック。その後、バイトはビルドコンテナからすべてのランナーへそのまま、最も遅い受信側の速度で同時に流れる。
tar c | curl -T - https://pipe/.../build?n=30ビルドコンテナが tarball をそのまま curl にパイプ。ファイル書き込みなし、レジストリプッシュなし。
POST /api/v1/pipe/[path]?n=30サーバーは 30 すべての受信側が接続するまで送信側を保持。n の不一致は 400 を返す。事前接続済みの受信側でも問題ない。
curl https://pipe/.../build?n=30 | tar x各ランナーが同一のバイトを取得。バックプレッシャーは送信側の帯域幅ではなく、最も遅い受信側から流れる。
何も永続化しない。何もキャッシュされない。パイプは接続を仲介し、その後身を引く。最も遅いランナーが終わると、転送は終わる — そして URL は消える。
ナイーブ: 同じ 800 MB の tarball を 30 回レジストリプル、30 のコールドキャッシュ、30 のネットワークラウンドトリップ。パイプ: 1 エグレス、1 転送、最も遅い受信側がペースを設定。
12 秒
ラインレートで 1 つのエグレス。最も遅い受信側がペースを設定するが、誰も再ダウンロードしない。
1× / ビルド
バイトはビルダーから一度だけ出る、パイプでファンアウト。S3 GET 料金なし、Docker Hub プルなし。
0 バイト
パイプはディスクに何も保持しない。クリーンアップするレジストリなし、無効化するキャッシュキーなし。
壁時計時間の数値は、ビルドコンテナと同じ地域ネットワーク上の 30 ウェイマトリックスを想定; 地域間転送はパイプではなく、地域間帯域幅でゲートされる。
ビルドが 1 つの URL と 30 の curl になると、CI 足場のスタックが消える。エイジアウトするアーティファクトストレージなし。ローテーションするレジストリ認証情報なし。デバッグするキャッシュアクションなし。
バックプレッシャーはパイプに組み込まれている。速いワーカーは遅いワーカーを待ってレジストリラウンドトリップを無駄にしない — パイプで待ち、その後同じレートで吸収する。誰も再ダウンロードしない。
レジストリにプッシュされるものがないので、何かを認証する必要がない。URL 自体が認証情報 — 短命で、1 つの転送にスコープされ、ビルドが終わると追い出される。
バイトはビルダーから一度だけ出る。パイプがブロードキャスト。マトリックス実行ごとに 30 のレジストリプルではなく、ビルドごとに 1 エグレスを支払う。
パイプはキーごとではなく、ビルドごと。ミスヒットする GitHub Actions キャッシュなし、buildx レイヤーの謎なし、先週の main からの古い tarball なし。
同じパターンが node_modules、.pnpm-store、target/、wheel キャッシュ、データセットシャードを処理。ストリーミングできるなら、ファンアウトできる。
1 送信側。30 受信側。S3 請求書ゼロ。
90 秒と S3 ヒットを要した 30 ウェイのプッシュが、12 秒と 1 つのエグレスで完了。誰も再ダウンロードしない。レジストリ認証情報はローテーションされない。マトリックスが完了すると URL は自動退避。
マトリックス CI フローが通常組み立てるピース — レジストリ、キャッシュアクション、ミラー、カスタムアップロードステップ。パイプはそれらを 1 つの URL に折りたたむ。
同じ tarball を 30 回プッシュするのを止めよう。一度プッシュ。30 の curl にストリームを共有させよう。