Zalandoのエンジニアリングチームは最近、毎秒約100万件のリクエストを処理する高スループットAPI向けのインプロセス型クライアントサイドロードバランサーの設計と実装について説明した。その結果、レイテンシの予測可能性が向上し、インフラコストが削減されるとともに、障害が実際にはどこで発生しているのかを、より正確に把握できるようになった。
ZalandoのProduct Read APIは、欧州最大級のオンラインファッション小売業者の25市場において、一桁ミリ秒のレイテンシで毎秒数百万件のリクエストを処理できるよう設計されている。このAPIのバッチエンドポイントは、1件のリクエストを最大100件の個別商品Podへの高ファンアウト(多数並列呼び出し)に分岐させ、それぞれがクラスター共通のエッジロードバランサーである「Skipper」を経由する。バッチ処理は100回のホップのうち最も遅い応答を待つ必要があり、しかもそのインフラはチームの管理外だったため、Skipperによるレイテンシの急増と自分たちのシステム起因の問題を切り分けることができなかった。この課題に対処するため、チームは高ファンアウトな内部トラフィックのルーティングをプロセス内で実行する方式へ移行し、一方でエッジトラフィックや単一GETリクエストについては引き続きSkipperを利用することにした。Zalandoのシニア・プリンシパル・エンジニアであるConor Gallagher氏次のように説明する。
「高ファンアウトな内部トラフィックについては、ルーティングの判断を呼び出し元プロセス自身の内部で行うべきだと私たちは判断しました。一方で、Skipperが優れた性能を発揮しているエッジトラフィックについては、そのまま維持すべきです。私たちはSkipperを置き換えようとしたのではありません。内部の高ファンアウト経路を、プロセス内で動作するクライアントサイドロードバランサーという、より適切な専用アーキテクチャへ移行したのです。」
/filters:no_upscale()/news/2026/07/client-side-load-balancer/en/resources/1cslb-consistent-hash-load-balancing-after-1783270121535.jpg)
product-sets から product pod への直接ルーティング。出典:Zalandoブログ
チームは移行期間中に両方の経路で同じキーが同じPodへルーティングされるよう、Skipperのアルゴリズム(xxHash64とエンドポイントごとに100個の仮想ノード)をそのまま再実装した。これによりキャッシュの分断を回避でき、さらに単体テストによってこの挙動が維持されるようにした。Gallagher氏は次のように述べている。
これは、エンドポイントの追加や削除が行われた際に、再マッピングが必要になるキーが全体の約1/Nだけにとどまり、キャッシュの不要な再配置や再ウォームアップを最小限に抑えられることを意味します。また、Skipperと私たちのライブラリは同じハッシュ関数と同じ数の仮想ノードを使用しているため、同じPodセットに対しては完全に同一のハッシュリングを生成します。
この記事では、クライアントサイドルーティングに関するエンジニアリング上の意思決定と課題について取り上げており、一貫性ハッシュ、使用率ベースのロードバランシング、キャッシュを考慮したスケーリング、段階的なロールアウト戦略、オブザーバビリティの向上、さらにアベイラビリティゾーン単位でのルーティングに関する実験などが解説されている。
チームは、コントロールプレーンをクラッシュさせるほど負荷の高いポーリング方式を、watchベースの Kubernetes Informerに置き換えた。また、完了まで数時間かかっていたデプロイパイプラインを、迅速に実行でき、必要に応じて容易にロールバックできる仕組みへと再構築した。さらに、ロードバランサーをトグル機能の背後で1%から100%まで段階的に展開した結果、SkipperのPod数を50以上から8まで削減し、デプロイにかかる1日当たりのコストを450ドルから110ドルへ引き下げた。スケールアップ時のレイテンシ急増を解消するためには、30秒にわたってNリングへ段階的にトラフィックを流すフェードイン機構を2.5乗カーブで導入し、新しいPodが実際に担当する製品データだけをウォームアップするようにした。AmazonのCTOであるWerner Vogels氏は次のように述べている。
私は、さまざまな理由からこれをクライアントサイドで実装することにはあまり賛成していません。しかし、Zalandoチームによるこのエンジニアリングは見事だと思います。また、彼らが最後にまとめている「得られた教訓(lessons learned)」も気に入っています。
AWSのプリンシパルエンジニアであるAlexey Kuznetsov氏は、次のようにコメントしている。
AmazonからGoogleへ移ったとき、クライアントサイドロードバランシングは私にとってある種の啓示でした。(中略)こうした考え方は、もっと広く認識されるべきだと思います。
AZ(アベイラビリティゾーン)を考慮したルーティングによってAZ間通信コストを削減しようとしたものの、その結果としてキャッシュが分断され、DynamoDBの読み取りリクエストが急増してしまった。一方、新しいアプローチでは、ジッターを加えた1回のリトライ、FIFO方式による過負荷時のリクエスト削減(overload shedding)、およびより詳細で情報量の多いロギングを導入することで通信経路の堅牢性を高めた。その結果、ノードレベルで発生する短時間のフリーズを可視化できるようになり、ロードバランサーがそれらを自動的に回避できるようになった。
Zalandoが独自のクライアントサイドロードバランサーを構築したのは、本当に特殊で極端な要件を抱えるケースだったからにすぎないと強調したうえで、Gallagher氏は次のように締めくくっている。
独自に構築すべきでしょうか。ほとんどのケースでは、その答えは『やめておくべきです』です。SkipperやEnvoyのような成熟したプロキシは、一貫性ハッシュによるルーティング機能を標準で提供しています。しかも、それらは別の開発チームによって保守され、多くのユーザーによる実運用を通じて十分に鍛えられています。
Hacker Newsでは、あるユーザーが独立したロードバランサーモジュールを開発するという選択に疑問を呈している。
この記事で説明されているアプローチは巧妙ですし、その成果も印象的なので、まずはチームに敬意を表したいと思います。しかし、読み進めるうちに少し違和感がありました。というのも、Skipperは彼らの同僚たちによって開発・運用されているにもかかわらず、まるでサードパーティー製の依存コンポーネントのように扱われていたからです。Skipperに改善や機能追加をコントリビュートしたり、あるいはSkipperのサービスディスカバリー機構を再利用したりすることもできたのではないでしょうか。
Skipper はオープンソースとしてGitHubで公開されている一方で、この新しいクライアントサイドロードバランサーは公開されておらず、Zalando社内でのみ利用されている。