AWS Lambda関数の呼び出し方を改めて確認する(1)

### はじめに
AWS Lambdaを皆さんどのようにお使いですか？
数msで終わるような軽量の処理から、900秒まで(2019年12月12日現在）の処理までプログラムを実装して簡易な設定をすればすぐ動作させることができて非常に便利なサービスだと思います。そのAWS Lambda関数の呼び出し方（起動方法）について改めて確認したいと思い、記事を書いてみました。なお、当記事ではWebアプリケーションのバックエンドとして動作するLambdaを例にLambda関数がどう呼び出されるのか説明し、[こちらの記事](https://qiita.com/horit0123/items/86c727d8d3ae74b9e52a)ではVPCと絡めて確認しています。

---
### サマリ
1. 様々な方法でLambda関数を起動することが可能だが、リクエスターにはIAMポリシーまたは、Lambda関数のアクセス許可ポリシーによる許可が必要
2. 同期で応答を受け取る方式と非同期で受付のみをし別プロセスで受け取る方式がある
3. 呼び出す際には、AWS Lambdaのエンドポイントにアクセスする必要がある

---
### Lambda関数を起動する方法
Lambda関数を呼び出す方法としては様々な方法がサポートされています。
シェルやプログラム等で呼び出したければ以下の方法を使うことが多いでしょう。

- AWS CLI
- AWS SDK
また、既に登録済みの関数を試しに起動したいという場合には`AWSマネジメントコンソール`経由でボタンクリックで起動することもあるでしょう。
　それ以外に以下に一部記載しましたが様々なAWSサービスと連携してLambda関数を起動することができます。

- Amazon API Gatewayのバックエンド
- Amazon API Gatewayの認可機構であるCustom Authorizer
- Amazon SQS, Amazon Kinesis Data StreamsやAmazon DynamoDB Streamsのバックエンド
- CloudWatch Eventsによるタイマー起動先
- Amazon Alexaのバックエンド


### 同期と非同期
　Lambda 関数を前述の方法で呼び出すことができますがその際に抑えておくべきこととして[同期呼び出し](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/invocation-sync.html)と[非同期呼び出し](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/invocation-async.html)があります。なお、非同期呼び出しの内容についてはこちらの[記事](https://qiita.com/horit0123/items/295f8dc55d8c07e6512a)にまとめてみたので見てください。それぞれ、応答方法、エラー時の挙動が異なりますので未確認というかたは是非ご確認ください。例えば、AWS CLI で以下のコマンドを実行した場合は同期呼び出しとなり、関数の処理応答を待ち次に進みます。一方、非同期呼び出しは受付のみをし、関数の実行は別のプロセスで行う呼び出し方になりますので当然、関数の処理結果は戻ってきません。

``` 同期呼び出し
  aws lambda invoke --function-name QiitaSample --payload '{"key1":"test"}'  result.txt
```

ちなみにCLIでは `--debug`をつけて実行するとAWS CLIがAWSのエンドポイントとどのように通信しているかログを表示してくれます。試しに実行した結果の一部を抜粋しています。以下をご覧いただくとわかとおり、CLIはAWS Lambdaのエンドポイント(lambda.us-east-1.amazonaws.com)にPOSTで通信していることがわかります。AWS Lambda関数を起動した場合は上記エンドポイントにアクセスしてLambdaを起動する形となります。

```
2019-12-12 17:07:36,780 - MainThread - botocore.auth - DEBUG - CanonicalRequest:
POST
/2015-03-31/functions/QiitaSample/invocations

host:lambda.us-east-1.amazonaws.com
x-amz-date:20191212T170736Z
x-amz-security-token:IQoJb3JpZ
```
### アクセス制御
 Lambda関数の呼び出しは誰でも成功できるわけではありません。許可がないと呼び出しは失敗します。AWS CLIのバックエンドで呼び出される場合にも、CloudWatch Eventsのタイマー起動で呼び出される場合にも、Alexaのバックエンドとして呼び出される場合にも、**許可**を明示的にする必要があります。許可を与える方法としては以下の二種類があります。

- AWS IAM(User/Role/Group)へ適用する許可ポリシー
- 各Lambda関数の[アクセス許可ポリシー](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-permissions.html
)
 
クロスアカウントあるいは、各種AWSサービスからの呼び出しを許可したい場合には、自身のアカウントのIAMユーザー、ロール、グループが使えないので（別アカウントだから）、各Lambda関数にリソースベースのアクセス許可ポリシーで許可を与える方式となります。
 では、許可設定について間違いやすいので、実際にコンソールでみてみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535037/873254f4-a585-cf09-1628-75865f2f2ebc.png)

図ではQiitaSampleを真ん中に左側と右側にそれぞれAPI GatewayとAmazon SQSが配置されています。左側、つまり、この例ではAPI Gatewayは、このLambda関数(QiitaSample)のアクセス許可ポリシによってQiitaSample関数を呼び出す許可を与えられていることを意味します。
　右側のAmazon SQSは、このLambda関数(QiitaSample)からアクセスできるサービスを表しています。これは、各Lambda関数ごとに指定可能な「実行ロール」でに指定されたロールがアクセス許可されているサービスが表示されています。さらに細かい内容は「アクセス権限」タブを開くと確認ができるようになっています。なお、Lambda関数内から出力するログについては、CloudWatch Logsへの書き込み権限を関数に与える必要があり、各関数の実行ロールで指定する形となります。

重要なことは、以下の3つを理解することです。
1． Lambda関数を呼び出すには、リクエスターに「許可」が与えられている必要があること
2. 許可はIAMで設定するか各関数でアクセス許可設定をすること
3. Lambda関数がAWSサービスを呼び出したい場合は、Lambda関数の実行ロールに許可設定をするか、呼び出すサービスのリソースベースポリシーでLambda関数に許可を与えること

### 呼び出し経路
　さて、ここからはAWS Lambdaを呼び出す方法をもう少し深掘りしていきたいと思います。今回は、WebアプリケーションをAWSLambda等で実装する場合を題材にパターンを上げて整理したいと思います。


|No. |ケース | 説明| 
|--- |--- |   --- | 
|1 |直接呼び出し | モバイルやPC上のアプリやSPA(Single Page Application)からAWS Lambda関数を直接呼び出す方式|
|2 |API Gateway 経由呼び出し |モバイルやPC上のアプリからAPI Gateway経由で呼び出す方式|
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535037/a7212c4e-eaba-bd60-7726-2ffbe365086a.png)

実は、私自身、初めてAWS Lambdaを勉強した際に、上記の1,2両方呼び出し方があるけど、どう違うの？という疑問を持ちました。違いとしては、以下があるかなと思います。

|No. |観点 | 直接呼出し| API Gateway経由| 
|--- |--- |--- |--- | 
|1 |Lambda起動のための認可 | アプリ自身が直接呼び出すため、Credentialを事前にアプリ側で取得し（例；CognitoのIDプールの利用）、それを利用して呼び出す必要がある。|Lambda関数がAPI Gatewayの呼び出し許可をアクセス許可ポリシーで指定するか、API Gatewayの実行ロールでLambda関数の呼び出しを許可する必要がある。必ずしもアプリでCredentialを取る必要はない（API Gatewayの構成次第|
|2 |Web API向け機能| 直接Lambda APIを実行するため追加機能はつけられない|スロットリング、課金、JWT/OIDCによる認可等の機能を組み込めることや、バックエンドの隠蔽が可能|
|3 |クロスリージョン| ユーザーのロケーションに関係なく呼び出し可能|Lambda関数がAPI Gatewayと異なるリージョンにいても呼び出しは可能|
|4 |クロスアカウント| アプリ自身が直接呼び出すため各アカウントのCredentialが必要、**かつ**、Lambda関数側にもアクセス許可ポリシーで許可設定が必要|Lambda関数がAPI Gatewayの呼び出し許可をアクセス許可ポリシーで指定することで、API Gatewayが異なるアカウントでも呼び出し可能|

もちろん API Gatewayを挟むことでAWSサービル利用料金に違いがでます。ただ、API Gatewayを挟むことでAPIの機能（課金・ログ・認可等）をより実装しやすくなるというところがメリットかなと考えています。

さて、Webアプリケーションを題材に、Lambda関数の呼び出し方についてはここまでとし、次回はLambda関数が別のサービス等を呼び出す部分にも光を当ててみます。