最近、Zeabur は zbpack v1 既存インフラの廃止に伴い、新世代のビルドシステム(zbpack v2)への更新をすべてのユーザーにプッシュしています。移行の過程でいくつかの互換性の問題が判明しており、本記事では問題の背景、対応方法、そして同様の問題に遭遇した場合の緩和策について説明します。
Zeabur の背後で動作するビルド基盤は、次のように設計・運用されています。
「registry v1」は distribution/distribution を用いて構築していましたが、様々な問題が生じたため、自社設計の registry v2 への移行を進めました。
blob unknown
を返し manifest のアップロードを拒否し、起動時にイメージを取得できないことがありました。distribution/distribution
はグローバルな blob の重複排除を行う傾向があり、全 manifest を読み出さずに「不要な blob」を把握できませんでした。公式のガベージコレクションは stop-the-world が前提で、Zeabur 規模のレジストリでは実行不能です。その結果、R2 に blob が大量に蓄積し、深刻なパフォーマンス劣化を招いていました。そこで registry v2 では、「OCI イメージをビルドした後、R2 バケットへ直接プッシュ」し、Cloudflare Workers を用いて、バケット内の OCI 構造を OCI Distribution Specification の Pull API と互換な read-only API として提供する設計にしました。これにより大幅な性能向上と multipart アップロードの最大化が可能になり、レジストリ由来の問題も回避できます。同時に、blob の重複排除をリポジトリ内に限定したため、「リポジトリを削除すれば関連する blob を GC できる」ようになり、stop-the-world なしで運用管理を大幅に簡素化できました。
ただし、この設計変更により registry v2 の push フローは大きく変わりました。zbpack v2 では実装済みですが、zbpack v1 は push フローが複雑で、かつ image ビルドを buildkit CLI に大きく依存しているため対応が難しく、直近 1 か月ほどは zbpack v1 のプロジェクトを引き続き zbpack v1(registry v1)でビルドし、ユーザーから「起動できない」という報告があった場合にのみ手動で zbpack v2 へ切り替える運用としていました。
しかし、registry v1 の負荷は増す一方で、エラー頻度も右肩上がりとなり、同様の問い合わせが急増したため、やむを得ず「イメージ関連の処理を zbpack v1 から zbpack v2 へ切り替える」判断を検討することになりました。
鋭い開発者の皆さんはお気づきかもしれませんが、zbpack (v1) の GitHub リポジトリ は Archived からメンテナンス再開へと戻り、Dockerfile 関連の更新が多数入っています。これは、zbpack v1 を zbpack v2 へ接続するための「互換レイヤー」を準備していたためです。
私たちは、zbpack v1 の Dockerfile 生成機能は維持しつつ、Dockerfile 生成後のビルド処理は v1 の内蔵ロジックを使わずに v2 へ切り替える方針にしました。そこで、v1 の既存の Dockerfile 生成機能を公開関数化し、ビルドサービスがその関数で Dockerfile を生成してから、zbpack v2 を実行しているビルドマシンへ渡すようにしました。
とはいえ、zbpack v1 は元来ビルドマシン上で動作する前提で設計されており、実装やブリッジが必要な部分が多数存在しました。
このため、私たちは zbpack v1 互換レイヤーを実装し、ビルドマシンからの v1 呼び出しを移植しました。コード読取りのような明白な問題は全体配信前に解決しており、テストマシンでの内部テストでも誤判定はありませんでした。また、ロールアウトの影響を継続監視する on-call エンジニアも配置していました。しかし実際に全マシンへ配信したところ、互換レイヤーで考慮漏れとなっていた問題が多数見つかりました。例えば次の通りです。
ZBPACK_
で始まる変数が無効になっていた。ZBPACK_DOCKERFILE_NAME
の挙動が以前と一致しなかった。これらは、対応するテスト環境を用意しにくいケースがあったこと、またロールバックの影響がより大きくなる可能性があったことも踏まえ、on-call 時には「修正 → テスト → 配信」を素早く回す判断をしました。同時に、お客様へは一時的な回避策(workaround)も提供しました。8/26〜8/27 の期間で、zbpack の互換レイヤーをおおむね完成させ、plan type / plan meta の表示にも対応し、さらに registry v2 のダウンロードを高速化するため本土向け CDN を設計・導入しました。
互換レイヤーでカバーし切れていなかったエッジケースをご報告いただいたすべてのお客様に、心より感謝申し上げます。いただいたご報告により、私たちは迅速に調査・修正を進めることができました。
上記で挙げた問題は、互換レイヤーにおいて実装済みまたは修正済みです。もし他にも不足があれば、ぜひチケットを作成いただき、エンジニアにお知らせください。
現在判明している既知の問題は次の通りです。
PORT=<公開ポート>
(例: PORT=8080
)を手動で設定することで緩和できます。今回のインシデントの核心原因は、主に次の点にあります。
今回の影響を受けられたお客様には、影響時間に応じたクレジットによる補償をご提供いたします。今後この分野の機能を配信する際には、より一層慎重に進めてまいります。互換レイヤーの問題をご指摘いただいたすべてのお客様に、重ねて感謝申し上げます。