【備忘録・まとめ】AWS Lambda 開発者ガイド

## 出典

[AWS Lmabda 開発者ガイド](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/welcome.html)

## まとめ

* AWSの代表的なFaaS
* サーバレスアーキテクチャにおける主な計算リソース
* 入力としてeventと呼ばれるオブジェクトを受け取り、呼び出し元へ値を返す
    * eventはその他のAWSサービスあらゆるところから飛ばして、lambdaを呼び出して返り値を利用できる仕組み
* [オフィシャルのランタイム](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-runtimes.html)がある
* この辺抑えておけばとりあえずは
    * メモリ
    * タイムアウト
    * ロギング（CloudWatch logs）
    * 同時実行数とスロットリング・スケーリング
    * 権限周り
    * バージョニング・エイリアス
    * 呼び出しタイプ
        * 同期と非同期
    * デプロイパッケージは今どき手で作らないと思う
        * SAMとか他のフレームワーク使うかな
    * レイヤー
    * コンテキスト共有

なお、個人的に今のところは必要ないので、ストリームベースのイベントソースについては触れない。

## 要点

### Lambda関数

* デプロイパッケージ
    * フレームワークに頼らず手動でLambda関数をリリースする場合、[デプロイパッケージ](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/deployment-package-v2.html)と呼ばれるzipアーカイブを作成し、Lambdaにアップロードする必要がある
    * この辺はもう手でやらずに仕組みに乗るべき
* [プログラミングモデル](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/retries-on-errors.html)
    * ハンドラ
        * Lambda関数の実行を開始するためにAWS Lambdaが呼び出す関数
        * いわゆるエントリーポイント
    * contextオブジェクト
        * ハンドラ関数に渡ってくる2番めのオブジェクト（1番目はイベント）
        * 主にLambdaとやりとりするためのメソッドやオブジェクトの集合
        * イベントソースに値を返したりする時に使う
    * ログ
        * 主にCloudWatch Logsにログが吐き出される
    * 例外
        * Lambda関数は、AWS Lambdaに関数の終了を通知しなければならない
        * その通知方法によって、通常終了、異常終了等の扱いが異なる
        * その関数の呼び出しタイプが同期呼び出しの場合は、その結果を呼び出し元に転送する

### [関数の設定](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-configuration.html)

* メモリ
    * 関数実行時に、その実行1回のランタイムが使用できるメモリ容量の上限
    * 128MB〜およそ3GBまで、64MB刻みで設定できる
    * 実行時の使用量が設定値を超えると、その実行は失敗する
* タイムアウト
    * 関数実行1回の実行時間上限
    * 2019年現在、最長で15分
    * 実行時間が設定値を超えると、その実行は失敗する
* 実行ロール
    * 実行されたLambda関数が引き受けるロール
    * 処理内で用いられる他のAWSリソースへのアクセス権限が付与されていなければならない
* ハンドラ名
    * 実行される処理のエントリポイント名
* [環境変数](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/env_variables.html)
    * 処理実行時にランタイムへ渡せる環境変数
    * 関数バージョン毎に値を切り替えられる
    * [KMS](https://docs.aws.amazon.com/ja_jp/kms/latest/developerguide/overview.html)を用いて、環境変数を暗号化した状態でデプロイできる
    * デプロイされた値はデフォルトで暗号化される
        * 暗号化キーもLambdaデフォルトのものがある
    * [予め設定されている環境変数](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/current-supported-versions.html)がある
* [バージョニング](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/versioning-aliases.html)
    * 全てのLambda関数にはバージョンが存在している
        * デフォルト値は$LATEST
    * 各バージョン毎にARNが存在し、Lambda関数管理における最小の粒度
    * publishすることで、新しいバージョンを作成できる
    * 各バージョンには任意のエイリアスを張ることができ、それらエイリアスにもARNが付与される
        * 実際にリソースとしてLambda関数を外部から指定する場合、エイリアスで指定するのが良い
* [レイヤー](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/configuration-layers.html)
    * 実態はライブラリ、カスタムランタイム、またはその他の依存関係を含むZIPアーカイブ
    * これを予め「レイヤー」としてLambdaに登録しておく
    * 登録されたレイヤーを各Lambda関数に設定することで、関数のライブラリを使用可能
    * レイヤーの使用に関するアクセス許可は、レイヤーのリソース上で管理される

### [関数の呼び出し](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/invoking-lambda-functions.html)

関数呼び出しのタイプについては、オンデマンド呼び出しと一部例外（API Gateway等）を除いては、呼び出し元のイベントソースによって呼び出し方が決まっていて、基本的に呼び出しタイプを制御することはできない。

呼び出しタイプの違い（同期/非同期）によって、[戻り値や例外](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/retries-on-errors.html)、[再試行](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/retries-on-errors.html)といった辺りの挙動に違いがあるので注意する。

* イベントソースマッピング
    * どのイベントソースからイベントを受け取って関数がキックされるか、を対応付けた設定のこと
    * 各Lambda関数に設定されている
* イベントデータ
    * サポートされているイベントソースとイベントの構造については[このへん](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/eventsources.html)に詳しい
* [スケーリング](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/scaling.html)
    * 同時実行数：ある時点に関数コードが実行されている件数
        * 「　毎秒発生するイベントの数 x 関数の平均実行時間　」　で、見積もれる （握力x体重xスピード=破壊力）
        * [同時実行数の管理](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/concurrent-executions.html)
    * スケーリングの制限は、下記の順で行われる
        * 各リージョンに設定されたバースト制限までは（恐らく）無断階にスケールする
        * バースト制限を超えると、トラフィックに応じてアカウント制限まで、同時実行数500ずつスケールする
* [デッドレターキュー](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/dlq.html)
    * 非同期実行されたLambda関数は失敗した場合2回再試行され、その後呼び出しに使われたイベントは破棄される
    * この破棄されるイベントをAmazon SQSないしAmazon SNSに転送して、失敗の分析に使うことができる
        * これをデッドレターキュー（DLQ）と呼ぶ
    * DLQを指定しない関数は、イベントを破棄する

### ランタイム

* [実行コンテキスト](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/running-lambda-code.html)
    * 実行コンテキストとは、データベース接続あるいは HTTP エンドポイントなど、Lambda関数コードのすべての外部依存関係を初期化する一時的なランタイム環境・・・と、書かれてはいるがイマイチぴんと来ない
    * 連続した同じLambda関数の呼び出しに関しては、前回の呼び出し時の状態が一部次の呼び出しに引き継がれるらしい

### [AWS SAM](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html)

* アプリの管理に使えるサーバレスアーキテクチャフレームワーク
* 便利

### モニタリング

* コードの実行時間、同時実行数等のメトリクスはCloudWatchで確認できる
* コードから吐き出されたログはCloudWatch Logsで確認できる
* Lambda - CloudWatchの話は[このへん](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/monitoring-functions.html)
* AWS X-Rayを使うと、連携している他のAWSリソースとのやりとりにかかった時間などがトレースできる

### [アクセス管理](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/access-control-overview.html)

* 認証
    * IAMユーザーおよびロールが使用可能
    * 適宜ポリシーをアタッチする
* アクセスコントロール
    * アイデンティティベースのポリシーは、IAMユーザー/ロールとして、ユーザーないしイベントソースのサービスに権限をアタッチできる
    * 逆に、Lambda関数自体に、呼び出し元のユーザーないしイベントソースのサービスへの、Lambda関数に対するアクセス権限をアタッチして管理することができる
        * 「リソースベースのポリシー」と呼ばれる
        * こちらの方が、呼ばれるLambdaに関して払い出されている権限を一括管理できるので、扱いやすい
        * クロスアカウントでアクセス権限を付与したりする場合はコレを使う

### [タグ付け](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/tagging.html)

* Lambda関数毎に、タグを付与することができる
* 扱いは他のAWSリソースと同じ
* 請求、環境、システム名などで分類しておくと吉

### [同時実行数とその管理](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/concurrent-executions.html)

* デフォルトでは、AWS Lambdaは特定のリージョン内のすべての関数にわたり、合計の同時実行数を1000に制限
    * ただし、随時拡張要請可能
* 関数レベルの同時実行数の制限
    * デフォルトでは、同時実行数の制限は、すべての関数の同時実行数の合計に対して適用
    * オプションで、関数の同時実行数の制限を設定できる
        * こうすることで、すべての関数の同時実行数の合計の中で、その関数用に同時実行数枠を優先確保できる
        * 残りの関数を処理するトラフィックの量に関係なく、割り当てた同時実行数がその関数に適用されるようになり、その制限を超えると、関数がスロットリングされる

### スロットリング動作

* 同時実行数の制限に達すると、その関数へのそれ以上の呼び出しリクエストは制限される
* 制限された呼び出しごとに関数のCloudWatch Throttlesメトリクスが増加する
* 関数が同期的に呼び出しでスロットリングされた場合、Lambdaは429エラーを返し、呼び出し元のサービスで再試行が必要になる
    * ThrottledReasonエラーコードは、関数レベルのスロットリング (指定されている場合) が行われたか、アカウントレベルのスロットリング (以下を参照) が行われたかを示す
* 非同期的に呼び出されたLambda関数がスロットリングされると、AWS Lambdaはスロットリングされたイベントを最大6時間自動的に再試行する
    * ただし、再試行間には遅延がある

### [ベストプラクティス](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/best-practices.html)

* 関数コード
    * Lambda ハンドラ (エントリポイント) をコアロジックから分離する
    * 実行コンテキストの再利用を活用して関数のパフォーマンスを向上させる
        * コードで取得する外部設定や依存関係は、最初の実行後はローカルで保存および参照される（何を言ってるのかイマイチ）
        * すべての呼び出しで変数/オブジェクトの再初期化を制限し、代わりに、静的初期化/コンストラクタ、グローバル/静的変数、およびシングルトンを使用する
        * 前の呼び出しで確立した接続 (HTTP やデータベースなど) をキープアライブにして再利用する
    * 環境変数を使用してオペレーショナルパラメータを関数に渡す
    * 関数のデプロイパッケージの依存関係を制御する
        * デプロイパッケージとして固めることで、ライブラリのバージョン等を固定できる
    * デプロイパッケージのサイズをランタイムに必要な最小限のサイズに絞る
        * コールドスタート等が速くなる
    * 依存関係の複雑さを最小限に抑える
        * フレームワークを単純化して、実行コンテキスト起動時のロードの高速化する
    * Lambda関数内で、任意の条件が満たされるまでその関数自身を自動的に呼び出すような再帰的なコードを使用しない
* 関数の設定
    * Lambda関数のパフォーマンステストを行う
        * SAMだとメモリも実行時間も簡単に割り出せる
    * 関数の実行時間を分析し、最適なタイムアウト値を決める
    * IAMポリシーの設定時に最も制限的なアクセス許可を使用する
    * [AWS Lambdaの制限](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/limits.html)をよく読んでおく
    * 使用しなくなったLambda関数を削除する
    * イベントソースとしてAmazon SQSを使用している場合、関数の予想実行時間の値が、キューの可視性タイムアウトの値を超えないようにする
        * 前回実行が終わらないうちに、重複して同じイベントで2回Lambda関数が実行される可能性あり
* 非同期呼び出し
    * デッドレターキューを作成して関数の非同期エラーに対処する
* 必要でない限り、Lambda関数をVPCに配置しない