Amazon Web Services ブログ

Karpenter の活用: AWS ソリューションを使った Kafka の Amazon EKS 移行

AppsFlyer は、モバイルアトリビューションとマーケティング分析のグローバルリーダーです。AppsFlyer は、包括的な計測プラットフォームとプライバシークラウドを通じて、顧客のプライバシーを守りながらエコシステムの協調を促進することで、企業がすべてのチャネルとデバイスにわたるマーケティング活動の影響を理解することを支援します。

AppsFlyer 内では、データがコアであり、詳細な分析を公開し、顧客がキャンペーン活動のどこに焦点を置くか 正しい決定を下せるようになります。データのバックボーンは Apache Kafka の助けを借りて管理されており、1000 を超えるマイクロサービスのタイムフローを処理し、最大 50 を超えるクラスターに分散し、最大 800TB のデータを保持しています。

従来の Kafka インフラストラクチャでは、伝統的なセットアップを採用しており、各 Kafka ブローカーが専用の Amazon Elastic Compute Cloud (Amazon EC2) ノードにデプロイされていました。このシステムは、Chef、Terraform、サードパーティのサービスなど、さまざまなツールによって管理されており、それらは複数のGitプロジェクトに分かれていました。

この状況は、インフラストラクチャの変更のたびに複雑な依存関係の考慮が伴うため、、管理が複雑になりました。各コンポーネントは、Kafka のアップグレードなどの些細なタスクでさえ、慎重な検討、テスト、承認が必要でした。この複雑さにより、チームの作業負荷と複雑さが大幅に増え、その結果、AppsFlyer の内部研究開発 (R&D) に投資できるリソースが減少しました。

そのため、AppsFlyer Platform グループが、従来の Kafka インフラストラクチャを Kubernetes に再設計する課題を受けた際、我々はさまざまな側面で Kafka システムを成長・改善する機会と捉えました。主な目標は、クラスターをより高度で自動化された高性能かつ管理が容易なインフラストラクチャに移行することでした。これにより、クライアントとチームの日々の運用の両方に恩恵がもたらされます。

この投稿では、Kafka アプリケーションを Kubernetes に移行したことで私たちの組織が実現した主な利点と、直面した課題、そしてその課題を克服するために採用した AWS のソリューションについて共有します。

Amazon Elastic Kubernetes Service による効率化

レガシーインフラストラクチャを再設計する際には、過去のインフラストラクチャと一致するソリューションを実装し、パフォーマンスを向上させ、簡単な管理と保守可能な自動化システムを提供する機会があります。加えて、コスト削減の余地も見込まれます。

幸いにも、Kubernetes に移行する際、AWS Cloud には複数のソリューションが用意されていました。

Kubernetes はコンテナ化されたワークロードを管理するための多目的なツールで、サービス検出、オーケストレーション、ストレージ、シークレット管理、自己修復機能など、さまざまな機能を提供します。

Amazon Elastic Kubernetes Service (Amazon EKS) は、AWS 上で Kubernetes クラスターを構築・維持を簡素化する完全マネージド型の Kubernetes サービスです。AWS の主要サービスと統合されているため、ステートフルアプリケーションに関する様々な AWS クラウドコンポーネントとの接続や対話が容易になります。

私たちのデプロイでは、Strimzi Kafka Operator を使用しています。これは、Kubernetes クラスター内で Apache Kafka を実行するプロセスを簡素化するプロジェクトです。これを選択したのは、Kubernetes 上で Kafka を効果的に管理するために専用に構築されたコンテナイメージとオペレーターを備えているためです。

AWS が提供する実装とツール (例えば External DNSAWS Load Balancer Controller) とオープンソースのツールを利用することで、私たちのニーズにあったデプロイメントを構築できました。大規模な Kafka の実行とクラスターの簡単なリバランス機構を提供する Cruise Control などのオープンソースツールを使用しました。さらに、リアルタイムでトピックのメッセージを観察できるユーザーインターフェース (UI) ツールである Redpanda-Console も使用しました。これらのツール群により、ストレージ、ネットワーク、アプリケーションなど、多層のインフラストラクチャを単一の Kubernetes サービスの下で管理できるようになりました。

異なる Git プロジェクトで個別のコンポーネントを管理する必要があった従来のインフラストラクチャとは異なり、1 つの中央集権化された Git プロジェクトの下で、すべてを 1 か所で管理しました。これにより、コラボレーションを改善し、関連リソースの可視性とトラッキングを向上させることができました。

Graviton による最適なパフォーマンス

コンテナオーケストレーターを決めた後、Kafka pod を実行する AWS インスタンスの種類を選択する必要がありました。

当初、コストパフォーマンスと強化されたローカル SSD ストレージを考慮して、従来の i3 インスタンスから改良された i3en インスタンスへの移行を計画していました。しかし、ベンチマークの際に、AWS から私たちにメリットをもたらす可能性のある別のインスタンスタイプが紹介されました。AWS Graviton です。

Graviton インスタンスは、さまざまなシステム利用シーンに合わせた多様なインスタンスタイプを備え、パフォーマンスと機能が向上しています。Graviton インスタンスは、ARM ベースのプロセッサを搭載していますが、我々のユースケースにとって最も重要なのは、IOPS とスループットが向上したローカル NVMe ストレージです。

従来のインフラストラクチャでは、Kafka ブローカーにインスタンスローカルストレージを使用することにしました。これは、入出力操作/秒 (IOPS) やフェッチ時間などのパフォーマンス要因を最大化するためです。外部ストレージでは、100 万以上の IOPS とミリ秒単位のレイテンシーという要件を満たすことができませんでした。

Kubernetes でローカルストレージを使用するのは比較的新しい概念です。このようなシステムでは、ノードを失うと、そのノード上のデータも失われるため、失われたデータを複製するための復旧時間が長くなります。

そのため、従来のインフラストラクチャと新しい Kubernetes インフラストラクチャの両方で、オンデマンドインスタンスを使用しています。これにより、ノードの終了と中断のインシデントを減らすことができ、Kafka が他のブローカーからデータを復元する必要がある回数を効果的に減らすことができます。これにより、複数のノードが終了することによる完全なデータ損失のリスクを回避できます。

しかし、高速なデータ復旧に関して、Graviton インスタンスはローカル NVMe SSD ストレージを提供しており、リアルタイムのバスデータベースやステートフルアプリケーションにとって大きな恩恵があります。

従来の i3.2xlarge インスタンス上で実行されている Kafka クラスターと、新しい im4gn.2xlarge Graviton インスタンス上で実行されている Kafka クラスターを比較すると、スループットが 75% 向上、CPU 消費が 10% 低下、そして特に注目すべきは、書き込み I/O のパフォーマンスが 58% 向上、読み取り I/O のパフォーマンスが 92% 向上するという驚くべき結果が得られました。

今に至るまでの経緯を振り返ると、im4gn Graviton インスタンス上の新しい Kubernetes アーキテクチャで Kafka を実行しているわけですが、CPU コア数を半減させ、コストを 50% 削減できたと自信を持って言えます。さらに、カーボンフットプリントも低減できるという利点もあります。

Kubernetes、Kafka、ローカルストレージの力

Graviton インスタンスを選択した後、Graviton の Local SSD ストレージの利点を活用したいと考えました。ベンチマークでは、書き込み I/O パフォーマンスが 58% 向上し、読み取り I/O パフォーマンスが 92% も大幅に向上したことが確認できました。これは無視できない事実でした。

ローカルストレージには、ノードの障害ごとに新しいブローカーが起動してデータを復元する必要があるため、復旧時間が遅くなるという課題がありますが、私たちはその課題に取り組むことにしました。単一の Kafka pod が存在するノードと同じ場所にストレージを配置することで (外部ストレージを使用するのとは対照的に)、プロデューサーとコンシューマーにパフォーマンス向上の恩恵をもたらすことができます。

まず、Kafka の  Persistent Volume Claims (PVC) を対応する  Persistent Volume (PV) に割り当てる管理を行うプロビジョナーを選択する必要がありました。そして、これらの PV は Graviton インスタンスによって提供されるローカル SSD ストレージにマッピングされます。

最終的に、Rancher の Local Path Provisioner オープンソースプロジェクトを使用することにしました。このプロビジョナーは、ローカルタイプのストレージの PVC を監視し、ノード上に PV を自動的に作成するとともに、PVC と PV のバインディングもします。

次に、ノードの障害シナリオについて考える必要がありました。つまり、基盤となるノードが障害を起こすと、そのノードがホストしていたローカルストレージが削除されます。これにより、新しい Kafka pod は、ストレージが再度利用可能になるのを待つため、ステータスが Pending になります。。

この状況に対処するため、私たちは Local PVC Releaser という独自の社内オープンソースコントローラーを開発しました。このコントローラーは、計画的または計画外のノード終了に関する Kubernetes イベントを監視します。ノードの終了を検出すると、コントローラーは自動的に終了したノードにバインドされていた PVC (ローカルストレージタイプ) を削除します。これにより、Strimzi Operator が PVC を再作成し、pod を新しいノードに安全に割り当てられます。そして、データの複製を開始されます。

Persistent Volumes Diagram

Persistent Volumes の図

各 Kafka pod を単一ノードで実行するように設定し、ノードの障害が単一のブローカーにのみ影響するようにすることで、ノイジーネイバー問題を回避し、Kafka クラスターの可用性を高めることができます。

ステートフルアプリケーション向け Karpenter による Amazon EKS 自動化の最適化

最終的な構成を決定した後、次のステップはオートスケーラーを導入して、このソリューションのポテンシャルを最大限引き出す自動化をすることでした。当初、私たちは Kubernetes Autoscaler ソリューションを試し、EKS クラスターでのオートスケーリングイベントを処理しようとしました。

しかし、Kafka pod でインスタンスローカルデータストレージを使用したり、大規模な障害から保護するためにKafka pod を異なるアベイラビリティーゾーン (AZ) に分散させることで、複雑さが増したため、プロダクションではスケーリングイベントや障害イベントインシデントに即座に対応する必要がありました。

そのとき、AWS は 我々に Karpenter を紹介してくれました。Karpenter は AWS が立ち上げ、支援するオープンソースプロジェクトで、Kubernetes ノードのオートスケーリングソリューションとして機能します。Kubernetes pod の要件と制約を考慮して、コストとリソース効率を最適化するように動作します。Karpenter は、より柔軟性のあるスケーリングを目指しています。

Karpenter は主に 3 つの点で私たちを助けてくれました。

1. スピード

Kafka と Kubernetes には多くの自己修復機能がありますが、新しい AWS インスタンスを起動して EKS クラスターに追加できる速度は、私たちとユーザーにとって非常に重要です。

Karpenter は、スピーディな応答時間で、pod が新しいリソースを要求したことを自動的に認識します。Karpenter は、設定 (私のチームがプログラミングしたもの) で要求されたとおりにインスタンスをデプロイしますが、この時 Karpenter は pod の要件も考慮します。この様なロジックを通して、我々はインスタンスのデプロイ時間と復旧時間を Kubernetes autoscaler では 9 分程度を想定していましたが、Karpenter を使用することで 1 分未満に短縮できました。

2. ローカルストレージの認識

この大幅な削減は、Karpenter のローカルストレージ認識機能によるものです。Karpenter には、ストレージスケジューリング要件を自動検出し、トポロジースプレッドとハードウェア仕様と組み合わせてノードの起動判断に統合する機能があります。

ローカルストレージの認識は、Kubernetes autoscaler が提供していない機能でした。Kubernetes autoscaler は、pod のローカルストレージをどこに配置すべきかを直接認識していないためです。一方で、Karpenter は自動的にローカルストレージを認識し、正しい AZ にインスタンスをデプロイします。

3. 大幅なコスト削減

Karpenter の最適化により、大幅なコスト削減が実現されます。Karpenter は、EKS クラスター上で実行されているデプロイアプリケーションをサポートするために必要なインスタンス数だけでなく、コスト効率を最適化するための特定のインスタンスファミリーとサイズを計算できるためです。

この機能は、Kafka クラスターと並行して動作するサードパーティツールの pod に役立ちます。これらの pod はステートレスなので、Karpenter が便利です。Karpenter は、インスタンスタイプとサイズでサポートできる pod の数を自由に決定できます。これは、使用頻度が低い別のインスタンスをデプロイする必要がないよう、同じインスタンス上に戦略的に pod を配置することで実現されます。

これらすべての結果から、ステートフルアプリケーションを Amazon EKS 上で実行する際のニーズを満たしたため、Karpenter を主要な自動スケーリングソリューションとして完全に利用することを決定しました。

Kafka の Kubernetes への移行

この投稿で説明した AWS ツールと、この投稿では説明しきれないその他多くのツールを使って、新しいアーキテクチャを確立した上で、AppsFlyer チームは 従来の Kafka クラスターを新しく改善された Kubernetes 上の Kafka インフラストラクチャに 2 か月足らずで移行することに成功しました。

この移行により、「Produce」リクエストと「Consume」リクエストの時間の大幅な性能向上、ディスク I/O 書き込み性能の強化、ノード障害発生時の高速かつ柔軟な復旧が実現し、ユーザーにメリットをもたらしました。さらに、これらの要素は Terraform でラップされているため、簡単に管理でき、AppsFlyer チームはクラスターを最新で健全な状態に保つことができます。

これにより、今日では、この基盤を使って 50 を超える本番 Kafka クラスターを管理しています。以前の設定と比べて CPU コア数が半分になり、驚くべき 30% のコスト削減が実現しました。

新しく得た知識と既に確立されたビルディングブロックを武器に、私たちは今や Kubernetes 上でより多くの種類のステートフルなアプリケーションを、はるかに短い時間で構築できるようになりました。

Kafka on EKS with Local NVME Diagram

EKS 上の Kafka とローカル NVME の図

翻訳はソリューションアーキテクト祖父江が担当しました。原文はこちらです。