🏃

Lambda Container Imagesでサーバーレス運用のモヤモヤが解決した話

2024/05/17に公開

こんにちは。atama plus SREチームの加藤です。

みなさんはサーバーレスアプリケーションをどのように管理していますか?
AWS Lambdaで動かすならAWS SAM, Serverless Framework, Chaliceなどのサーバーレスフレームワークは構築が簡単で魅力的ですね。

atama plusでもZappaというサーバーレスフレームワークを使ってAPIを構築・管理していました。
しかしインフラをTerraformで一元管理できないなどの課題が溜まってきたため、Lambdaのコンテナイメージ[1]を利用する構成に移行しました。

今回はZappaの運用に感じた課題と、それをLambdaのコンテナイメージでどのように解消したかを紹介します。

当初の構成

atama plusでは学習関連のサービスとは別に、機微情報の管理をするサブシステムに分離しています(以下、SecureAPIと呼びます)。
SecureAPIはPythonとFastAPIで作られており、それをZappaを利用してLambdaにデプロイしていました。

image.png
[2]

課題

SecureAPIを運用する上で次のような課題がありました。

  • TerraformとZappaでインフラの管理が分かれ、相互依存していた
  • FastAPIをASGIで動かせない
  • コールドスタートによるパフォーマンス問題があった
  • PythonのバージョンアップタイミングがLambdaのランタイム対応状況に左右されていた

以下、詳しく見ていきます。

TerraformとZappaでインフラの管理が分かれ、相互依存していた

Zappaのようなサーバーレスフレームワークはインフラの構築からLambdaへのデプロイまでを自動化してくれ便利ですが、実際にサービスを運用する際はZappaが作成してくれるインフラだけでは足りません。

image.png

上図で色をつけた部分はZappaでは構築できない、またはカスタマイズが難しいためTerraformで管理していました。
こうなると

  • VPC(Terraform)を先に作らなければLambda(Zappa)を作れず
  • Lambda(Zappa)を作らなければログ集約(Terraform)を作れない

といった相互依存の状況になります。

TerraformとZappaを行ったり来たりしながらになるので新規環境を構築するスピードが落ちていました。

FastAPIをASGIで動かせない

FastAPIは本来ASGIで動作しますがZappaがWSGIにしか対応していないため変換ライブラリ利用して動かしていました。
ただこの変換中にエラーが出ることがあり、時間を掛けて調査を行ったものの原因をつかめずお手上げ状態となっていました。

コールドスタートによるパフォーマンス問題があった

Lambdaのコールドスタートにより一部のリクエストに対してレスポンスタイムが悪化する場合がありました。
これについてはLambdaのProvisioned Concurrencyという機能を使って、予め決めた並列数分のインスタンスを立ち上げておくことで緩和できることはわかっていました。
しかし残念ながらZappaではこれを設定する機能がない[3]ため導入できていませんでした。

PythonのバージョンアップタイミングがLambdaのランタイム対応状況に左右されていた

他のPython製アプリケーションとタイミングがずれることで以下のような認知負荷を生んでいました。

  • 開発者はSecureAPIのPythonバージョンが異なることを意識して開発しなければならない
  • せっかくバージョンアップで得た知識をSecureAPIのバージョンアップのときに再び思い出す必要がある

解決策

そもそもLambdaを使っているからこれらの問題が起きるのであって、ECS等に移せるならそうする方が柔軟なカスタマイズができると思います。

ここはサービスごとに判断すべきことですが、SecureAPIに限ってはアクセス量がそれほど大きくないサービスで、ECSに移した場合かなりのコスト増が見込まれました。

そのためできる限りLambdaのまま問題を解決しようと考えました。

Zappaがやっていたことを整理すると3つに分解できます。
① API GatewayとLambdaを作成する
② APIサーバーをLambdaのインターフェースに合うよう変換する
③ アプリケーションをLambdaにデプロイする

それぞれ解決策を、
①はTerraformに寄せることで解決できる
②はmangumという専用ライブラリによって代替できる
③はLambda Container Imagesを導入すれば、コンテナイメージをECRにプッシュする操作に置き換えられる

と考えていきLambda Container Imagesに至りました。これが抱えていた課題にぴったりハマりすべてを解決することができました。

  • インフラはすべてTerraformで管理し、アプリケーション側ではイメージをビルドするだけになった
  • mangumを使ってASGIアプリケーションを動かせるようになった
  • LambdaのProvisioned Concurrency機能を使ってコールドスタート問題を防げるようになった
  • 任意のPythonバージョンが使えるようになった

最後に

フレームワークの使用は最初に作るときは楽をできる反面、しっかりした構成を作りたいときや大幅にカスタマイズしたいときは枷になるなと改めて実感しました。
近くServerless Frameworkが一定条件のもと有償化[4]するそうなので、この機会にLambda Container Imagesへ乗り換えてみるのも一考の余地があると思います。

明日はコーポレートエンジニア・石川の「社内面談動画を文字起こしするツールを自製した話」です。
お楽しみに!

脚注
  1. 名前の通り、Lambdaで用意されたランタイムではなくコンテナイメージを動かす手法です。 ↩︎

  2. 厳密にはCustomer VPCの中にいるのはLambdaではなくLambda VPCに接続するENIです。 ↩︎

  3. https://github.com/Miserlou/Zappa/issues/2047 ↩︎

  4. https://www.serverless.com/blog/serverless-framework-v4-a-new-model ↩︎

atama plus techblog

Discussion