Grabは、Kafka 2.3で導入された、Apache Kafkaコンシューマーが同じアベイラビリティゾーン(AZ)内のブローカーノードに接続する機能を利用し、再構成されたコンシューマーのAWS上のトラフィックコストをゼロに削減した。この変更により、AWS上でApache Kafkaを実行するための全体的なインフラコストが大幅に削減された。
GrabはApache Kafkaを中心としたストリーミング・データ・プラットフォームを構築し、すべての会社の全製品をサポートしている。Kafkaのベストプラクティスに従い、彼らの初期構成では各Kafkaパーティションに3つのレプリカを使用し、AWSリージョン内の3つの異なるアベイラビリティゾーンにまたがっていた。このプラットフォームを担当するチームは、AWSがクロスAZデータ転送に課金するため、クロスAZトラフィックがKafkaプラットフォームのコストの半分を占めることを確認した。
Fabrice Harbulot氏とQuang Minh Tran氏は初期設定のコストについて言及している。
この設計の問題点はAZ間のネットワーク・トラフィックが膨大になることだ。デフォルトではKafkaクライアントがパーティション・リーダーとしか通信しないためで、そのリーダーは67%の確率で別のAZに存在する。
全体的なクロスAZトラフィックは新たに公開されたメッセージ、ブローカー間のデータ複製、コンシューマーが取得したメッセージを組み合わせたものだ。
コンシューマがパーティション・リーダーからデータを取得するデフォルトのコンシューマ構成(出典:Grab Engineering Blog)
Apache Kafka 2.3以降、コンシューマーがパーティション・レプリカからフェッチするように設定できた。これにより、コンシューマーが同じAZ内のブローカーのみからメッセージを取得する場合、データ転送コストをかけずにフェッチできる。
この機能では、Kafkaブローカーとコンシューマーの両方に、それらが存在するAZを認識させる必要がある。Kafkaブローカーについては、チームはbroker.rackに
AZ ID(az1、az2、az3など)の値を設定した。後者はAWSアカウント間で一貫性がないため、AZ名(1a、1b、1cなど)とは異なる。また、パラメータreplica.selector.class
にorg.apache.kafka.common.replica.RackAwareReplicaSelector
を設定した。
コンシューマー側では、チームは内部のKafka SDKを更新し、EC2ホストのメタデータに基づいてclient.rack
パラメータにAZ IDを設定することで、アプリケーションチームが環境変数をエクスポートして機能を有効にできるようにした。
コンシューマがもっとも近いレプリカからデータを取得するカスタムコンシューマコンフィギュレーション(出典:Grab Engineering Blog)
新しいセットアップをいくつかのサービスに展開した後、チームはAZ間のトラフィックコストが下がるのを確認し、いくつかの注目すべき副作用を発見した。まず、エンド・ツー・エンドのレイテンシが最大500ミリ秒増加した。これは、ほとんどのコンシューマーがレプリカからメッセージを取得することを考えれば、予想されることだ。したがって、レプリケーション時間が遅延の原因となっている。待ち時間の影響を受けやすいデータフローは、余分なコストがかかっても、常にパーティションリーダーからフェッチするのが理想的だ。
次に、ブローカーのメンテナンスの場合、レプリカから直接メッセージをフェッチするコンシューマーは、ダウンタイム中にブローカーが利用できなくなる可能性があるため、同じAZのブローカーがオンラインに戻るまで待機/リトライする必要がある。最後に、チームはAZ間のコンシューマー数に関連するブローカーの負荷の偏りを確認した。これはブローカーのバランスのとれた負荷を確保するためには、コンシューマーの均等な分散が不可欠であることを意味する。