The Hitchhiker's Guide to AWS Lambda

こんにちは、@yuimam です。

令和元年も年の瀬が迫ってきていますが、AWS Lambda というサービスが 2014 年にアナウンスされ、早くも 5 年の月日が流れました。5 年も経つと、リリース当初から機能追加やアップグレード、仕様変更などなど様々な変化があるもので、もはや AWS Lambda は一言では語れず一枚岩ではありません。

そこで、**The Hitchhiker's Guide to AWS Lambda** と題しまして、Newbie でも Expert でも何かしら参考にできるような、AWS Lambda のガイドを作ってみようと思い立ちました。
<font color="DarkGray">(単に自分のカンペのためという説)</font>

皆さんのサーバーレス人生に少しでもお役に立てれば幸いです。

なお、下記の情報はすべて特筆ない限り、**東京リージョン** / **2019-12-25** 時点の情報です。
まだまだ書き足りていないところも多々あるので、随時追記をしていきたいと思います！

# AWS Lambda とは？

**2014 年 11 月**にプレビューとして発表。
[AWS Lambda – Run Code in the Cloud](https://aws.amazon.com/jp/blogs/aws/run-code-cloud/)

何らかのイベントを契機として、**Lambda 関数** と呼ばれる自身で実装したアプリケーションが、個々に隔離された環境で起動される。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535748/2a78613e-67bc-735e-18a3-356f0ce1c3b7.png" width=40%>
Lambda 関数は、**自身で実装したアプリケーションがイベントの発生にあわせて個々に等しく起動** されるため、たとえば Ruby on Rails や Spring フレームワークのように、モノリシックなアプリケーションを配置する環境としては適さず、軽量な実装となることを意識して開発する必要がある。

イベントの一例としては、Amazon API Gateway への HTTP リクエストの着信や、Amazon DynamoDB へのレコード書き込みなど、他の AWS サービスで発生した事象をもとに、Lambda 関数を呼び出すことができる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535748/c3854d13-d835-d502-6c59-0487a32977fc.png)

## 特徴

サーバーレスに類するサービスとして、以下のような特徴を持っている。

- **ゼロベースからイベントドリブンでアプリケーションが起動**
  - 必要なときにニーズにあわせて実行環境が高速に立ち上がる。
  - 事前にサーバーなどインフラをプロビジョニングしておく必要がない。
- **動かしたいアプリケーションのコードを持ち込むだけで動作**
  - OS やミドルウェアのインストール、設定がいらない。
- **アプリケーションの実行時間に応じた課金形態**
  - 仮想サーバーのように常時起動しておく必要がないためコストメリットが得られやすい。
- **フルマネージドな耐障害性およびスケーラビリティ**
  - マルチ AZ の仕組みや AutoScaling のルールなどを自分で考える必要がない。

## SLA

**2018 年 10 月**に発表。
[AWS Lambda がサービスレベルアグリーメント (SLA) を発表](https://aws.amazon.com/jp/about-aws/whats-new/2018/10/aws-lambda-introduces-service-level-agreement/)

月間稼動率 99.95 ％ と定められており、これを下回った場合、サービスクレジット (将来の利用金額からのディスカウント) を受けることができる。

# ランタイム

AWS Lambda の関数を動作させる実行基盤のことで、**Amazon Linux**、もしくは **Amazon Linux 2** のいずれかで動作する (比較的最近リリースされたランタイムからは、Amazon Linux 2 で動作するようになっている) 。

AWS Lambda ではじめから提供されているもの (標準サポート) と、自身で実行環境を持ち込むもの (カスタムランタイム) の大きく 2 種類に分かれる。

## 標準サポート

下記の言語およびバージョンに対応。

|言語|バージョン|OS|
|:--|:--|:--|
|Node.js|12.13.0|Amazon Linux 2|
|Node.js|10.16.3|Amazon Linux 2|
|Node.js|8.10|Amazon Linux|
|Python|3.8|Amazon Linux 2|
|Python|3.7|Amazon Linux|
|Python|3.6|Amazon Linux|
|Python|2.7|Amazon Linux|
|Ruby|2.5|Amazon Linux|
|Java|11|Amazon Linux 2|
|Java|8|Amazon Linux|
|Go|1.x|Amazon Linux|
|.NET Core|2.1|Amazon Linux|

言語自体の EOL にあわせて、AWS Lambda のランタイムとしてのサポートも順次終了させる場合が多い。
最新のサポート情報に関しては以下を参照。
[AWS Lambda ランタイム](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-runtimes.html)

なお、AWS SDK など一部ライブラリは、初めからランタイムに含まれているものもあるが、バージョンアップなど自動で変更される可能性があるため、**利用するライブラリは自身で持ち込むことが推奨**される。

## カスタムランタイム

**2018 年 11 月**のアップデートにより、任意の言語が AWS Lambda で実行可能になった。
[AWS Lambda がカスタムランタイムをサポート &amp; 関数間における共通コードの共有を実現](https://aws.amazon.com/jp/about-aws/whats-new/2018/11/aws-lambda-now-supports-custom-runtimes-and-layers/)

カスタムランタイムを使うことで、自身で言語の動作環境を持ち込むことができるため、Lambda が標準でサポートしていない言語や、サポートしていないバージョンの言語を利用することができる。

### カスタムランタイムの使い方

最低限行うべき実装としては、***bootstrap*** という名前の実行可能ファイル (シェルスクリプト、バイナリなど) に以下の内容を実装して、AWS Lambda へアップロードする。

(1) While ループなどのループ構造のなかで、下記の処理を実装する。

(2) 環境変数 ***AWS_LAMBDA_RUNTIME_API*** を取得する。

(3) 以下の URL に GET リクエストを送り、レスポンスヘッダー ***Lambda-Runtime-Aws-Request-Id*** を控えておく。
```http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next
```
(4) カスタムランタイムの処理を実行する。

(5) 処理の実行結果を以下の URL に POST で送信する。
```http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/${Lambda-Runtime-Aws-Request-Id}/response
```

#### サンプル

```shell:bootstrap
#!/bin/sh
set -euo pipefail
# Initialization - load function handler
source $LAMBDA_TASK_ROOT/"$(echo $_HANDLER | cut -d. -f1).sh"
# Processing
while true
do
  HEADERS="$(mktemp)"
  # Get an event
  EVENT_DATA=$(curl -sS -LD "$HEADERS" -X GET "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next")
  REQUEST_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2)
  # Execute the handler function from the script
  RESPONSE=$($(echo "$_HANDLER" | cut -d. -f2) "$EVENT_DATA")
  # Send the response
  curl -X POST "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response" -d "$RESPONSE"
done
```

# Lambda Layers

**2018 年 11 月** にカスタムランタイムと同じタイミングで発表された。
[AWS Lambda がカスタムランタイムをサポート &amp; 関数間における共通コードの共有を実現](https://aws.amazon.com/jp/about-aws/whats-new/2018/11/aws-lambda-now-supports-custom-runtimes-and-layers/)

複数の Lambda 関数で利用するライブラリなど、共通のコンポーネントを関数のあいだで共有する機能のこと。

zip 形式でアップロードすることができ、Lambda 関数の実行時に、/opt ディレクトリの配下へ展開される。ひとつの Lambda 関数には、最大で 5 つまでの Lambda Layers を含むことができる。

用途としては大きく 2 種類に分かれる。

## Lambda 関数で利用するライブラリを配置する

AWS Lambda が標準でサポートしているランタイムでは、/opt 配下の特定のディレクトリが、ライブラリの検索パスとして設定されている。

例えば Java のランタイムでは、*/opt/java/lib* がクラスパスとして設定されているため、*java/lib* ディレクトリが展開するように zip 圧縮して Lambda Layers へ登録することで、ライブラリを参照することが可能となる。

それぞれのランタイムで、どのディレクトリに対しパスが通っているかについては以下のドキュメントを参照。
[ライブラリの依存関係をレイヤーに含める](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/configuration-layers.html#configuration-layers-path)

## カスタムランタイムを配置する

カスタムランタイムを動作させるためには、**bootstrap** という名前のファイルに所定の処理を書くが、この bootstrap ファイルを Lambda Layers に含めることができる。bootstrap という名前のファイルを任意の名称で zip 化し、Lambda Layers としてアップロードするだけで動く。

## 公開されている Lambda Layers

作成した Lambda Layers は AWS アカウントをまたいでシェアすることができるため、便利なライブラリやカスタムランタイムが多く公開されている。
便利な Lambda Layers や実装のサンプルについては下記が詳しい。
[λ AWSome Lambda Layers](https://github.com/mthenw/awesome-layers)

# プログラミングモデル

普段どおりプログラムを書くのと同様に実装することができるが、いくつか AWS Lambda 特有の概念がある。

## ハンドラ

実装した Lambda 関数のエントリポイントのこと。
以下のように ***ファイル名.メソッド名(あるいは関数名)*** の形で指定する。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535748/f2f98c56-6684-db6c-b572-a76de5717c2d.png" width=40%>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535748/961fdd6d-e34c-31ff-85c4-dc8494b2b354.png" width=70%>

多くの AWS Lambda のランタイムでは、ハンドラの引数に対して、**イベント** と **コンテキスト** の 2 種類のデータが渡される。
また、Lambda 関数をトリガーするイベントソースが同期呼び出しの場合、必要に応じて **レスポンス** を所定の形式で返却することが求められることがある。

### イベント

Lambda をトリガーするイベントソースで発生したイベントの内容がデータとして格納される。どのようなデータが格納されるかは、イベントソースにより異なるため、イベントソースごとにどのようなデータ構造となるかについては、以下のドキュメントを参照。
[他のサービスで AWS Lambda を使用する](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-services.html)

たとえば、下記は S3 へファイルを配置したときに Lambda 関数をトリガーした場合のデータ構造。

```json
{
  "Records": [
    {
      "eventVersion": "2.0",
      "eventSource": "aws:s3",
      "awsRegion": "us-east-1",
      "eventTime": "1970-01-01T00:00:00.000Z",
      "eventName": "ObjectCreated:Put",
      "userIdentity": {
        "principalId": "EXAMPLE"
      },
      "requestParameters": {
        "sourceIPAddress": "127.0.0.1"
      },
      "responseElements": {
        "x-amz-request-id": "EXAMPLE123456789",
        "x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH"
      },
      "s3": {
        "s3SchemaVersion": "1.0",
        "configurationId": "testConfigRule",
        "bucket": {
          "name": "my-bucket",
          "ownerIdentity": {
            "principalId": "EXAMPLE"
          },
          "arn": "arn:aws:s3:::my-bucket"
        },
        "object": {
          "key": "uploaded.json",
          "size": 1024,
          "eTag": "0123456789abcdef0123456789abcdef",
          "sequencer": "0A1B2C3D4E5F678901"
        }
      }
    }
  ]
}
```

### コンテキスト

Lambda 関数のランタイムに関する情報が含まれる。たとえば Lambda 関数の名前や割り当てられているメモリの量、実行ごとに振られる UUID といったメタデータが含まれる。
どのようなデータが得られるかについては利用するランタイムごとに異なる。

### レスポンス

関数の return ステートメントなどでデータを返却することができる。
ユースケースとしては、Lambda 関数のイベントソースが同期呼び出しで、レスポンスが必要な場合など。

たとえば、下記は Application Load Balancer からトリガーされる Lambda 関数にて、返却すべきレスポンスのデータ構造。

```json
{
    "isBase64Encoded": false,
    "statusCode": 200,
    "statusDescription": "200 OK",
    "headers": {
        "Set-cookie": "cookies",
        "Content-Type": "application/json"
    },
    "body": "Hello from Lambda (optional)"
}
```

# アプリケーションのデプロイ

## デプロイ方法

アプリケーションのデプロイ方法は大きく分けて 3 種類。

- AWS コンソールで直接プログラムを編集する
  - ライブラリのインストールができないため、お試しの用途でのみ利用することを推奨
- zip 形式で圧縮したプログラムをアップロードする
- zip 形式で圧縮したプログラムを S3 へ配置し配置したファイルへの URL を記載する

### デプロイ可能なサイズ

zip 圧縮の状態で **50 MB**、解凍した状態で **250 MB** まで。
この 250 MB は関数本体だけでなく、Lambda Layers の内容も含んだ総サイズとなるため注意が必要。

他に、アプリケーション本体ではなく、一時的なデータの保管場所として別途 /tmp ディレクトリを利用することができ、こちらには **512 MB** までのファイルを配置することができる。

なお、下記のように AWS のコンソールから編集可能なサイズは **3 MB** までとなる。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535748/faf0de05-ddaf-334a-a97c-4ab051e51662.png" width=50%>

## バージョニング

ある特定時点での Lambda 関数の設定やデプロイされているコードを **バージョン** として管理することができる。
バージョンを発行するごとに **バージョン** の番号が加算されていく。
自身で発行したバージョン番号に加えて、**$LATEST** という最新のバージョンが常に存在する。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535748/f7f3766e-3c88-8b71-0d88-7d57f6376b73.png" width=30%>

## エイリアス

特定のバージョンに対して、別名を付与することができる。
文字、数字、ハイフン、アンダースコアが利用可能。

たとえば、**バージョン 1** の Lambda 関数を **master**、**$LATEST** の Lambda 関数を **dev** といった形で名前をつけることができる。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535748/8924502c-50c2-2787-90bb-25eca2d7bcd4.png" width=30%>

# パフォーマンス

## メモリ

**2017 年 11 月**に利用可能なメモリサイズが倍増。
[AWS Lambda で Lambda 関数の最大メモリ容量を倍増](https://aws.amazon.com/jp/about-aws/whats-new/2017/11/aws-lambda-doubles-maximum-memory-capacity-for-lambda-functions/)

64 MB ごとに **128 MB から 3008 MB** のあいだで設定可能となっている。

## CPU および NW 帯域

AWS Lambda では、設定したメモリサイズに比例して、利用可能な CPU 性能やネットワーク帯域もあわせて増加する仕組みとなっている。
また、通常 CPU はシングルコアで動作するが、**1856 MB** 以上のメモリを割り当てるとマルチコアで動作する。

## 実行のタイムアウト

**2018 年 10 月**に 5 分から 15 分へ延長された。
[AWS Lambda で最長 15 分間実行に要する関数の取扱いが可能に](https://aws.amazon.com/jp/about-aws/whats-new/2018/10/aws-lambda-supports-functions-that-can-run-up-to-15-minutes/)

Lambda 関数実行時のタイムアウトの値を **最大 15 分** まで任意に設定できる。
設定したタイムアウトの時間を超過すると、Lambda 関数の実行が強制終了される。

# VPC アクセス

**2019 年 9 月**より、VPC アクセスに要する時間が短縮されるとともに、VPC アクセスを設定した際の挙動が変更された。
[[発表] Lambda 関数が VPC 環境で改善されます](https://aws.amazon.com/jp/blogs/news/announcing-improved-vpc-networking-for-aws-lambda-functions/)

VPC のサブネットとセキュリティグループを指定して AWS Lambda を保存すると、**自身の VPC の内部に ENI (Elastic Network Interface) が自動で作成** され、AWS Lambda が動作する VPC と接続される。

これにより、AWS Lambda から外部に出る通信 (Outbound 通信) は、自身の VPC を通すことができるため、**自身の VPC 内に存在するリソースに AWS Lambda からアクセスすることができる** ようになっている。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535748/0e459dbc-2995-c206-6e3e-79fb094e0970.png" width=80%>

# Lambda 関数のライフサイクル

AWS Lambda で関数が実行されるまでには、下記の (1) - (7) までが行われる。

<font color="Green">(1) AWS Lambda をトリガーする何らかのイベントが発生する。</font>
<font color="SteelBlue">(2) AWS Lambda の実行環境 (MicroVM) が起動される。
(3) デプロイしたアプリケーションがダウンロードされる。
(4) ダウンロードされたアプリケーションが zip 解凍され展開される。
(5) アプリケーションを実行するためのランタイム (例 : Java ならば JVM など) が起動される。
(6) アプリケーションのハンドラ外の処理 (グローバル変数の設定など) の内容が実行される。</font>
<font color="Chocolate">(7) ハンドラに記述した処理が実行される。
(8) (1) と同様のイベントが新たに発生した場合、再度 (7) を実行する。</font>
<font color="Tomato">(9) しばらくイベントが発生しない場合、実行環境 (MicroVM) が破棄される。</font>

### コールドスタート

Lambda 関数の初回起動時に **<font color="SteelBlue">(2) から (6) までの動作</font>** を含むことを、AWS Lambda のコールドスタートという。
しばらく AWS Lambda をトリガーするイベントが発生しない場合、(9) で実行環境が破棄されるが、その場合 (1) からやり直しとなり、アプリケーションの展開やランタイムの起動などの時間を要する。

一方で、イベントソースからある程度の頻度で関数がトリガーされる場合、コールドスタートの部分は短縮され、(7) のプロセスのみが実行されるため、より高速な実行となる。

なお、AWS Lambda で課金対象となる時間は、(7) が実行されている時間のみ。

### グローバルスコープの利用

下記のようにハンドラ外 (グローバルスコープ) へ、様々な初期化処理やライブラリの読み込み処理を配置することで、初回実行時のコールドスタートには時間を要するものの、通常の関数実行に要する時間は短縮することができる。

```python
import boto3
import json

dynamodb = boto3.resource('dynamodb')

def lambda_handler(event, context):
    table = dynamodb.Table('my-table')
```

逆に、グローバルスコープで実行した処理は、関数実行のたびに毎回行われるわけではないため、毎回実行が必要な処理はグローバルスコープに記述してはならない。
# イベントソース

AWS Lambda の実行をトリガーする主体のことをイベントソースという。
イベントソースは AWS Lambda の呼び方に応じて、**同期呼び出し**、**非同期呼び出し**、**ポーリング** の 3 種類がある。

## 同期呼び出し

イベントソースが Lambda を呼び出すタイプのトリガー。呼び出しとともに Lambda が実行されるため、Lambda 関数の処理結果がそのまま呼び出し元へ返却される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535748/0c89c692-d3a3-745b-7946-c691666d6720.png)

### 同期呼び出しのイベントソース

- Amazon Elastic Load Balancing (Application Load Balancer)
- Amazon Cognito
- Amazon Alexa
- Amazon Lex
- Amazon API Gateway
- Amazon CloudFront (Lambda@Edge)
- Amazon Kinesis Data Firehose
- AWS Step Functions

### エラーハンドリング

エラー時はクライアント側でリトライを行う。

### Lambda へ送信できるリクエストのサイズ

6 MB まで

### スケーラビリティ

トリガーされるとすぐに Lambda 関数が起動するため、同時実行数はトリガーされる量に比例して増加する。

## 非同期呼び出し

イベントソースが Lambda を呼び出すが、イベントの情報を Lambda 内部のキュー (Amazon SQS が利用されている) にいったん格納し、あとから実行するタイプ。いつ実行されるかについては特に保証はない。また、Lambda 関数の処理結果は呼び出し元に返却されない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535748/3c290e99-18da-8ca1-ecc7-b149253bac36.png)

### 非同期呼び出しのイベントソース

- Amazon S3
- Amazon Simple Notification Service
- Amazon Simple Email Service
- Amazon EventBridge  (Amazon CloudWatch Events)
- AWS CodeCommit
- AWS Config
- AWS IoT Events
- AWS CloudFormation
- Amazon CloudWatch Logs
- Amazon API Gateway  (明示的に非同期を指定した場合)

### エラーハンドリング

**2019 年 11 月** より、柔軟にエラーハンドリングの設定が可能になった。
[AWS Lambda が非同期呼び出しの最大イベント経過時間と最大再試行回数をサポートするようになりました](https://aws.amazon.com/jp/about-aws/whats-new/2019/11/aws-lambda-supports-max-retry-attempts-event-age-asynchronous-invocations/)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535748/ba5fab8f-1170-0a1c-e21a-b8099474cac2.png)

#### イベントの最大有効期間

**同時実行数不足やシステムエラー時のリトライ**を行う時間。
リトライの間隔自体は 1 秒から 5 分まで指数関数的に増加するが、リトライに要した合計時間がイベントの最大有効期間を超過するとエラーとなる。
**60 秒** から **6 時間** までのあいだで指定できる。

#### 再試行回数

Lambda 関数で **実装した処理のなかでエラーが発生した際のリトライ** を行う回数。
**0 回** から **2 回** までのあいだで指定できる。

#### デッドレターキュー (DLQ)

**2016 年 12 月**のアップデートで追加。
[AWS Lambda のデッドレターキューのサポート](https://aws.amazon.com/jp/about-aws/whats-new/2016/12/aws-lambda-supports-dead-letter-queues/)

**イベントの最大有効期間**、もしくは **再試行回数** を超えて、Lambda の処理がエラーとなった場合に、**SQS 標準キュー** もしくは **SNS トピック** に対してエラーを発生させたイベントの情報を送信する機能。

### 宛先指定

**2019 年 11 月**のアップデートで追加。
[AWS Lambda が非同期呼び出しの宛先指定をサポート](https://aws.amazon.com/jp/about-aws/whats-new/2019/11/aws-lambda-supports-destinations-for-asynchronous-invocations/)

宛先を設定することで、SQS キュー (標準キューのみ)、SNS トピック、Lambda 関数、EventBridge イベントバスのいずれかに対して、実行結果を送信できる。Lambda 関数の **処理成功時および処理失敗時** に、結果を送信する宛先となる AWS サービスをひとつずつ設定可能 。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535748/1e09d8c5-b09a-2f62-7c68-f4fd07cda6ed.png)

### Lambda へ送信できるリクエストのサイズ

256 KB まで

### スケーラビリティ

特にドキュメント上に明記なし。
試しに手元で S3 に対し、1000 件のファイルを一度に配置した限りでは、すぐに Lambda 関数が起動されたので、同期の場合と同じくイベントソースの流量に応じて Lambda 関数が起動されるように見える。

## ポーリング

Lambda がイベントソースに対して、最新データがないかポーリングするタイプのトリガー。ストリームベースのものと、そうでないものに分かれる。

### ストリームベース
イベントソースのシャードごとに処理され、同一シャード内の新しいメッセージに対する処理はブロックされる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535748/c5bb1c2e-46c4-9313-b7c4-2f434d448525.png)

#### ストリームベースのイベントソース

- Amazon DynamoDB : 1 秒あたり 4 回の基本レートでポーリング
- Amazon Kinesis Data Streams : 1 秒あたり 1 回の基本レートでポーリング

#### バッチサイズ

Lambda 関数 1 回の実行に対し、**1 シャードごとに 1 から 10,000 まで**のメッセージをまとめて取得できる。
ただし、この値はあくまで取得するメッセージ数の最大値であるため、仮に 10,000 を設定しても、必ず 10,000 メッセージが入るというわけではなく、メッセージの流量によっては遥かに少ない場合もある。

#### エラーハンドリング

**2019 年 11 月** より柔軟にエラーハンドリングが可能になった。

[Amazon Kinesis Data Streams および Amazon DynamoDB イベントソースの障害処理機能](https://aws.amazon.com/jp/about-aws/whats-new/2019/11/aws-lambda-supports-failure-handling-features-for-kinesis-and-dynamodb-event-sources/)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535748/da002751-dfe9-6d31-6499-6a3150fd9613.png)

##### レコードの最長有効期間

イベントソースのデータを処理する期間を指定。
60 秒 ~ 604800 秒 (7 日) まで設定可能。

##### 再試行回数

エラー時に AWS Lambda の実行を何回リトライするか指定。
0 ~ 10000 まで設定可能。

##### 障害時の送信先

**レコードの最長有効期間**、もしくは **再試行回数** を超えて、Lambda の処理がエラーとなった場合に、**SQS 標準キュー** もしくは **SNS トピック** に対してエラーを発生させたイベントの情報を送信できる。

##### エラー時にバッチを分割

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535748/b6368fba-ba9e-9e45-8b93-f15ceac3c4d5.png)
Lambda 関数がデータを処理するあいだにエラーが発生した場合、エラーが含まれるバッチを 2 等分して処理を継続する機能。

#### スケーラビリティ

##### シャードあたりの同時バッチ

**2019 年 11 月** より、ひとつのシャードに対して複数の Lambda 関数を実行できるようになった。
[Amazon Kinesis Data Streams および Amazon DynamoDB イベントソースで並列化係数をサポート](https://aws.amazon.com/jp/about-aws/whats-new/2019/11/aws-lambda-supports-parallelization-factor-for-kinesis-and-dynamodb-event-sources/)

同時バッチの値としては **1 から 10 まで** の整数が指定できる。シャード数 x 同時バッチの数が、起動する Lambda 関数の合計数になる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535748/0e6685f6-ff77-b279-374f-e9651ade8068.png)

### ストリームベースでない
新しいメッセージに対する処理はブロックされない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535748/9ab584a5-a609-8735-ed21-1b46fbb36a94.png)

#### ストリームベースでないイベントソース

- Amazon Simple Queue Service : ロングポーリングでデータを取得。

#### バッチサイズ

Lambda 関数 1 回の実行あたり、1 から 10 までのメッセージをまとめて取得できる。

#### スケーラビリティ

初回実行時には SQS からメッセージを読み取るために、最大 5 つの Lambda 関数が起動する。
その後、**インフライトメッセージの数が増加傾向** にあった場合、1 分ごとに 60 ずつ同時実行数がスケールアウトする。
**インフライトメッセージの数が減少傾向** にあった場合、1 分ごとに 30 ずつ同時実行数がスケールインする。
インフライトメッセージの数を基準とするため、必ずしも SQS 上に存在するメッセージの数と一致しないことに注意が必要。

#### エラーハンドリング

**Amazon SQS に設定しているエラーハンドリングの内容に依存する。**
Lambda 関数の実行がエラーとなった場合、Amazon SQS へメッセージの内容が戻される。
その後、Amazon SQS に設定したデッドレターキューへ、エラーとなった内容が送信される。

# 同時実行数

同時に実行されている Lambda 関数の総数のこと。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535748/011ee65b-230b-3a56-93df-8362746cd304.png)
Lambda 関数を同時にいくつ実行できるかは、**AWS アカウント**と**リージョン**ごとに決まる。
**東京リージョンではデフォルトで 1000 件** までの Lambda 関数を同時に起動することができる。
**AWS アカウントごと** なので、*アカウント A* と *アカウント B* のようにアカウントを分けていれば、それぞれで Lambda 関数を 1000 件起動することができる。
**リージョンごと** でもあるので、同一アカウント内でも *東京リージョン* と *フランクフルトリージョン* でそれぞれ Lambda 関数を 1000 件起動することもできる。

同時実行数の上限である 1000 件は、AWS サポートへ連絡することで、ユースケースに応じて上限緩和が可能。

## 同時実行数のバースト

AWS Lambda では、関数の実行が一度に大量に行われた (バーストした) 場合、**仮に Lambda 関数の同時実行数を上限緩和していても**、初期同時実行数 (東京リージョンの場合は 1000) までしか一度に実行できず、以降は  **1 分ごとに 500 ずつ同時実行可能な数が増加** する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535748/bf525b09-3333-b7cf-69c8-bc0cecd06552.png)
この 1 分毎に増加する同時実行数 **500** は上限緩和することはできない。

## 同時実行数の予約

**2017 年 11 月**のアップデートにより、特定の Lambda 関数に同時実行数を割り当てることができるようになった。
[AWS Lambda 関数別の同時実行の上限を設定](https://aws.amazon.com/jp/about-aws/whats-new/2017/11/set-concurrency-limits-on-individual-aws-lambda-functions/)

一部の Lambda 関数が大量に実行されて、他の関数の実行に影響を与えないよう、特定の Lambda 関数用に同時実行数をとっておく仕組みがある。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535748/74b19b6c-e231-da98-3d20-76b41060a585.png)
**Lambda 関数をウォームアップするわけではなく、あくまで同時実行数を予約するだけである点に注意。**
これを 0 に設定すると、逆に関数の実行を抑制することができる。

## Provisioned Concurrency

**2019 年 12 月**のアップデートにより、あらかじめ Lambda 関数をウォームアップさせておくことが可能になった。
[AWS Lambda が Provisioned Concurrency を発表](https://aws.amazon.com/jp/about-aws/whats-new/2019/12/aws-lambda-announces-provisioned-concurrency/)

Java ランタイムを利用するケースなど、関数の初回起動 (コールドスタート) に大きく時間を要する場合、この機能を使うことで **コールドスタートの時間を削減** することが可能となる。
また、あらかじめ同時実行数をプロビジョニングしておくことが可能となるため、**同時実行数のバーストにあらかじめ備えておくことが可能** となる。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535748/3cf86d72-4e3e-476b-1cc6-b927907a2fe5.png" width=80%>

# ログ

**/aws/lambda/関数名** のロググループが自動で作成され、CloudWatch Logs に書き込まれる。
ロググループのなかで、実行される MicroVM ごとにログストリームが作成されてログが記録される。なお、出力先のロググループを変更することはできない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535748/4f906749-217e-c2e0-aca7-db16594311a4.png)

記録されるログのサンプルは以下の通り。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535748/270650de-602b-47af-2e9a-186637859493.png)

以下のような内容が記録される。

- **Duration** : Lambda の実行に要した時間 (ミリ秒)
- **Billed Duration** : 課金対象となる時間 (ミリ秒)
- **Memory Size** : Lambda 関数に割り当てられているメモリの大きさ (MB)
- **Max Memory Used** : Lambda 関数で使用されたメモリの最大量 (MB)
- **Init Duration** : コールドスタートの場合に記録され、ハンドラ外の処理や、アプリケーションの読み込みに要した時間が記録される (ミリ秒)

なお、既定では CloudWatch Logs に記録されるログの内容を自動で廃棄しないため、実行のたびにログが蓄積され続けることに注意。

## AWS X-Ray との連携

**2017 年 5 月** より、X-Ray と AWS Lambda が統合された。
[AWS X-Ray で AWS Lambda をサポート](https://aws.amazon.com/jp/blogs/news/aws-lambda-support-for-aws-x-ray/)

これにより、Lambda 関数内の細かな処理ごとのパフォーマンスデータが、X-Ray のコンソールから確認できる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535748/f4482c6e-03a9-11db-72f7-e12899ca8795.png)

AWS コンソールより、アクティブトレースにチェックを入れるのみで利用開始できる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535748/965fe96d-d805-c07d-7e76-083331ead033.png)


# メトリクス

CloudWatch から最短で **1 分** 間隔での監視が可能。

下記はどの Lambda 関数でも出力されるメトリクス

- **Invocations (呼び出し)** : Lambda 関数の実行回数
- **Duration (所要時間)** : Lambda 関数の実行に要した時間
- **Errors (エラー)** : 例外を出力するなど正常終了しなかった Lambda 関数の実行回数
- **Throttles (スロットリング)** : Lambda 関数の同時実行数が不足し起動できなかった回数

下記はデッドレターキューが設定されている Lambda 関数で出力されるメトリクス。

- **DeadLetterErrors** : デッドレターキューへの書き込みが失敗した場合にカウントアップされるメトリクス

下記はストリームベースのトリガーを持つ Lambda 関数で出力されるメトリクス。

- **IteratorAge** : バッチデータの最後のレコードがイベントソースに格納された時間と、Lambda がバッチを受け取った時間との差をミリ秒で表示する。この値が大きいほど、入ってくるデータに対して処理が追いついていないことを意味する。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535748/c5a2f95e-988d-4c52-b7c3-afb428e89433.png" width=80%>

下記は特定の関数でなく、リージョン内にある全ての関数について、横断的に提供されるメトリクス。
こちらは 5 分間隔での監視。

- **ConcurrentExecutions** : リージョン内の関数の同時実行数の合計
- **UnreservedConcurrentExecutions** : 同時実行数予約を行っていないリージョン内の関数について、同時実行数を合計したもの

# アクセス許可

他の AWS サービスと同様に IAM の仕組みを利用するのが基本。
IAM ポリシーを割り当てた IAM ロールを作成し、Lambda 関数と紐付ける。
最低限設定が必要な IAM ポリシーとして、よく利用されるものは下記のとおり。

- **AWSLambdaBasicExecutionRole** : Lambda 関数を実行するのに最低限の権限を持つ IAM ポリシー。ログを CloudWatch Logs へ出力するための権限が含まれる。
- **AWSLambdaVPCAccessExecutionRole** : VPC へ接続する Lambda 関数を実行するのに最低限の権限を持つ IAM ポリシー。こちらもログを CloudWatch Logs へ出力するための権限が含まれる。

# 価格

基本は **リクエスト数** と **関数の実行時間** 、さらに **無料の利用枠** を合算した価格となる。
それに加え、Provisioned Concurrency の設定状況により価格が変動する。

## リクエスト数

AWS Lambda を呼び出した回数のこと。
**100 万リクエストあたり 0.2 米ドル**

## 関数の実行時間

**AWS Lambda に割り当てたメモリの量 (GB) x 実行時間 (100ミリ秒単位)** として計算される。
そのため、単位は **GB-秒** となる。

この実行時間には、コールドスタートに要した時間は含まれないため、**ハンドラの実行に要した時間が課金対象** となる。
また、100 ミリ秒単位で切り上げられ、下記のように関数実行時のログに *Billed Duration* として課金対象となった時間が記録される。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535748/f33de216-df5c-b52a-ac66-e5c7ecca22dd.png)
例えば、**200 ミリ秒**で終わる Lambda 関数に **128 MB** のメモリを割り当て **3000 万回**実行した場合の関数の実行時間は、

(**200 (ミリ秒)** / 1000)(秒) x **30,000,000 (回)**  = **6,000,000 (秒)**
**6,000,000 (秒)** × (**128 (MB)** ÷ 1024)(GB) = **750,000 (GB-秒)**

といった形で算出される。

金額は Provisioned Concurrency を有効にしているかどうかで若干変動する。

- Provisioned Concurrency を設定していない場合
  - **1 GB-秒あたり、0.000016667 米ドル**
- Provisioned Concurrency を設定している場合
  - **1 GB-秒あたり、0.000012562 米ドル**

## Provisioned Concurrency を有効化していた時間

こちらも関数の実行時間と同様に、GB-秒という単位で計算される。

例えば、Lambda 関数に **128 MB** のメモリを割り当て Provisioned Concurrency を **900** で **2 時間 (7200 秒)** 有効にした場合、
(**128 (MB)** / 1024)(GB) x **900** x **7200 (秒)** = **810,000 (GB-秒)**
といった形で算出される。

**1 GB-秒あたり、0.000005384 米ドル**

## 1 ヶ月ごとの無料利用枠

以下の利用分に関しては、無料利用枠として、常に毎月の利用料金から減額される。

- リクエスト数 : **100 万件** のリクエスト
- 関数の実行時間 : **400,000 GB-秒**

# AWS Serverless Application Model (AWS SAM)

AWS リソースのデプロイを自動化する AWS CloudFormation について、サーバーレスに関するリソースをより簡潔にデプロイ可能とするために拡張したもの。CloudFormation と同様に、YAML もしくは JSON の形式で AWS Lambda をはじめとするサーバーレス関連リソースを **SAM テンプレート** として記述し、CloudFormation のコンソールや CLI からデプロイができるようになっている。

デプロイの際には、SAM テンプレートから CloudFormation の文法に自動で変換されるようになっている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535748/9518fcef-5be2-6cbf-0fe8-25e1374abcce.png)

オープンソースのもと開発が行われており、GitHub にてリポジトリが公開されている。
[AWS Serverless Application Model (AWS SAM)](https://github.com/awslabs/serverless-application-model)

## デプロイできるリソース

以下の 5 つのリソースタイプに対応している。

- AWS Lambda
- AWS Lambda の Lambda Layers
- Amazon API Gateway
- Amazon DynamoDB
- Serverless Application Repository

## AWS SAM CLI

SAM の仕組みを便利に利用するためのコマンドラインツールとして提供されており、サーバーレスなアプリケーションのひな形作成からビルド、デプロイまでを CLI から実行できる。

こちらもオープンソースで公開されている。
[AWS SAM CLI](https://github.com/awslabs/aws-sam-cli)

### 提供している機能

- AWS Lambda を含むサーバーレスアプリのひな形生成
- SAM テンプレートの文法チェック
- 実装した Lambda 関数のビルド
- Lambda 関数のローカル実行
- Lambda 関数をテストするための模擬イベントデータ生成
- 実装したサーバーレスアプリのデプロイ
- デプロイしたサーバーレスアプリの実行ログ確認
- Serverless Application Repository へサーバーレスアプリを格納

# RDS Proxy (Public Preview)

**2019 年 12 月** にパブリックプレビューとして発表。
[Amazon RDS プロキシのご紹介 (プレビュー)](https://aws.amazon.com/jp/about-aws/whats-new/2019/12/amazon-rds-proxy-available-in-preview/)

AWS Lambda と RDS を組み合わせて利用している場合、そのサービス特性上、Lambda 関数が同時に大量に起動されると、RDS のコネクションプールを使い切りコネクション枯渇の状態になる可能性があった。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535748/69e9edf5-24cc-fcf2-8f46-41fb8a2cc032.png)

そこで、AWS Lambda と RDS のあいだに **RDS Proxy** としてプロキシをはさみ、仮に AWS Lambda からの **コネクション要求が過剰に発生しても接続を待機させる** など、コネクションを効率よく分配する仕組みを提供している。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535748/fb383463-8cc8-6881-f71d-67144f366958.png)

RDS は **MySQL 5.6/5.7** 、および **Aurora MySQL** に対応。
AWS Lambda のコンソールから RDS Proxy を作成することができるようになっている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535748/e94c0055-efa2-80fd-de49-6090de100c24.png)

# 免責

本投稿は個人の意見に基づく内容であり、所属する企業や団体と関係はありません。
また、掲載内容に関しても動作を保証するものではありません。