Langfuse セルフホストでハマったポイントをまとめてみる

こんにちは、AI チームの長澤 (@sp_1999N) です。

この記事はAI Shift Advent Calendar 2025の16日目の記事です。

弊社サービスの AI Worker Platform では AI エージェントの監視基盤として Langfuse を採用しています。

Langfuse をはじめとしたエージェント監視基盤については、こちらの記事でまとめておりますので、よければご覧ください。

さて、この記事では Langfuse を (GKE で Helm dependency を使って) セルフホストするときに、個人的にハマってしまったポイントを整理してご紹介します。

ドキュメントを詳しく読めばわかるものも含まれますが、何かの参考になれば幸いです!

* 本記事は Langfuse v3 を対象としています

ハマりポイント:環境変数

Langfuse をセルフホストする場合、ローカルであれば Docker Compose を、クラウド (k8s) であれば Helm chart を使ってデプロイします。

この時、設定すべき環境変数が多数あります。ドキュメントには丁寧にまとめられており、また GitHub リポジトリを参考にすれば、Docker Compose を使ってのローカル環境では比較的簡単に立ち上げられます。(実際に git clone して docker compose up すればすぐに使うことができます)

必須なものが必ずしも必須とは限らない

ただ少し注意が必要なのが「一見 required として紹介されているが、他の環境変数と組み合わせると必須でなくなる」ものが存在する点です。例えばドキュメントでは REDIS_CONNECTION_STRING は必須ですが、リポジトリで紹介されている Compose ファイルにこの環境変数は存在しません。代わりに REDIS_HOSTREDIS_PORT などが設定されています。

このように「組み合わせで代替できるもの」がいくつか存在します。ただ代替が可能なものは以下のように明記されているため、事前にドキュメントに目を通しておくことをお勧めします。

Connection string of your redis instance. Instead of REDIS_CONNECTION_STRING, you can also use REDIS_HOSTREDIS_PORTREDIS_USERNAME and REDIS_AUTH.

環境変数はもっとある

一方で Environment Variables に載っていない環境変数も設定できます。例えば、プロジェクトなどの初期セットアップを行うために必要な LANGFUSE_INIT_PROJECT_ID などの変数は各種機能紹介ページに散在しています。

従って、環境変数でハマらないようにするには、事前にドキュメントページを (Environment Variables だけでなく、関連しそうなページを全体的に) 読んでおくことをおすすめします。

ハマりポイント:Helm values

k8s を使ってセルフホストする場合、公式配布されている Helm チャートを使う形になります。ドキュメントを見ると、概ねアプリケーション設定ごとにどのように values ファイルに変数を設定すべきかが分かります。

ただし、Compose ファイルで設定した環境変数を values ファイルではどのように書けば良いかが明記されていないものもあります。

そんな時は langfuse-k8s のリポジトリにある README を見ることをおすすめします。README には必要な value やその指定方法が網羅されています。例えば langfuse.salt などフィールドが明確に用意されているもの以外は langfuse.additionalEnv にまとめて指定する形になります。

どこに何を書くかで迷子になる

ただ「何を additionalEnv に指定するか」にハマりポイントがあります。例えば SSO を実現するための環境変数として AUTH_GOOGLE_CLIENT_ID などがあり、この辺りのものは additionalEnv に指定すれば問題ありません。しかし、同じページで紹介されている AUTH_DISABLE_SIGNUP という環境変数を additionalEnv に設定するとチャートレンダリングエラーが発生することがあります(厳密な発生条件は validation を参照)

AUTH_DISABLE_SIGNUP には、これに相当する langfuse.features.signUpDisabled のフィールドが個別に用意されており、このフィールドから AUTH_DISABLE_SIGNUP の環境変数がヘルパー関数によって内部的に生成されます。

このほかにも「additionalEnv に見せかけて指定フィールドが存在する環境変数」が存在しており、ものによっては「同じ名前の環境変数が重複して k8s マニフェストに登録される」という事象にも遭遇しました。

このポイントにハマらないようにするには README と睨めっこしながらも「実際にレンダリングを行って、意図通りの環境変数が作成されているか」をチェックすることをオススメします。

ハマりポイント:ClickHouse

Langfuse ではトレース保存用データベースとして ClickHouse を内部利用します。ClickHouse に関して、ハマりポイントが2つあったのでご紹介します。

いつの間にかいる ZooKeeper

Langfuse のドキュメントではその存在を見かけないのですが、実際に k8s でデプロイすると ZooKeeper という Pod が立っていることに気が付きます。この正体は「ClickHouse が内部的に利用する分散コーディネータ」になります。ClickHouse を複数レプリカ立てる時に、分散クエリの調整やテーブルメタデータの管理などをしてくれます。

この時、nodeSelectortolerations を指定する場合、親の ClickHouse に指定するだけだと不十分になります。子である ZooKeeper にもフィールドが個別に用意されているため、ZooKeeper に対しても指定が必要になる点に注意が必要です。

逆に単一レプリカの non-HA 構成なら ZooKeeper がデプロイされないかというとそういうわけではなく、デフォルトでデプロイされるようになっています。単一レプリカ時は不要になるので、Helm チャートでデプロイされないように指定する必要があります。(e.g., clickhouse.zookeeper.enabled: false)

(一点余談としては、ClickHouse は分散コーディネーターとして ClickHouse Keeper を推奨しているようですので、将来的には Langfuse で ZooKeeper を意識することもなくなるのかなと感じています)

PVC 作成ゾーンに気をつけろ

これは Langfuse に限った話ではないですが、ClickHouse の Pod がうまく立たない時に遭遇したものになります。

ClickHouse には永続ボリュームが必要なため、PVC (PersistentVolumeClaim) が作成されます。この時、環境や設定によっては「PVC と ClickHouse Pod (k8s node) が存在するゾーンが互いに異なる」ことがあります。このような状況が発生すると、ClickHouse Pod が永続ボリュームをマウントすることができず、Pod が立ち上がってくれない事態が発生します。

これを避けるには clickhouse.persistence.storageClassstandard-rwo を指定するなどの対応が必要になります (GKE)。

standard-rwovolumeBindingMode として WaitForFirstConsumer を使用しているため、以下のように動作することでゾーン不一致の問題を回避できます。

  1. PodがスケジュールされるまでPVCのバインディングを遅延
  2. Podがスケジュールされるノードのゾーンに合わせてPVが作成される

おわりに

今回の記事では、弊社の AI エージェントサービスで利用している Langfuse について、個人的にハマってしまったポイントをご紹介しました。公式の Helm チャートをそのまま利用する場合は強く意識せずともうまく立ち上がる気もしますが、既存のクラスタに取り込むなど、Langfuse を Helm dependency でサブチャート化しようとした時に自分は苦しんでしまいました。

この躓きが誰かの参考になれば幸いです。

PICK UP

TAG