AWS LambdaにはFunction URLsという機能がある。提供されたのはわりと最近 (2022年4月) のことだ。
AWS Lambda Function URLs の提供開始: 単一機能のマイクロサービス向けの組み込み HTTPS エンドポイント | Amazon Web Services ブログ
Function URLsを使うとAPI Gatewayを噛ますことなくLambda関数にHTTPSエンドポイントを用意することができる。API Gatewayが有料なのに対してFunction URLsは無料だ!いやLambdaの使用分の課金があるが、リクエスト毎月100万件まで無期限無料なので問題なかろう。
そういうわけで、Github Pages上のSPAからFunction URLsを直に叩く構成なら無期限無料で簡単なWebアプリをデプロイできそうだと思い立った私は、実際なんか作ってデプロイしてみた……
どうだったのか
作ったのは brainf*ckを吐く自作言語のPlayground だ。自作言語のコンパイラ (OCamlで書いた) の実行ファイルを載せたコンテナイメージ利用のLambda Functionで 自作言語 → brainf*ck の変換をして結果を返す。
で、まあ問題なく動いたのだが、いくつか微妙な点があってデプロイ後にAPI Gatewayを使う構成に変えてしまった。「微妙な点」とは具体的に次の2つだ:
- ローカルでの動作確認が難しい。
- AWS SAM CLIはFunction URLsのローカルでの動作確認に対応していない。新しい機能だからだろうか?結局ローカルで確認をするためにAPI Gatewayの設定を書くことになったし、さらに後日それをデプロイした。
- LocalStackを使えばローカルでの動作確認ができそうな感じがする。しかしコンテナイメージ利用のLambdaを動かすのがたぶん有料で、今回の用途には適さなかった。
- スロットリング機能がない。
さきほどAPI Gatewayが有料と述べたが、1ドル=360円のレートでも課金は2800件につき1円程度だ。月1000件もリクエストは来ないと思うし、100円くらいなら払っても構わないし、冷静に考えてみるとこの用途では料金の観点でもFunction URLsを使う必要はなさそうだった。
ついでに言うと、コンテナ利用のLambda Functionを利用するにはECRにイメージをpushする必要があり、プライベートリポジトリはデータ量で課金される。私の場合 (12ヶ月の無料期間のあとに) 月額1円くらい取られるかも。無期限無料ではなかったな。
完
以下、Function URLsのデプロイまでの流れとかハマった点とかを記しておく。
記録
AWS SAM CLIというのを使うとサーバーレスアプリケーションのプロジェクト作成・ビルド・ローカル動作確認・デプロイが全部できるらしい。
チュートリアル: Hello World アプリケーションのデプロイ - AWS Serverless Application Model ←このページを参考にプロジェクトを作成する。
API Gatewayを消してFunction URLsを使う
template.yaml
とapp.js
を書きかえればFunction URLsでAPIをデプロイできるわけだな。
Hello Worldテンプレートのtemplate.yaml
には最初からAPI Gatewayを使用する旨の設定が書いてあるわけだが、これを消す。
Events: HelloWorld: Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api Properties: Path: /hello Method: get
↑消すやつ。これの他にOutputs:
以下にもAPI Gatewayに関する項目があって、そいつも消す。
さてAPI GatewayのかわりにFunction URLsの設定を書く。さっき消したEvents:
と同じ場所にFunctionUrlConfigを書けば良いらしい。今回必要なのはAuthType
とCors
だ。
FunctionUrlConfig: AuthType: NONE Cors: AllowOrigins: - http://localhost:3000 - https://roodni.github.io AllowHeaders: - Content-Type
ブラウザから呼べねえ
よし動作確認するぞ!ローカルにエンドポイントを生やすコマンドはsam local start-lambda
とsam local start-api
の二通りがあるんだが、start-lambda
は (たぶん) ブラウザから叩くのに適さない。ここで使うのはstart-api
の方だ。
$ sam local start-api -p 3001 Error: Template does not have any APIs connected to Lambda functions
あれ?もしかしてFunction URLsってSAM CLIで動作確認できない??いや設定書けるのにテストできないってのは変だろ。やり方を調べるぞ……
- Lambda + API Gateway と 関数 URL(FunctionURLs)を併用する場合の SAM テンプレート | Wedding Park CREATORS Blog
- Test Lambda Function URL with a HTTP client locally? : aws
- ぱっとした回答が無い。
-
I'm not aware of anything that does that for you already.
一応「Function URLs ローカル」でTwitter検索もするか……うーんダメそうな感じだ。
API Gatewayで動作確認する
もういい。Lambda 関数 URL の呼び出し - AWS Lambda ←このページによればリクエストイベントの形式はFunction URLsとAPI Gatewayでだいたい同じらしいので、ローカルで試すときはAPI Gatewayを使ってやる。
CORSの設定のためResources:
以下に AWS::Serverless::HttpApi - AWS Serverless Application Model を書く。
BfreCompileApi: Type: AWS::Serverless::HttpApi Properties: CorsConfiguration: AllowOrigins: - http://localhost:3000 AllowHeaders: - Content-Type
↑BfreCompileApi
というのは私が適当につけた名前なので気にしないでほしい。
さっき消したEvents:
をいろいろ書き換えて復活させる。
Events: BfreCompileEvent: Type: HttpApi Properties: ApiId: !Ref BfreCompileApi Path: /codegen Method: POST
↑Type
をApi
からHttpApi
に変えたほかApiId
という項目を追加した。
これでプリフライトリクエストが通るようになる。さらにLambda Functionのハンドラをいじって返値のheadersプロパティにAccess-Control-Allow-Originを付与してやることで、ようやくsam local start-api
のエンドポイントをブラウザから叩けるようになる。
デプロイ用に修正が要る
sam deploy --guided
でアプリケーションをデプロイできるが、API Gatewayはあくまで動作確認用なのでデプロイ前に関連する設定をyamlからコメントアウトしておく。←アホ
デプロイしたらCORSエラーが出た。ヘッダが重複……?うわっハンドラで手動付与したCORSヘッダとFunction URLsの設定のCORSヘッダが二重になって返されてやがる!! もう知らん。デプロイ前にCORSヘッダの手動付与する部分をコメントアウトして対処する。←アホ
ちなみにAPI Gatewayでデプロイする場合、手動付与のCORSヘッダが存在してもAPI Gatewayの設定を優先して塗り潰してくれるので問題がないのだが、ならsam local start-api
でもそうしてくれたら最初から手動付与しなくても良くなって万事解決だよな。挙動が謎だ。
最後にリクエストボディがBase64エンコードされる場合があることに引っ掛かったが、これはイベントオブジェクトのisBase64Encoded
を見れば対応できるので大した問題ではない。
動いた
わぁい
完