🌟

Google Cloud 上に Langfuse v3 本番環境をセルフホスティングしてみた

2025/02/04に公開

はじめに

こんにちは。
クラウドエース株式会社 第一開発部の髙木です。

ここ数年のうちにすさまじい勢いで大規模言語モデル(以下、LLM) が発展しており、様々な LLM サービスが提供されるようになりました。
一方で、LLM の生成の監視やプロンプトの管理については、まだ世の中に浸透していないと考えています。

本記事ではそのような LLM の監視・管理に特化したアプリケーションである Langfuse の新バージョンのデプロイ手順を紹介します。

対象読者

以下の対象読者を想定しています。

  • Langfuse v3 を本番環境でデプロイしたい方
  • Cloud SQL + Cloud Run 構成の Langfuse v2 アプリケーションを v3 にアップデートしたい方
  • Google Cloud でサービスをデプロイした経験がある方

Langfuse v3 はシステム構成が複雑なため、1 箇所設定を間違えるとデプロイできないことがあります。
そのため、Cloud Run や Google Compute Engine (以下 GCE) でエラーが出た際に、ログを見て自力で解決できるのが望ましいです。
また、本記事では細かい設定の説明は省いています。ネットワークや Google Cloud の知識がある程度ないと置いていかれることが予想されます。

一方、Langfuse v2 は構成が簡素なので、初心者でもとっかかりやすいです。
「 Langfuse について」の章を読んで少しでも興味が湧いた方は、ぜひ弊社の遠矢の記事をご覧ください。

https://zenn.dev/cloud_ace/articles/2a3668221e9c90

Langfuse について

Langfuse とは

Langfuse は、LLM アプリケーションのデバッグ、分析、反復を行うのに役立つエンジニアリング プラットフォームです。

Langfuse は以下の機能を提供します。

  1. データの追跡と分析
    a. プロンプトと LLM の応答を追跡
    b. ユーザーセッションの記録
    c. コストと利用状況の監視
  2. デバッグ機能
    a. 詳細なトレース機能
    b. プロンプトのバージョン管理
    c. エラーの検出と分析
  3. 評価機能
    a. LLM の出力品質の評価
    b. パフォーマンス指標の測定


トレースの例

Langfuse v3 の特徴

2024 年 12 月 6 日に Langfuse v3 がリリースされました。

Langfuse の新バージョンを導入することで、スケーラビリティ、パフォーマンスの向上などが見込めるようになりました。
これは主にシステムの構成を変更したことで実現しています。

Langfuse v2 では、アプリケーションが 1 つ、ストレージが 1 つというシンプルな構成でした。ストレージは PostgreSQL を使用します。
Langfuse v3 では、アプリケーションが 2 つ、ストレージが 4 つの構成になっています。ストレージは PostgreSQL に加え、ClickHouse、Redis、Blob Storage を使用します。4種類のストレージを使い分けることで、大量のイベントを高い信頼性で処理できるようになります。

Google Cloud での構成

実際に Google Cloud でセルフホスティングする際の構成を説明します。

Langfuse v3 を Google Cloud に導入する方法として、最初に思いつくのは Google Kubernetes Engine(以下、GKE) を使用したセルフホスティングです。GKE でセルフホスティングする方法は GAO 株式会社の遠矢がこちらにまとめております。

SaaS 版 ClickHouse の ClickHouse Cloud は垂直スケーリングします。記事執筆時点で垂直スケーリングする Google Cloud のコンピューティングプロダクトは GKE しかないため、GKE を使用したセルフホスティングは Langfuse の推奨構成になると考えています。

しかし、GKE の費用は割高であるため、v3 へのアップデートを躊躇している方もいらっしゃると思います。
そこで、私は GKE を使用せずに、セルフホスティングできる構成を考えました。

以下は、本記事で紹介する構成のアーキテクチャ図です。

各コンポーネントに対する Google Cloud プロダクトの選定理由を記載します。

コンポーネント Google Cloud プロダクト 選定理由
Langfuse Web Cloud Run サーバーレスコンテナサービス
自動スケーリング
PostgreSQL Cloud SQL フルマネージドデータベースサービス
Langfuse Worker GCE 高パフォーマンス 仮想マシン
固定の内部 IP アドレス
Redis Memorystore for Redis フルマネージドキャッシュサービス
ClickHouse Managed Instance Group
(以下、MIG)
スケーリング可能
Blob Storage Google Cloud Storage
(以下、GCS)
マネージドストレージサービス
可用性・耐久性

Langfuse v2 のセルフホスティング

アーキテクチャ図を見比べるとわかる通り、Langfuse v2 環境を構築したのちに v3 環境を構築できます。
そのため、まずは Langfuse v2 のセルフホスティングをします。

Langfuse v2 は、弊社の遠矢が執筆した以下の記事の通りに構築します。
サブネット名は test-subnet にしています。

https://zenn.dev/cloud_ace/articles/2a3668221e9c90

ただし、以下の項目を変更します。

DATABASE_URL の文字列を変更する。

まず、CloudSQL の内部 IP アドレスをメモしておきます。

シークレットに登録する DATABASE_URL の文字列を以下のように変更します。

postgresql://postgres:PASSWORD@INTERNAL_IP/DB_NAME

それぞれの値は次のように設定します。

項目
PASSWORD db のパスワード
INTERNAL_IP 内部 IP アドレス
DB_NAME 任意で設定したデータベース名

Docker のタグは2

Cloud Run の [コンテナイメージ] を docker.io/langfuse/langfuse:2 にします。
現在 v3 環境ですので、latest を選ぶとデプロイ失敗します。

IAM の設定

以下のリンクから Langfuse v2 で作成したサービスアカウントに追加の権限を与えます。
https://console.cloud.google.com/iam-admin/iam

以下のロールを追加します。

ロール 追加理由
ログ書き込み Langfuse Worker のログの確認のため

ファイアウォール ルールの設定

以下のリンクからファイアウォール ルールを作成します。
https://console.cloud.google.com/net-security/firewall-manager/firewall-policies/list?hl=ja

  1. Langfuse Web のファイアウォール ルール
項目
名前 allow-langfuse
ネットワーク test-vpc
ターゲットタグ langfuse-worker
送信元 IPv4 範囲 test-subnet の IPv4 範囲
プロトコルとポート tcp: 3030
  1. Clickhouse のファイアウォール ルール
項目
名前 allow-clickhouse
ネットワーク test-vpc
ターゲットタグ clickhouse
送信元 IPv4 範囲 test-subnet の IPv4 範囲
プロトコルとポート tcp: 8123,9000
  1. ヘルスチェックのファイアウォール ルール
項目
名前 allow-health-check
ネットワーク test-vpc
ターゲットタグ clickhouse
送信元 IPv4 範囲 35.191.0.0/16, 130.211.0.0/22
プロトコルとポート tcp: 8123

Memorystore for Redis のセットアップ

以下のリンクから Memorystore for Redis のインスタンスを立ち上げます。
https://console.cloud.google.com/memorystore/redis/instances?hl=ja

公式ドキュメントによると、10 万イベント/分あたりの容量は 1 GB です。
容量は適宜調整します。

項目
ティア 基本
容量 1GB
リージョン asia-northeast1
ネットワーク test-vpc
接続 プライベート サービス アクセス
Set an IP range name test-vpc-ip-range
AUTH を有効にする オン
転送中の暗号化を有効にする オフ

以下のように構成を追加します。

以下の値をメモしておきます。後ほど Secret Manager に登録します。

記載箇所
プライマリ エンドポイント [接続]
AUTH 文字列 [セキュリティ]

Blob Storage の準備

以下のリンクから GCS バケットを作成します。
https://console.cloud.google.com/storage/browser?hl=ja

項目
ロケーションタイプ Region
リージョン asia-northeast1

次に HMAC キーを発行します。
GCS の [設定] から [相互運用性] へいき、HMAC キーを作成します。
通常はサービスアカウントのアクセスキーを使用します(参考リンク)。

以下の値をメモしておきます。

記載箇所
GCS バケット名 [バケット]
HMAC アクセスキー [設定] -> [相互運用] -> [HMAC キー]
HMAC シークレット [設定] -> [相互運用] -> [HMAC キー]

Cloud NAT のセットアップ

GCE でデプロイした langfuse-worker と GCS バケットを接続するために、Cloud NAT を構成します。
以下のリンクから Cloud NAT ゲートウェイを作成します。
https://console.cloud.google.com/net-services/nat/list?hl=ja

項目
NAT タイプ 公開
ネットワーク test-vpc
リージョン asia-northeast1
Cloud NAT マッピング VM インスタンス、GKE ノード、サーバーレス

MIG のセットアップ

インスタンステンプレートの作成

以下のリンクからインスタンステンプレートを作成します。
https://console.cloud.google.com/compute/instanceTemplates?walkthrough_id=compute--create-new-instance-template&start_index=1&hl=ja#step_index=1

項目
リージョン asia-northeast1
マシンタイプ e2-medium
ブートディスク Container Optimized OS
サービスアカウント v2 で作成したサービスアカウント
ネットワークタグ clickhouse
ネットワーク test-vpc
サブネット test-subnet
外部 IPv4 アドレス なし

また、起動スクリプトとして以下のコードを使用しています。
このコードでは /home でコンテナの起動とログの設置をしています。

#!/bin/bash

sudo mkdir -p /home/ch_data /home/ch_logs
cd /home

# Dockerの設定ファイルを作成
cat <<EOF > /etc/docker/daemon.json
{
    "live-restore": true,
    "storage-driver": "overlay2",
    "log-driver": "json-file",
    "log-opts": {
        "max-size": "10m",
        "max-file": "3"
    }
}
EOF

# Dockerサービスをリロード
systemctl reload docker

# Clickhouseコンテナの起動
docker run --rm --name clickhouse-server \
  -v $(realpath ./ch_data):/var/lib/clickhouse/ \
  -v $(realpath ./ch_logs):/var/log/clickhouse-server/ \
  -e CLICKHOUSE_USER=clickhouse \
  -e CLICKHOUSE_PASSWORD=clickhouse \
  -d --ulimit nofile=262144:262144 \
  -p 8123:8123 \
  -p 9000:9000 \
  clickhouse/clickhouse-server

この起動スクリプト内の docker に設定する環境変数は変更できます。
以下の値をメモしておきます。

項目
CLICKHOUSE_USER ClickHouse ユーザー名
(上記コードでは"clickhouse")
CLICKHOUSE_PASSWORD ClickHouse パスワード
(上記コードでは"clickhouse")

MIG の作成

以下のリンクから MIG を作成します。
https://console.cloud.google.com/compute/instanceGroups?walkthrough_id=compute--instance-groups--create-regional-mig&start_index=1&hl=ja#step_index=1

項目
インスタンス数 3
場所 マルチゾーン
ステートフル構成 ディスクと内部 IP をステートフルに
ポートマッピング http->8123, clickhouse->9000

インスタンス数などの構成は ClickHouse Cloud に合わせています。詳しくは ClickHouse のスケーリングをご覧ください。

Load Balancer のセットアップ

以下のリンクから内部アプリケーション Load Balancer を作成します。
https://console.cloud.google.com/net-services/loadbalancing/list?hl=ja

項目
ロードバランサのタイプ ネットワーク ロードバランサ(TCP / UDP / SSL)
プロキシまたはパススルー パススルー ロードバランサ
インターネット接続または内部 内部
リージョン asia-northeast1
ネットワーク test-vpc

バックエンド サービス

項目
バックエンドタイプ インスタンスグループ
プロトコル TCP
インスタンス グループ 作成した MIG
ヘルスチェックのプロトコル tcp
ヘルスチェックのポート 8123

フロントエンド サービス

項目
サブネットワーク test-subnet
ポート 複数
ポート番号 8123, 9000

以下の値をメモしておきます。

記載箇所
Load Balancer の IP アドレス Load Balancer のフロントエンド

Secret Manager の追加

Langfuse v3 で追加された環境変数をシークレットに登録します。

名前 シークレットの値
ENCRYPTION_KEY openssl rand -hex 32で生成した値
CLICKHOUSE_URL http://LB_IP:8123
CLICKHOUSE_MIGRATION_URL clickhouse://LB_IP:9000
CLICKHOUSE_USER CLICKHOUSE ユーザー名
CLICKHOUSE_PASSWORD CLICKHOUSE パスワード
LANGFUSE_S3_EVENT_UPLOAD_BUCKET GCS バケット名
LANGFUSE_S3_EVENT_UPLOAD_ACCESS_KEY_ID HMAC アクセスキー
LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY HMAC シークレット
REDIS_CONNECTION_STRING redis://default:REDIS_AUTH@REDIS_HOST:6379

ただし、CLICKHOUSE_URLCLICKHOUSE_MIGRATION_URLREDIS_CONNECTION_STRING の値は次のように設定します。

項目
LB_IP Load Balancer の IP アドレス
REDIS_HOST Memorystore のプライマリエンドポイント
REDIS_AUTH Memorystore の AUTH 文字列

環境変数の詳細は公式ドキュメントを参照してください。

Langfuse v3 のセルフホスティング

langfuse web のデプロイ

以下のリンクから Cloud Run へ行きます。
https://console.cloud.google.com/run?hl=ja

Langfuse v2 のサービスをクリックし、[新しいディビジョンの編集とデプロイ] をクリックします。

  • コンテナイメージの URL を docker.io/langfuse/langfuse:3 にします。
  • 環境変数の設定では以下のように設定します。

  • シークレットは Secret Manager で設定した値を全て参照します。

ここまで完了したら、[デプロイ]をクリックします。
デプロイが失敗した場合、まずは環境変数とシークレットが間違えてないか確認すると良いです。

langfuse worker のデプロイ

以下のリンクから Langfuse worker をデプロイします。
https://console.cloud.google.com/compute/instances?hl=ja

項目
リージョン asia-northeast1
マシンタイプ e2-medium
ブートディスク Container Optimized OS
ネットワークタグ langfuse-worker
ネットワーク test-vpc
サブネット test-subnet
外部 IPv4 アドレス なし
サービスアカウント v2 で作成したサービスアカウント
アクセス スコープ すべての Cloud API に完全アクセス権を許可
Logging を有効にする true

また、起動スクリプトとして以下のコードを使用しています。

#!/bin/bash

# 環境変数ファイルのディレクトリを作成
mkdir -p /var/run/containers

PROJECT_ID= #プロジェクトIDを入力
ACCESS_TOKEN="$(curl -s -H 'Metadata-Flavor: Google' \
'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token' \
  | jq -r '.access_token')"
echo $ACCESS_TOKEN

SECRET_NAME=("DATABASE_URL" "SALT" "ENCRYPTION_KEY" "CLICKHOUSE_MIGRATION_URL" "CLICKHOUSE_URL" "CLICKHOUSE_USER" "CLICKHOUSE_PASSWORD" "REDIS_CONNECTION_STRING" "LANGFUSE_S3_EVENT_UPLOAD_ACCESS_KEY_ID" "LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY" "LANGFUSE_S3_EVENT_UPLOAD_BUCKET")
for SECRET in "${SECRET_NAME[@]}"
do
  VALUE=$(curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \
  "https://secretmanager.googleapis.com/v1/projects/$PROJECT_ID/secrets/$SECRET/versions/latest:access" \
  | jq -r '.payload.data' | base64 --decode)
  echo "$SECRET:$VALUE"
  echo "$SECRET=$VALUE" >> /var/run/containers/env.list
done

# 固定値の環境変数を追加
cat >> /var/run/containers/env.list << EOF
CLICKHOUSE_CLUSTER_ENABLED=false
LANGFUSE_S3_EVENT_UPLOAD_REGION=auto
LANGFUSE_S3_EVENT_UPLOAD_ENDPOINT=https://storage.googleapis.com
LANGFUSE_S3_EVENT_UPLOAD_FORCE_PATH_STYLE=true
LANGFUSE_S3_EVENT_UPLOAD_PREFIX=events/
EOF

# ファイルのパーミッションを設定
chmod 600 /var/run/containers/env.list

# Langfuse Workerコンテナの起動
docker run --rm --name langfuse-worker \
  --env-file /var/run/containers/env.list \
  -p 3030:3030 \
  -a STDOUT \
  langfuse/langfuse-worker:3

PROJECT_ID= #プロジェクトIDを入力のところにプロジェクトIDを入力してください。

VM を作成したら Cloud Logging でログを見ます。
https://console.cloud.google.com/logs/query?hl=ja

startup-script:でログを検索し、 Secret manager で設定した値がログに出ているか確認します。

その他にエラーが出た場合、はエラー内容に応じて対処します。
Redis connection error: ERR unknown command 'client'のエラーは無視して良いです。(Memorystore for Redis には client コマンドがないためこのようなエラーが出るようです。)

動作確認

Langfuse の初期設定

Langfuse の初期設定を v2 で完了させている場合はスキップしても構いません。

  1. Cloud Run の URL から Langfuse にアクセスします。
  2. サインアップ & サインインします。
  3. 組織を作成します。
  4. プロジェクトを作成します。

API キーの取得

API キーの取得を v2 で完了させている場合はスキップしても構いません。

[Settings]→[API Keys]→[Create new API keys] の順にクリックします。

以下の値をメモします。

  • Secret Key
  • Public Key
  • Host

トレースを作成

トレースは通常 LLM のモデルに回答をリクエストする際に発生しますが、今回は Langfuse に対して直接 HTTP リクエストを送る検証をします。

ターミナルで以下のコマンドを実行します。
ただし、Secret Key, Public Key, Hostには先ほどメモした値を入力します。

curl -X POST "{Host}/api/public/ingestion" \
-u "{Public Key}:{Secret Key}" \
-H "Content-Type: application/json" \
-d '{
  "batch": [
    {
      "type": "trace-create",
      "id": "'$(uuidgen | tr "[:upper:]" "[:lower:]")'",
      "timestamp": "'$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")'",
      "metadata": null,
      "body": {
        "id": "'$(uuidgen | tr "[:upper:]" "[:lower:]")'",
        "name": "string",
        "timestamp": "'$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")'",
        "public": false
      }
    }
  ],
  "metadata": null
}'

ステータス201のレスポンスが返ってきたら成功です。

トレースの確認

Langfuse の画面に先ほど作成されたトレースが表示されているか確認します。

表示されない場合、Cloud Logging でエラーを確認し、解決します。
※トレースの表示までの流れは以下のようになっています。デバッグに活用ください。

おわりに

最後までお読みいただき、ありがとうございます。

本記事の構成でセルフホスティングを実施した場合、GKE を使用する場合と比較してコストを大幅に抑えることができます。ただし、ClickHouse で自動スケーリングができない点には注意しなければなりません。

これらのことから、以下のユースケースに適しています。

  • 使用量の増加や減少が緩やかで、アラートで手動スケーリングをする運用で問題ないケース
  • Langfuse の使用量を予測でき、スケジュールされた Cloud Functions などを使用してスケーリングできるケース
  • PoC や開発目的で、Langfuse v3 を小規模で開始したいケース

ClickHouse の部分については、必要に応じて GKE や ClickHouse Cloud への移行も可能で、柔軟な拡張性を備えています。

インフラ構築の知見を深めながらの実装であったため、完成までに時間を要しましたが、得られた知見は非常に貴重なものとなりました。本記事が、Langfuse v3 のセルフホスティングを検討されている方々の参考になれば幸いです。

Discussion