Elixir Bumblebee を AWS Lambda で動かす（前提条件編）

## はじめに

以前、 Bumblebee を SageMaker で動かしてみました

https://qiita.com/RyoWakabayashi/items/6cad17cd6047cc7113c0

しかし、もっと安く、気軽に AWS 上で動かしたいですよね

というわけで今度は AWS Lambda を使います

https://aws.amazon.com/jp/lambda/

実用的かどうかは、、、:joy:

長くなりそうなので、この記事では前提条件の説明だけにとどめます

次回ローカル実行、その次にデプロイ、クラウド実行を紹介します

## Lambda とは

Lambda はサーバーレスという仕組みで動いています

開発者は関数を書いて Lambda にデプロイしさえすれば、すぐにクラウド上で関数を実行できます

サーバーの構築や保守が不要なのですごく楽です

しかも処理が呼び出されたときに必要な数だけコンテナが起動するようになっているので、使っていないときは何も動いていない状態で、大量アクセスがあればその分だけ大量にコンテナが動いてくれます

つまり可用性が非常に高いです

そして「処理が実行された時間」×「処理に割り当てたメモリ」で課金されるため、呼び出しの回数が少なければ超低価格で運用することが可能です

おまけに無料利用枠まであるのでほぼ無料で使えてしまいます

ただし、呼び出しが非常に多い場合は却って高くつく場合があります

API Gateway + Lambda から EC2 + Elixir に移行することで運用費用が大幅にカットできた、という記事もあります

https://medium.com/coryodaniel/from-erverless-to-elixir-48752db4d7bc

つまり、利用目的や頻度によってどちらがいいかは変わります

そして、 Lambda は Elixir を公式にサポートしていません

なので Elixir on Lambda というのは、あまり理にかなった実装ではなさそうです

でもあらゆる場面で Elixir を使ってみたい病に罹っているため、一旦そこからは目を逸らして実装してみます

## Elixir を Lambda で動かすために

### カスタムランタイム

Lambda がサポートしている言語（ランタイム）は以下のリンクで確認できます

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-runtimes.html

2022/12/22 現在では以下のものです

- Node.js
- Python
- Java
- .NET
- Ruby
- カスタム

カスタムは自分で Lambda から呼び出す実行可能ファイルを作る方法です

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/runtimes-walkthrough.html

実はこのカスタムランタイムで Elixir を動かす、という方法もなくはありません

以下の記事はその方法で実装しています

https://dev.classmethod.jp/articles/lambda-customruntime-elixir-reinvent-2018/

しかし残念ながら、この記事で使っている alertlogic/erllambda のイメージが OTP 21.3 で止まっています

https://hub.docker.com/r/alertlogic/erllambda/tags

もう一つ、カスタムランタイムを使っているケースがありました

https://eitoball.hatenablog.com/entry/2018/12/19/010546

こちらは Docker コンテナ内で erlang や Elixir を入れているため、カスタムすれば好きなバージョンが指定できます

が、実は Lambda にはもう一つ壁があります

Lambda に関数をデプロイする際、実装したコードと依存モジュールを ZIP で圧縮します

aws-cli では、最大で 50MB までしかデプロイできません

また、 S3 にアップロードしてから参照する方法もあるのですが、こちらも展開後のサイズが 250MB 未満でないといけません

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/gettingstarted-limits.html

今回動かしたいのは Bumblebee です

EXLA などのモジュールを含めないといけません

また、 Bumblebee で読み込むモデルファイルも含んでおく必要があります

関数が起動されてからダウンロードしていたのでは遅すぎるからです

実は上記の方法で途中まで実装していたのですが、このファイルサイズ上限に引っかかってしまいました

従って、カスタムランタイムで動かす方法は使えません

### コンテナイメージ

Lambda にはもう一つデプロイ方法があります

コンテナイメージを使う方法です

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/images-create.html

この場合、 ECR にコンテナイメージを上げさえすれば何でもありです

~~サーバー構築不要という Lambda の良さをかなぐり捨てていますが~~

実は以前 Python + TensorFlow でコンテナイメージの Lambda 関数を実装しています

https://qiita.com/RyoWakabayashi/items/43475cb816c5198ed8f9

そして、この方法で Elixir on Lambda を実装した先駆者様がいました

https://qiita.com/erin/items/2aa7a70c34b0b2a64e5b

Lambda 上で動かすためのモジュールを自分で作るという素晴らしい実装です！

https://github.com/imahiro-t/faas_base

ただし、やはりまだ問題があります

XLA には glibc の 2.31 以上が必要です

https://github.com/elixir-nx/xla#xla_target

しかし、このモジュールで生成されるコンテナイメージは　amazon/aws-lambda-provided:al2.2021.07.05.11　になっていて、古いです

https://github.com/imahiro-t/faas_base/blob/main/lib/mix/tasks/aws/release.ex#L109

実は Amazon Linux の古いバージョンを使うと glibc が 2.26 になっていて、 EXLA が動かないのです（これもやってみて気づきました）

https://github.com/imahiro-t/faas_base/blob/main/lib/mix/tasks/aws/release.ex

というわけで Amazon Linux のコンテナイメージの最新版をベースに使います

https://hub.docker.com/_/amazonlinux/tags

こうすればあとは何でも自由です

## まとめ

Elixir Bumblebee を AWS Lambda で動かすための前提条件について説明しました

次回はまずコンテナを実装してローカルで動かします