この記事は5019文字13で読めます

でもお高いんでしょう…?をなんとかしたい。

#Table of Contents

#Langfuse v3

皆さま、Langfuse使ってますか?

Langfuse v3にバージョンアップをしたことでLangfuseにかかるインフラコストが上がってませんか?前回の記事でLangfuse v3をAWSマネージドサービスで構築する方法を紹介しましたが、構築したあとのコスト問題にも向き合う必要があります。

私は上がりました…。

Langfuse v3になることで、ClickhouseWorkerRedisなどの追加が必要になるためコストがどうしても上がってしまいます。

そこで、少しでも安く運用するために工夫する方法をいくつかご紹介します。

#Fargate Spotを使う

Langfuse v3を安く運用する方法の1つとして、Fargate Spotを使う方法があります。

Fargate SpotはAWSの未使用リソースを利用することで、EC2のインスタンスを最大70%割引で利用できるサービスです。

Langfuse v3は、ClickhouseやWorkerでコンピュートリソースが必要になるため、Fargate Spotを使うことでコストを抑えることができます。

運用の要件にもよりますが、ClickhouseはトレースのReadが目的になるDBなので、万が一Spot Instanceがリタイアメントで停止しても運用しているLLMアプリケーションには被害がないですし、 その間トレースもPostgreSQLに記録され、Web/Worker間でキューイングされるためデータの損失もありません。

(Workerも同様ですが、Export系のジョブは失敗してしまう可能性があります。ただしこちらはリトライが可能です。)

手前味噌になりますが、拙作のTerraformではTerraform Moduleを使う際に、 is_spot_instance をtrueにすることでFargate Spotを使うことができます。

variable "is_spot_instance" {
  description = "Whether to use spot instances for Langfuse Worker(s) / Clickhouse node"
  type        = bool
  default     = false
}

#ECS FargateでARM64(Graviton)を使ってみる

Langfuse v3を安く運用する方法の2つ目として、ECS FargateでARM64(Graviton)を使ってみる方法があります。

ARM64(Graviton)は、従来のx86アーキテクチャよりもコストパフォーマンスが高く、EC2のインスタンスを20-30%のコスト削減した状態で利用できるサービスです。

もちろんFargateでも使えますが、最新のGraviton(Graviton 3)を使うことはできませんので注意してください。

Langfuse v3は、ClickhouseやWorkerでコンピュートリソースが必要になるため、ARM64(Graviton)を使うことでコストを抑えることができます。

幸いにもLangfuse, ClickhouseともにDocker ImageでARM64のイメージを提供しているため、次のようにplatformを指定し、ECRにpush、ECSタスク定義でplatformを指定することでARM64を使うことができます。

# Docker pullするときにplatformを指定 (Clickhouseも同様に)
docker pull --platform linux/arm64 langfuse/langfuse-worker:3
docker tag langfuse/langfuse-worker:3 ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/langfuse-worker
docker push ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/langfuse-worker

タスク定義でplatformを指定することでARM64を使うことができます。

resource "aws_ecs_task_definition" "langfuse_worker" {
  family       = "langfuse-worker"
  cpu          = 2048
  memory       = 4096
  network_mode = "awsvpc"
  requires_compatibilities = ["FARGATE"]

  execution_role_arn = aws_iam_role.langfuse_ecs_task_execute_role.arn
  task_role_arn      = aws_iam_role.langfuse_task_role.arn

  # ARM64を指定
  runtime_platform {
    operating_system_family = "LINUX"
    cpu_architecture        = "ARM64"
  }
  ...中略...
}

#ECS Fargateではなく、ECS on EC2でコストを落とす

Langfuse v3を安く運用する方法の三つ目として、ECS Fargateではなく、ECS on EC2でコストを落とす方法があります。

基本的にFargateは割高なので、on EC2で運用することでコストを抑えることができます。

ECS on EC2は、EC2インスタンスを使ってコンテナを実行するサービスです。

Fargateと違い、EC2インスタンスを自分で管理する必要がありますが、ECS Optimized AMIを使うことで、さほど苦労せずECS on EC2を簡単にセットアップできます。

#ClickhouseをS3 Diskで運用する

Langfuse v3を安く運用する方法の四つ目として、ClickhouseS3 Diskで運用する方法があります。

今回一番話したかった内容がこれです。

ClickhouseにはS3 table enginesという機能がサポートされてます。

S3 Table Engineは、S3バケット上のデータをテーブルとして扱うための仕組みです。

このエンジンを使用すると、S3のデータを直接クエリできるようになります。

ClickhouseのテーブルデータをS3に保存することで、ブロックストレージ自体に実データが保存されなくなり、EFSのコストを抑えることができます。

(メタデータという形でS3への参照情報は保存されます。)

現状の構成では、ClickhouseのためにEFSを使ってECSでのデータの永続化をしていますが、EFSはEBSに比べて約300%高いため、正直なるべくEFSを使いたくない、という悩みがございます。

EFSとEBSのコスト比較でEFSが約300%高いことを示す図

LangfuseのClickhouse実装では高速なデータアクセスが必要ないため、ブロックストレージのようなEFSは不要です。代わりに、S3をバックエンドストレージソリューションとして使用し、テーブルデータをS3に保存することで、コスト効率の良いデータ永続化を実現します。

ClickHouseのS3 Diskを使ったアーキテクチャでEFSにはメタデータのみ保存する構成図

#S3 DiskとStorage Policyを使う

ClickhouseにはS3 DiskとStorage Policyという仕組みがあります。

S3 Diskは上記のS3 table enginesの応用で、ClickHouseがS3を物理ディスクとして扱えるようにする設定です。

これにより、MergeTreeエンジンで使用されるストレージとしてS3を利用できます。

ただし、S3Diskはメタデータを保存するための別のブロックストレージが必要です。このメタデータストレージにはEFSを使用することで完全な永続性を実現します。

Storage Policyは、ClickHouseがデータをどのディスク(またはストレージ)に保存するかを制御するポリシーです。

これにより、ホットストレージ(高速なSSDなど)とコールドストレージ(S3など)の間でデータ管理を柔軟に行なえます。

今回はディスクアクセス速度は必要ないため、S3Diskのみを使ってデータをS3に保存します。

<clickhouse>
    <storage_configuration>
        <disks>
            <s3_disk>
                <type>s3</type>
                <endpoint>https://s3.${AWS_REGION}.amazonaws.com/${S3_BUCKET}/</endpoint>
                <use_environment_credentials>true</use_environment_credentials>
                <metadata_path>/var/lib/clickhouse/disks/s3_disk/</metadata_path>
            </s3_disk>
        </disks>
        <policies>
            <s3_main>
                <volumes>
                    <main>
                        <disk>s3_disk</disk>
                    </main>
                </volumes>
            </s3_main>
        </policies>
    </storage_configuration>
</clickhouse>

#MergeTreeエンジンにStorage Policyを適用する

上記を設定したあと、Langfuseで使用しているMergeTreeエンジンにStorage Policyを適用することで、Langfuseで利用している全テーブルに対してStorage Policyを適用できます。

注意点として、Clickhouseで利用しているすべてのMergeTreeに適用されるため、Langfuse以外のテーブルにも適用される可能性があります。併用しているときは注意が必要です。

<clickhouse>
    <merge_tree>
        <storage_policy>s3_main</storage_policy>
    </merge_tree>
</clickhouse>

これでEFSにはメタデータのみ保存されるようになりました。

【Before】 S3 Disk適用前のEFSストレージ使用量を示すグラフ

【After】 S3 Disk適用後のEFSストレージ使用量がメタデータのみに削減されたグラフ

Clickhouseのテーブルデータ圧縮率はかなり高いため、それでも3GBちょいで済んでいたことのほうが驚きではあります。

#でもClickhouseに設定するの面倒ですよね

ということで、Clickhouse-serverをベースイメージにし、上記のコンフィグを設定したDocker Imageを作成しております。

clickhouse-server-s3diskのGitHub Container Registryページ

このイメージをclickhouse-serverの公式イメージの代わりに使うことで、S3 Diskを使ったClickhouseの運用を簡単に行なうことができます。

追加で必要なのは、S3 Diskで設定するS3の作成と、Clickhouseのタスク定義にS3の情報を環境変数として追加するだけです。

        {
          name = "AWS_REGION"
          value = var.region
        },
        {
          name = "S3_BUCKET"
          value = aws_s3_bucket.langfuse_clickhouse.id
        }

一応、https://github.com/tubone24/langfuse-v3-terraformにも上記を追加しておりますので、ご参考にしてください。

#まとめ

ここまでの実装は実は、LangfuseのDiscussion上でさまざまなアイディアをもらったうえで実現できました。

LangfuseのGitHub Discussionでコスト削減のアイディアが議論されているスレッド

改めて、Langfuseのコミュニティの力を感じることができました。ありがとうございました。企業名サジェスト機能の貧乏開発でもFargate Spotを使ったコスト削減に取り組んでおり、小さなコンテナを使い倒す知見は本記事とも共通するものがあります。

tubone24にラーメンを食べさせよう!

ぽちっとな↓

Buy me a ramen