LayerX エンジニアブログ

LayerX の エンジニアブログです。

Langfuse 用の ClickHouse 冗長構成を AWS Fargate で実現する

バクラク事業部 Platform Engineering 部 SRE グループの uehara です。

この記事では LLMOps プラットフォーム Langfuse のストレージとして利用する ClickHouse について、AWS Fargate で実現したクラスタ構成を紹介します。

背景

LayerX では行動指針を 「Bet Technology」から「Bet AI」へ変更 するなど、数あるテクノロジーの中でも AI や LLM に注力しています。

LLM を用いた機能開発や検証を進めるうえで LLMOps プラットフォームとして Langfuse を利用することとなりました。LLMOps とは、プロンプトやモデルの管理、生成された結果の可視化など、LLM の性能を最大限に引き出すための開発・運用プロセスを指します。

Langfuse は、トランザクションやキャッシュなどのストレージとして複数のデータストアを扱いますが、そのうちトレースや分析にはオープンソースの分析向けデータベースである ClickHouse を使用しています。 今回は本番運用を見越した冗長構成を実現するため、ClickHouse をレプリケーション構成で構築しました。

システム構成

ClickHouse は複数サーバでのレプリケーション構成に対応しています。それぞれのサーバがストレージを持ち、シャード単位でデータを分割して複製管理する仕組みです。

DML クエリはいずれかのサーバで実行された後、レプリケーション機能によって他サーバへ非同期で複製されます。DDL クエリはすべてのサーバで実行される分散クエリとして扱われます。

clickhouse.com

クラスタ構成では、複数サーバ間でのデータ同期や整合性を維持するシステムとして ClickHouse Keeper (Zookeeper 互換) のサーバが奇数台必要です。

構築したシステムの大まかな構成を以下に示します。(サービス内の一部通信は省略)

Langfuse / ClickHouse システム概要図

Langfuse はオートスケールによる複数台構成、ClickHouse は2台、ClickHouse Keeper は3台で構築しています。

ブラウザから ALB を経由して Langfuse がリクエストを受け、Langfuse がストレージにアクセスする際は NLB を経由していずれかの ClickHouse へルーティングされます。ClickHouse と ClickHouse Keeper 間はロードバランサーを経由せず直接通信します。(後述)

今回は ECS + Fargate でこの構成を実現しました。実現にあたって解決が必要だった課題についてご紹介します。

課題1: サーバ固有設定の管理

ClickHouse のクラスタ構成ではサーバごとに固有の ID や設定値が必要です。 そのため、単純に ECS Service のタスク数を増やすだけでは同じ設定のサーバが複数起動してしまい、正しく構成できません。

解決策として、以下の案を比較・検討しました。

  • EC2 インスタンス
  • ECS on EC2
  • ECS on Fargate

バクラク事業部では基本的に ECS on Fargate の構成を取っており、この構成を採用することでインフラ管理やデプロイにおいて既存の仕組みに乗れるという利点を評価しました。

最終的には1サーバごとに1つの ECS Service を構築し、以下の5サービスを立ち上げています。

  • ClickHouse #1
  • ClickHouse #2
  • ClickHouse Keeper #1
  • ClickHouse Keeper #2
  • ClickHouse Keeper #3

サーバ固有の設定値については、ClickHouse が持つ from_env 機能を用いて、環境変数から注入する形としました。これにより、設定ファイルやコンテナイメージを共通化し、タスク定義の環境変数で1号機・2号機・3号機の差分を管理しています。

課題2: EFS 同時アクセスの回避

データ保存先のファイルシステムとして、容量に柔軟性のある EFS を選択しました。しかし ClickHouse はファイルシステムの共有を想定していないため、複数タスクから同時にアクセスすると不整合状態となり正常に動作しません。

ここでも先ほどの ECS Service 分離と同様に、サーバごと (ECS Service ごと) に EFS リソースを用意して対応しました。

なお、サービスごとに EFS を分離しても、タスクをローリングデプロイすると新旧タスクが同時に EFS へアクセスする瞬間が生まれてしまいます。この課題については ECS Service のデプロイ設定を min: 0%, max: 100% とすることで旧タスクの終了後に新タスクが起動する流れとなり、競合を回避できます。

また ClickHouse #1 と ClickHouse #2 のデプロイが同時に走ると、それぞれのタスクが0となる瞬間が重なった場合に全断するため、同時にデプロイされないような制御も入れています。

課題3: 内部通信の制御

ClickHouse のクラスタ構成ではサーバ間のレプリケーションやクラスタ維持のため内部通信が発生します。

これらは各サーバと 1:1 の形でやり取りが行われます。例えば ClickHouse #1 は ClickHouse Keeper #1, #2, #3 を識別して通信できる必要があります。そのため、単純にロードバランサーに ClickHouse Keeper 3台をぶら下げるだけでは要件を満たせません。

今回は、ClickHouse と ClickHouse Keeper の通信をすべてタスク間の直接通信とし、名前解決に Service Discovery (Cloud Map) を採用しました。前述の5サービスそれぞれに固有のドメイン名を振ることで、個別の通信を可能としています。

内部通信の概要図 (サービス内部の横の通信は省略)

なお、ECS Service Connect の利用も検討しましたが、ECS Service Connect では単一ホスト名で1つのポートしかアクセスできないため上手く利用できませんでした。

ClickHouse, ClickHouse Keeper ともに複数のポートを使用するため、今回は Service Discovery で名前解決のみを行ってタスク間は直接通信しています。

課題4: EFS コストの最適化

ClickHouse は大量のデータを扱うため、EFS のデータ量増加に伴ってコストも増大することが予想されます。

ClickHouse では S3 をディスクとする構成に対応しており、テーブルデータを S3 にオフロードして EFS の利用量を抑えることが可能です。なお、S3 を用いる場合でもメタデータの管理等で EFS は引き続き必要となります。 clickhouse.com

S3 も同様に ClickHouse #1, #2 用にそれぞれバケットを用意しています。レプリケーション構成のためデータは重複してしまいますが、耐障害性を高めるためのコストとして許容し、コストを継続的に監視することとしました。

【追記】 S3 で稼働を開始しましたが、パフォーマンスの観点から EFS に戻しました。以下の記事も合わせてご覧ください。 tech.layerx.co.jp

動作検証

これまでに紹介した各リソースを構築した後、動作検証を実施しました。

  • Langfuse からの動作確認
    • Langfuse へ送信したトレースデータ等が正常に反映されること
  • レプリケーション確認
    • 片方の ClickHouse に書き込まれたデータが他方にもレプリケートされること
  • ClickHouse / ClickHouse Keeper ノード障害
    • タスクを1つ停止させ問題なく動作すること
    • 停止したタスクを再度起動し、ClickHouse が正常に動作すること

まとめ

Langfuse 用の ClickHouse データベースを AWS Fargate の冗長構成で構築した際の課題と解決策について紹介しました。

サーバ固有の設定や EFS の制約については ECS Service やリソース分離で対応、内部通信の要件については Service Discovery (Cloud Map) を用いることで解決できました。

固定の EC2 インスタンスを複数台立ち上げるよりもやや複雑な構成ですが、他サービスと同様に AWS Fargate で構築することで運用面のコストを低減できています。

この記事や構成の一部でも参考になれば幸いです。

お知らせ

私の所属する SRE グループでは、ともにプロダクトの信頼性を高めていく仲間を募集しています。少しでもご興味がありましたらぜひご連絡ください!

open.talentio.com

jobs.layerx.co.jp