AWS Lambdaで ハマった話。
最近• AWSをちょいちょいいじる
• AWS Lambdaとやらに⼿を出す
• 盛⼤にハマる
• ある程度知⾒がたまった
• いいタイミングでLTの機会が
Lambda?
• ランバダ?
• ラムダと読みます
• 以下Lambda
Lambdaとは• コードをAWSクラウド上で実⾏してくれる
• 対応⾔語はNodeJS,Java,Phython
• AWSクラウド上の⾊々な所で実⾏トリガーを設定できる
• 例えばS3へのファイルアップロード
• DynamoDBのテーブル更新時(insert, update, …)
例えば• S3に画像がアップロードされた瞬間、サムネイル
画像を⽣成する
• S3にログファイルがアップされたら、即座にファイルの解析を⾏う
• DynamoDBにレコードがインサートされたら、何かをする(利⽤シーン思いつかず)
デメリット
• 300秒以上かかる処理はできない
• メモリ1.5G以上使う処理はつらい
• Javaが遅い(らしい)
メリット• EC2いらない
• 安い
• スケーラブル
• 関数に対して割り当てるメモリを調節できる(128M ~1536M)
• CPUはメモリ⽐例して勝⼿に強くなる
そして
• 最近オンデマンド(https経由)からもLambdaを起動できるようになった!
• これは便利!
何ができるのか• クライアント(ブラウザ)が直接Lambdaを起動で
きる
• Lambdaに対してGET, POST等でデータを送信できる
• そしてそのデータに応じて/対して⾊々とごにょごにょできる
ただし
• HTTPS経由でのLambda関数の起動はAWS Lambda単体では実現できない
• ので…
AWS API Gateway
と、連携します
API Gatewayとは
• LambdaをHTTPSで呼び出せるようにするやつ
• これに関して詳細解説はしません
• 詳細は公式へどうぞ
• これとLambdaをうまく組み合わせて準備完了
で、何をしたいのか• ブラウザからポストしたデータをS3に書き出した
い
• けど書き出してる時間は待ちたくない
• 要はポストするだけして即座に”200 OK”が欲しい
• 厳密に⾔うと202
理想のイメージクライアント API Gateway Lambda
とりあえず202
⾮同期POST
S3
まずは普通に やってみる。
• Node.jsのコードを記述します
• exports.handler = function() {}に渡されいるeventにPOSTされたpayloadが⼊ってます
• 普通にaws-sdkを使ってS3にアップロードしてるだけです
これを押すと関数のテストが出来ます
エンドポイントから叩いてみる
ここから⾮同期化を⽬指す。
成功
⾮同期化への道。
⾮同期化へのアプローチ
1. 無思考的にsetTimeoutを試す
2. Node.jsのchild_processを使う
3. API Gatewayの統合リクエストヘッダーに X-Amz-Invocation-Typeを追加する
4. LambdaからLambdaを呼び出す
setTimeout();• S3へのアップロード部分の
実⾏を遅延させてみる
• setTimeout()は実⾏キューへの登録が遅延されるだけで結局同⼀スレッドで実⾏されるので意味なし
• (おさらい)Javascriptはシングルスレッド
ちなみに• setTimeout()後にプロセ
スを強制終了してみる
• プロセスが⼀瞬で終わります
• 当然setTimeout内の処理も実⾏されず。。
child_process
• Node.jsはchild_processモジュールを通じて親プロセス(メインストリーム)から⼦プロセスを⽣成することができます
• ⼦プロセスは親プロセスから切り離す事ができる!
何が嬉しいのか• ⼦プロセスを親から切り離すことによって、親プロ
セスが死んでも⼦プロセスは動き続ける事ができるようになる
• 更にこの状態で⼦が親に対して、⾃分の処理終了を待たないように宣⾔する事ができる
• 通常は切り離したとしても親は⼦の終了を待つ
実際のコード• spawnでOSコマンドを実
⾏できる
• detache: trueが切り離し命令
• unref()することで⼦の終了を待機しなくなる
• これで勝ったと思いきや…
それ、Lambdaで 出来ないよ
なぜなのか
• Lambdaではメインストリームの処理が終了した瞬間、派⽣する処理は全て殺されます
• なので前述の例で⾔うとunref()が実⾏された瞬間、親プロセスは以降の⾏に処理が無いのでそこで終了します
• = ⼦も終了します
なので• こうしても同じです
• Node.js的には正しいですが、Lambda的には意図した動作になってくれません
• detachしてるので⼦プロセスは⽣き残れるはずですが、Lambdaではprocess.exit()によって⼦も殺されます
• 無念。。。
X-Amz-Invocation-Type• Amazon API Gatewayの公式ドキュメントに下記
のような記述があります
要するに…
API Gatewayのここの
これに
これを
追加して下さい、ということです。
しかし• やってはみるものの、⼀向に⾮同期にならない
• 10秒スリープするようなスクリプトを呼ぶと、ご丁寧に10秒待たされる
• API GatewayのLambdaに関連しそうなドキュメントは⼀通り⽬を通したが⼀向に謎は解けない
• ここでかなりハマる
いろいろ試す• メソッドリクエストから統合リクエストに対して
ヘッダーをマッピングしなきゃいけない?
• X-Amz-Invocation-Type?XAmzInvocationType? x-amz-invocation-type?
• InvocationTypeはEvent?DryRun?
• POST/GET?
時は流れ
• ふと初⼼に戻りLambdaの仕組みに⽴ち返る
• Lambdaの公式ドキュメントに衝撃の事実を⾒つける
• LambdaはAPI Gatewayを⽤いたHTTPSを経由して実⾏される場合、常にRequestResponse(同期)の呼び出しタイプを使⽤します。
ん?
• API Gateway側のドキュメントとLambda側のドキュメントで⽭盾が⽣じてる気がしなくもない
• とはいえ出来ないものは出来ないので仕⽅ない
• ということで最終⼿段
Lambda => Lambda
• はい、LambdaからLambdaを呼びます
イメージクライアント Gateway Lambda
200 OK
⾮同期
S3
Lambda
実際のコード• Lambda上でaws-sdkを
⽤いたLambdaの⾮同期呼び出しを⾏えばよいことに気づく
• lambda.invokeAsync()がポイント
• InvokeArgsには受け取ったpayload(event)をそのままぶん投げる
⾮同期化成功!
まとめ
• Lambdaをうまく活⽤すればEC2いらずで経済的!
• HTTPS経由の⾮同期呼び出しは⼯夫すれば何とかなる!
最後に
ドキュメント読もう。ちゃんと。
ご清聴ありがとうございました。