AWS Lambda の Custom Runtime として Common Lisp (sbcl) を使う
この文章は、 lisp Advent Calendar 2018 - Qiita の 12/6 分の記事として書かれました。
表題の通りです。色々書いたり、気の利いたことをやろうと思ったのですが、今日 (12/6) は疲れた&時間がないのでこんな感じで・・
参考にしました
- Custom AWS Lambda Runtimes - AWS Lambda
- Tutorial – Publishing a Custom Runtime - AWS Lambda
- AWS Lambda 新機能 Custom Runtime を作ってみた - エムスリーテックブログ
- AWS Lambda で、なでしこが動いた - Qiita
- AWS LambdaのCustom Runtimeでmrubyを動かしてみた – VELTRA Engineering – Medium
- common lispでaws lambda - Qiita
- Common Lisp開発環境 on Dockerの現状 - eshamster’s diary
Custom Runtime を Lisp で書く。
bootstrap
という名前の実行可能ファイルを作り、 環境変数を読んで初期化 し、
所定の URI からリクエストを読み、所定の URI に吐くということを繰り返す 必要があります。
せっかく単一バイナリが吐ける sbcl を使うので、 Lisp で書いてしまいました。
環境変数を読んで初期化するのが bootstrap
関数、 ループに入ってリクエストを処理するのが main-loop
になります。
変なことをしている find-handler
については下で書きます。
Custom Runtime をビルドして登録
Docker でビルド環境を作る
Custom Runtime を AWS Lambda の環境でビルドします。 以下の Dockerfile を作りました。
やってることは単純です:
- SBCL を公式から拾って導入。 amazonlinux 環境の
yum
で拾えなかったので仕方なくSBCL公式から拾うことに。 - quicklisp を公式から拾って導入。
- quicklisp で bootstrap のビルドに絶対使うものを拾っておく。
最後の quicklisp ライブラリについては、 Docker のビルド時に拾う他に、 bootstrap.lisp
のビルド時に拾う手もあるのですが、「これは絶対使うんだから」と混ぜてしまいました。
Docker build 時に拾うものは以下です。
以下のようにして test
という名でビルドしました:
docker build -t test .
ビルドしてバイナリを作る
以下のようにビルド用スクリプトを書きました。
これもやってることは単純です:
- SBCLを起動し、
bootstrap.lisp
の依存ファイルと、続けてbootstrap.lisp
自体をロード。 - save-lisp-and-die で単一バイナリにする。名前は
bootstrap
とし、aws-bootstrap-test:bootstrap
から動きなさいと指示。 - zip で固める
以下のように docker でこのスクリプトを起動し、 aws_lambda_bootstrap.zip
を作成しました
docker run --rm -v `pwd`:/out test /out/build_bootstrap.sh
できた Custom Runtime を Layer として AWS Lambda に登録
できた zip を、 lisp-layer
という名前にして layer として登録しました。
aws lambda publish-layer-version \ --layer-name lisp-layer \ --zip-file fileb://aws_lambda_bootstrap.zip
デバッグでめっちゃ更新したので、すでにバージョン5になってます
AWS Lambda 関数を作る
最低限のものを作る
上で作った layer を使う AWS Lambda 関数を作ればよいので、早速作ります。
今回の bootstrap.lisp
には、 handler に default-handler
と指定すると適当な返答をするという仕込みをしてあるので、それを使ってみます。
ただ、 AWS Lambda に渡すのに、最低限何か中身を持った zip が必要らしいので、ほぼ空のファイルを作り・・
以下のように zip を作りました。
zip empty.zip empty.lisp
これを empty-test
という名で AWS Lambda 関数としてupします:
aws lambda create-function \ --function-name empty-test \ --zip-file fileb://empty.zip \ --handler default-handler \ --runtime provided \ --role <ロール名> \ --layers <作った lisp-layer の ARN>
AWS のコンソールでテストを作り:
実行。うまく動いているようです
Lisp コードを Load させる
せっかく Lisp を使っているので、動的にコードを load したいものです。そういう仕込みも bootstrap.lisp
にしておいたので試してみます。
適当なハンドラを書きました。
これを zip に入れて
zip outer-handler.zip outer-handler.lisp
これを outer-handler-test
という名で AWS Lambda 関数としてupします:
aws lambda create-function \ --function-name outer-handler-test \ --zip-file fileb://outer-handler.zip \ --handler outer-handler.lisp#outer-handler \ --runtime provided \ --role <ロール名> \ --layers <作った lisp-layer の ARN>
--handler outer-handler.lisp#outer-handler
の箇所に仕込みがあります。
今回作った bootstrap は _HANDLER
環境変数経由でこの値を読んで、 outer-handler.lisp
を Load し、 cl-user::outer-handler
シンボルを探して funcall
しようとします。上で少し触れた find-handler
関数はこれをやっています。
コンソールのテストから呼んでみます
呼べたようです。
既知の問題
時々 poll(2) に失敗したよ エラーが出ます。なんなんでしょうこれ。
調べても未回答の stackoverflow しか出ず、謎です。
成果物
感想
自宅 AWS 環境、自宅 Docker 環境を作るのに疲れました。そのために言葉がでないのでここは週末に書き直します。