🛬

サーバーレス全文検索を AWS Lambda × Meilisearch で実現したけど実用はできなさそうだったという話

2024/01/19に公開

今回は、FaaS のひとつである AWS LambdaLambda Web Adapter をもちいて、全文検索エンジンである Meilisearch の実行環境の構築に成功したものの、本番環境ではいまいち使えなさそうという結論に至ったという話をします。

とりあえず、こんな風にやりましたとか、なぜ実用化が難しそうだと思ったのかという話はしますが、細かい話や性能比較(EC2 VS Lambda) とかは元気が出たら書きます。

コンパクトに利用方法も書いておきますが、この構成はまだおすすめはできないです。
各サービスの使い方に関しては、そのうち記事を書く予定です。

とはいえ、私自身かなり学びがあったので供養の意味と、もしかしたらチャンスはあるのでは?という淡い期待も込めて公開します。
アイディアや意見・アドバイスいただけると大変ありがたいです。

要約

  • Meilisearch の実行環境を Lambda Web Adapter を用いて AWS Lambda で動かせるようになりました。
  • インデックスの保管場所に Amazon EFS を利用しました。
  • Amazon EFS の読み取り量がえらいことになりました。

→ EFS の読み込み料金を考えるとこの方式での実用はむずかしそうという結果が得られた。

はじめに

Meilisearch とは、Meili 社および多くの Contributor によって開発されているオープンソースの全文検索エンジンです。
ミリ秒単位の非常に高速な検索パフォーマンスやシンプルな API のほか、豊富な機能があり、多くの支持を得ています。
また、コミュニティが活発で、アップデートが継続的に行われる点も個人的に好きなポイントです(あとアイコンがかわいい)。

全文検索エンジンを導入するためには、Meilisearch Cloud, Algolia のようなクラウドサービスを利用することや、全文検索エンジンをセルフホスティングすることが選択肢になります[1]

どちらにしても、いくらかのランニングコストを支払い続ける必要があります。
全文検索の例に限らずの話ですが、全ての製品に潤沢な予算が割り当てられているとは限られず、開発者たちは、継続した開発・運用のために低コストな方法を模索しています。
特に、個人で開発・運営されていたり、小規模な製品において、それは顕著だと思います。

AWS Lambda などの Faas には多くの無料枠が提供されています。
また、Meilisearch の軽量さならコールドスタートによる遅延を加味しても実用に耐えられるのでは仮説を立て(リアルタイムでのインデックスの作成ができない[2]点には目をつぶる)、できるだけ低コストで全文検索エンジンを導入したいというニーズを叶えられるかもという考えから、今回の挑戦に至りました(あと面白そうだから)。

ちなみに、以下のように、Meilisearch をサーバーレス環境で実行するアプローチに関しては過去に行ったかたがいらっしゃいます。が Lambda で行っている事例は見つからなかったです。
https://blog.simonireilly.com/posts/serverless-search
https://github.com/simonireilly/cloud-run-meilisearch/tree/main

リポジトリについて

使用した Dockerfile はこちらから
https://github.com/ndjndj/meilisearch-on-aws-lambda

注意事項

利用方法

サーバーレス環境作成のための手順をいくつかのステップに分けると以下のようになります。
また、検索対象となるデータはローカル(非 Lambda 環境)で作成する必要があります。

  1. Meilisearch 実行環境の Dockerfile を作成します。
  2. 1 で作成した Dockerfile に Lambda Web Adapter[3] を適用し、build image します。
    • 環境設定の類を設定する
    • PORT を 8080 にする必要がある
  3. 2 で作成した Dockerimage を Amazon ECR に push します。
  4. 3 のイメージを AWS Lambda にデプロイします。
    • Lambda のメモリは 128 MB で OK(インデックスサイズによるかも)
  5. AWS Lambda に Amazon EFS をマウントします。
  6. 4 でマウントした EFS のアクセスポイントに data.ms をインポートします。
    • インポート方法はなんでも構いません(DataSync でも EC2 でも)
  7. 関数 URL を設定するなり API Gateway にアタッチするなりします。

実用性

実用性を議論するポイントは永続ストレージとして利用している data.ms のとりあつかいです。

前提として、Meilisearch では、data.ms 直下のファイルの更新を都度行っているらしく[4]
Lambda では tmp 直下以外に置いたファイルを操作しようとすると Read-only file system error が発生します[5]
なので更新が発生する data.ms/ を tmp 直下以外に置くことはできません。

かといって、tmp 直下に data.ms を置いてしまうと、AWS Lambda では、tmp 直下のファイルはある程度時間がたつと消えてしまうので、うまく検索を実行することができません[6]

上記のような背景から、Amazon EFS の利用を決めました。
つまり、EFS に data.ms をインポートし、Lambda にマウントすることで、data.ms の永続性を保とうとしました。
しかし、この方法を使うことで、データセットを毎回読み込むことになったため、EFS の読み込み量が非常に多くなってしまいます。

今回利用したデータは、約12万個のドキュメントでインデックスのサイズは140 MiB ほどです。 この場合ですと、毎回 100 MB 近くの読み込みが発生していました。
EFS のログを確認すると、コールドスタートの度に読み込みを行っているように思えます[7]

また、書き込み系の API が Meilisearch-Lambda 経由だと現実的ではなさそうなので、EFS においている data.ms/ を Lambda 以外の方法で更新する必要があります[8]

というわけで、AWS Lambda で Meilisearch 環境を作成することに成功はしましたが、どうしても EFS の読み取り量がかさんでしまうことで、コストの増加は避けられない結果となりました。

当初の目的としては、Meilisearch を運用するコストを減らすことが目的だったため、現在の状態で使うなら EC2 にホスティングしたほうが安上がりになる可能性が高いでしょう。

個人運営のサイトでアクセスされる時間が固まっていたり、インデックスのサイズがもう少くなかったりすればもしかしたらという感じでしょうか。

最後に

Meilisearch の高い検索性能がなければサーバーレスで構築しようなどとは考えもしませんでした。Meilisearch に関わる全ての Contributor に感謝します。

今回、低コストで全文検索エンジンを導入する選択肢を増やすために挑戦してみましが、なんとも言えずの結果になりました。

しかし、やってみてダメだと分かったこととはじめからダメだと言われたことは違います。
Meilisearch の性能を実感することができたし、Lambda Web Adapter の便利さも知れたのでやがて私の血肉になることでしょう。きっと改善の余地はあるさ。

意見や改善点を歓迎します。
Issue やコメントください。

参考

https://www.meilisearch.com/docs/learn/what_is_meilisearch/overview
https://aws.amazon.com/jp/lambda/
https://aws.amazon.com/jp/builders-flash/202301/lambda-web-adapter/?awsf.filter-name=*all
https://aws.amazon.com/jp/lambda/pricing/
https://calculator.aws/#/
https://blog.simonireilly.com/posts/serverless-search
https://github.com/simonireilly/cloud-run-meilisearch/tree/main

脚注
  1. KVS で頑張る方法もみたことあります ↩︎

  2. Meilisearch の仕様として、書き込みタスクをキューにスタックした後順番に処理するのでそもそもサーバーレスでは難しそうな気がしている。 ↩︎

  3. さらっと書いてますがめっちゃすごかったです、Web アプリを Lambda で実行できるようになるやつです。Meilisearch でも内部的に Rust 製の Web フレームワーク Actix を使用しているため、なんなく置き換えに成功しました。 ↩︎

  4. 多分 lock 系のファイルですが、更新のタイミングはキャッチアップできていません。ファイルの更新日ベースの判断です。 ↩︎

  5. これは AWS Lambda の仕様で、/tmp 以外のディレクトリは書き込みの権限は与えられていない。 ↩︎

  6. Index not found. が発生する。もしかしたら検証の余地は残っているかもしれません。そもそもデータが image に残っていないのかも ↩︎

  7. ここもきちんと検証したわけではないです。どちらにせよ 100Mb 近くの呼び出しが何度も起こっていることは確かです。 ↩︎

  8. 機能しないわけではなさそうですが、かなり不安定で、反映が遅かったり、そもそも反映しなかったり、API のレスポンスがかなり重くなったりする現象が発生しました。先述したように、書き込みタスクをキューにスタックした後順番に処理するような仕組みが一定時間で終了するサーバーレス環境とそもそも相性がよくないのかもしれません。 ↩︎

Discussion