AWS Lambda 同時実行数、エイリアス、環境変数とか実際の現場で使える機能を勉強してみる

# 概要

AWS認定試験資格取得に向けて実際にAWSを利用してみるシリーズの投稿です。
今回はAWS Lambdaをちょっとしっかり勉強してみようという中級編です。
気が付いたらLambdaがかなり進化をしていて、久しぶりに本を購入して勉強したのでその内容をまとめた投稿になっています。

私が初めてLambdaを使い始めた時の制限、コールドスタート問題の解決（同時実行数）や、実際の現場で使えるLambdaの切り戻し（エイリアス）、環境ごと異なる設定（環境変数）など、実運用で使える機能がたくさんあったのでその内容を投稿します。

認定試験勉強的にはAWS Lambdaの少しコアな部分を掴める内容になっています。
インフラに依存せずサクッとプログラミングができるLambdaを使ってみたい人、興味がある方は読んでみて下さい。

資格試験の勉強法は記事は以下を参照。

[AWS初心者がAWS 認定ソリューションアーキテクト – アソシエイト資格試験に合格した時の勉強法](https://qiita.com/fkooo/items/e5284a4ed3c3466ffd41 "AWS初心者がAWS 認定ソリューションアーキテクト – アソシエイト資格試験に合格した時の勉強法")
[AWS初心者がAWS 認定ソリューションアーキテクト – プロフェッショナル資格試験に合格した時の勉強法](https://qiita.com/fkooo/items/17fa401f4e46ecd00675 "AWS初心者がAWS 認定ソリューションアーキテクト – プロフェッショナル資格試験に合格した時の勉強法")

## 想定読者
* AWSでLambdaを使ってみたい人
* Lambdaの知識をレベルアップしたい人
* Lambdaを実運用で使っている人

## 参考書籍

[AWS Lambda実践ガイド 第2版](https://amzn.to/3E5O6xm "AWS Lambda実践ガイド 第2版")
<a href="https://www.amazon.co.jp/dp/4295013307?tag=blog_lambda_books_001-22&linkCode=ogi&th=1&psc=1" target="_blank" rel="noopener"><img border="0" src="https://m.media-amazon.com/images/I/51TVYJndNCL._SL160_.jpg" ></a>

[【Kindle版】AWS Lambda実践ガイド 第2版](https://amzn.to/37vUsd1 "【Kindle版】AWS Lambda実践ガイド 第2版")
<a href="https://www.amazon.co.jp/dp/B09TVG5H17?tag=blog_lambda_books_001-22&linkCode=ogi&th=1&psc=1" target="_blank" rel="noopener"><img border="0" src="https://m.media-amazon.com/images/I/51vlB3JskRL._SL160_.jpg" ></a>

多くを参考にさせて頂き、たくさんの新しい発見がありました。
解説も画面とサンプルソースがあり丁寧で読みやすく、Lambdaに特化した本はあまりないので現時点で唯一の専門書と言っても良いのではないでしょうか(^^)

その他、Lambdaの良書の紹介は以下が参考になると思います。

[AWS Lambda おすすめ本5冊紹介 ～サーバレス基礎からAmplifyを使った本格開発まで～](https://qiita.com/fkooo/items/6fdbe80e7e7376ffc61b "AWS Lambda おすすめ本5冊紹介 ～サーバレス基礎からAmplifyを使った本格開発まで～")


## 目次

* Lambda基礎
  * 関数の引数
  * ハンドラ
  * 呼び出し方式
  * ランタイム
* コールドスタート問題解決
  * プロビジョニングされた同時実行
  * ENI枯渇問題
  * RDSコネクション枯渇問題
* リリース後障害発生すぐリリースを切り戻したい（Lambdaのバージョン管理）
  * バージョン管理
  * エイリアス
  * 加重エイリアス
* 環境ごと異なる設定にしたい（Lambdaの環境変数）
  * 環境変数
* 制限事項

Lambda基礎で基本的なことを振り返り、そこから実際の現場で使えるコールドスタート問題解決、リリース切り戻し、環境ごと異なる設定の方法、最後にそれでも残るLambdaの制限事項をまとめていこうと思います。

なお、ランタイムはPythonで記載しています。

## Lambda基礎

### 関数の引数

まずはLambda関数の引数についてです。
Lambdaはサーバーレスの仕組みになるので何かしらのイベントを契機に引数でそれらの情報を受け取って動く仕組みになります。

Lambda関数の作成から**一から作成**を選択しLambda関数を作成すると以下がデフォルトのソースコードとして表示されます。
引数は、**event**、**context**の2つ。

```python
import json

def lambda_handler(event, context):
    # TODO implement
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }
```

**context**は、ランタイムによって微妙に違うのですが、Lambda関数のタイムアウトの残り時間やLambda関数名、割当メモリ量が取れるようです。

Python

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/python-context.html

Javaはこちら

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/java-context.html

**event**の方はLambdaが実行されるイベントごとに異なり、例えばAPIGatewayだと、こちら

https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format

**context**はLambdaの公式ドキュメントに記載があるので探しやすいですが、**event**の方は各イベントのドキュメントに記載があり、章立てがイベントごと異なるのでちょっと探しにくいです(^^;)

ちなみに引数に対してLambda関数のアウトプットとなる戻り値も**event**ごとの公式ドキュメントに記載があります。
例えばAPIGatewayだと、こちら

https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-output-format

そのため、引数の**event**と戻り値について呼び出し側の各イベントの公式ドキュメントを確認しましょう。となります。

### ハンドラ

次にハンドラになります。
ハンドラはLambdaが実行されるときに最初に呼ばれる関数（いわゆるメソッド）になります。
**ファイル名.関数名**が該当します。
以下のソースコードだと**ファイル名**：**lambda_function.py**、**関数名**：**lambda_handler**が該当します。
（今回Lambda関数名をtestとしています。Lambdaの内部管理としてLambda関数名はフォルダ名に使用されるようです）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188544/4ce6c307-5a69-5aa1-3a1e-6fa0e391311d.png)

これがデフォルト状態ですが、以下から変更も可能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188544/b0934865-2d5d-3439-e3d7-c254c7487c45.png)

AWSコンソールからLambda関数を作成すると自動的に設定されるのであまり意識しませんが、 SAM（Serverless Application Model）を使い始めると結構意識します。

### 呼び出し方式

どんどんいきます（^^）
次はLambda関数の呼び出し方式についてです。

Lambda関数の呼び出し方は呼び出し側によって決まり、以下の3種類になります。

* 同期型：例えば APIGateway からの呼び出し
* 非同期型：例えば S3 からの呼び出し
* ストリーム型：例えば DynamoDB からの呼び出し

つまりAWSサービスから呼び出される時は、**変更できません**。
（自身のプログラムからSDKを使って呼び出す場合は指定することができます）

この話はエラーハンドリングの処理の検討を伴い、だいぶ深い話しになるので詳細は割愛します。
分かりやすかった参考サイトを載せておきます。

https://dev.classmethod.jp/articles/lambda-idempotency/

### ランタイム

Lambda基礎編の最後はランタイムになります。

プログラミング言語が Node.js、Python、Ruby、Java、Go、.NET から選べます。
自分に合ったプログラミング言語が選べてサクッとプログラミングができるのがLambdaの強みですね(^^)

各言語のサポートバージョンなどの詳細が記載されている公式ドキュメントは以下です。

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-runtimes.html

## コールドスタート問題解決

ここからは実際の運用現場で使える実践編です！
私が学び直してびっくりした順に記載します(^^)
まずはLambdaの制限事項とされていたコールドスタート問題。。。

コールドスタート問題が何物かは以下の公式ブログが分かりやすかったです。

https://aws.amazon.com/jp/blogs/news/operating-lambda-performance-optimization-part-1/

このブログにも記載がありますが、以下の図でLambdaは初回呼び出しイベントを受け取るとソースコードをダウンロード(**Download your code**)、指定されたメモリ、ランタイム、および各種設定に基づいた環境を作成(**Start new execution environment**)します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188544/b20fa964-b503-61b8-4cd2-33b9ed245fa4.png)

この部分が初回呼び出しの時にだけ行われ時間がかかることから、コールドスタート問題と言われていました。これが**プロビジョニングされた同時実行**によって解決されています！

### プロビジョニングされた同時実行

**設定** ＞ **プロビジョニングされた同時実行** から簡単に設定できます。事前に同時実行数を決めておいてLambda関数を起動（コールドスタート処理を済ませる）しておこうという考え方ですね。
（画面サンプルは 5 を設定しています。5つ起動するまでしばらく時間がかかります）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188544/0221e6ae-b1e2-7623-b5c8-92fdca567fb2.png)

ちなみにコストが+αでかかります。**性能を求めるかコストを求めるかのトレードオフ**になるでしょう。
丁寧なのは設定画面でコスト予測が表示されます。（コスト計算が複雑なAWSでは珍しい^^;）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188544/16d7fc35-1ce1-43f9-117b-b6649b5ff9dd.png)

プロビジョニングされた同時実行について、公式ブログでは以下で説明されています。

https://aws.amazon.com/jp/blogs/news/new-provisioned-concurrency-for-lambda-functions/

このブログによると「500 件の同時実行を10,000 回」を実行し、プロビジョニングしていない場合は95%を超えると約 1 秒かかるが、プロビジョニングしている場合はすべてミリ秒のレスポンスを維持できるとのこと。これはすごいと思います。
これで本運用でもLambdaを使えるアプリケーションが増える！！
気がしました。

話しは少しコールドスタート問題から離れますが、大量アクセスがあるアプリケーションでLambdaを使用する場合、以前は制限事項となっていた以下の2つの問題も解決されていたので合わせて記載します。

* ENI枯渇問題
* RDSコネクション枯渇問題

### ENI枯渇問題

こちらの公式ブログで紹介されています。

https://aws.amazon.com/jp/blogs/news/announcing-improved-vpc-networking-for-aws-lambda-functions/

以前の問題は簡単に言うと以下

* VPC内のLambdaの起動時に新しい ENI を作成してアタッチする時間は、より長いコールドスタートを引き起こす
* Lambdaの増加に対応するにつれてより多くの ENI が作成され、サブネット内やアカウント単位の IP アドレスが枯渇する

これが解決されています。
ブログの中の図を見ると明らかなので、、、

Before
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188544/b55fccd2-dfb5-f543-e1bf-18fcc3955a06.png)

After
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188544/7c5cb3ed-512d-5074-12f1-c598fb1d0d2b.png)

シンプル！凄い！！
**VPC TO VPC NAT**という機能が追加されたようです。
以上です。

### RDSコネクション枯渇問題

こちらの公式ブログで紹介されています。

https://aws.amazon.com/jp/blogs/news/using-amazon-rds-proxy-with-aws-lambda/

こちらも問題点としては類似してますね。

* Lambdaの増加に対応するにつれてより多くの DBコネクション が作成され、DBのコネクションリソースを大量消費してしまう

Amazon RDS Proxy を使用することによって解決しています。

https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/rds-proxy.html

コールドスタート問題の補足以上です。

## リリース後障害発生すぐリリースを切り戻したい（Lambdaのバージョン管理）

タイトルに実際の運用感が出ていたでしょうか？

実際にLambdaで開発して本番リリースしました。
するとリリース直後に予期せず障害発生！
とりあえずユーザ影響を最小限にするため元のプログラムに切り戻す。。。
Lambdaに関わらず実運用でよくあることではないでしょうか。

この時のLambdaの対応について記載します。

### バージョン管理

Lambdaでは自分の任意のタイミングで**新しいバージョンを発行**ができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188544/3426e0c0-bc8e-2c36-223c-c2c22a65945f.png)

ちなみにどんなバージョンなのか説明も記載できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188544/77086e4a-73f1-23b6-ec24-df1235619e18.png)

Lambda関数のバージョンには以下が保持されるようです。
万が一の切り戻し用途としては十分かなと思います。

* 関数コードと関連するすべての依存関係
* 関数を呼び出す Lambda ランタイム
* 環境変数を含むすべての関数設定
* 関数の特定のバージョンを識別する一意の Amazon リソースネーム (ARN)

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/configuration-versions.html

### エイリアス

秀逸と思えたのはバージョンに**エイリアスを作成**できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188544/0e8abfc2-e78b-be5b-fa53-105f5037a1b2.png)

例えば**PRD**（**本番稼動バージョン**）エイリアスにして、バージョン**2**に設定しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188544/6189c6b2-bf62-142f-ccec-f9da87f3de89.png)

Lambdaを呼び出すAWSサービスはエイリアス経由でLambdaにアクセスしておきます。
呼び出し側のAWSサービスにより様々ですが、ARNを指定すると以下になります。
エイリアス**PRD**を指定していることが分かります。

```
arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:test:PRD
```

その後、次のバージョンアップが行われ新機能のリリースを行いました（リリースしてバージョン**3**を作成）。ただ、エイリアスは変更していないので、Lambdaを呼び出しているシステムはエイリアス**PRD**のバージョン**2**を参照して動いています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188544/b162135c-4c85-9892-00c2-61961a0486d3.png)

では、いよいよ新機能のリリースです。
はい、エイリアスのタブから**編集**ボタンを押して、バージョンを**3**にするだけです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188544/b472f744-fd4a-9b4e-1595-cab3b215e728.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188544/ee6f7ae5-94dd-7573-9494-c33463e4bc5b.png)

はい、リリース完了です。
　・
　・
　・
え！リリースしたけど、障害発生！！
すぐに切り戻しましょう！！！
はい、エイリアスのバージョンを**2**にして切り戻し完了です。

障害発生連絡から切り戻しまで30秒くらいでしょうか。
この手軽さはリリースに対する心理的プレッシャーがかなり下がります。
LambdaによってDXが加速していくでしょう(^^)

### 加重エイリアス

エイリアスの補足です。
画面にちらほら見えていた**加重エイリアス**、**重み=100%**。。。

そうです。いわゆるABテスト。新機能がどれだけユーザに受け入れられるか。
とりあえず全体の20%程度のユーザに適用して市場の反応をみよう。というやつですね。
簡単にエイリアスの設定画面から設定できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188544/60b7b262-9a3e-9c89-eed9-e63383b4cba8.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188544/f75da140-7043-8c19-a847-7de9f9c2ed20.png)

これで既存のバージョン**2**に**80%**、新バージョン**3**に**20%**、処理が振り分けられます。
Lambdaのバージョン管理、秀逸です！
ぜひバージョン管理、エイリアスを使って実運用で楽しましょう。

エイリアスの公式ドキュメントは以下です。

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/configuration-aliases.html

## 環境ごと異なる設定にしたい（Lambdaの環境変数）

次のテーマです。こちらも実運用でよくあるケースですね。
例えば、DBの接続情報。
本番環境と開発環境ではもちろん指定する内容が異なるでしょう。
そんなときに利用するのがLambdaの環境変数になります。

### 環境変数

設定は簡単です。以下のAWSコンソール画面で設定できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188544/7987191a-fd37-52fd-021c-cb6be2890915.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188544/013cafa5-8631-f068-aef8-abe767da3c7f.png)

任意の**キー**を設定し環境によって変わる設定値を**値**の部分に設定します。
するとソース内で以下ように取得できます。

```python
DB_HOST = os.environ['DB_HOST']
```

これで環境ごとの設定をソースコードから分離することができます。
ただし、注意事項もあります。
リージョンや関数名などは事前定義済の環境変数として最初から登録されている予約済みの環境変数もあるので、使えないキーもあります。一覧が以下にあるので確認しましょう。

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/configuration-envvars.html#configuration-envvars-runtime

## 制限事項

最後にLambdaの制限事項です。
いろいろ便利な機能追加によってLambdaは進化していますが、もちろん残っている制限事項もあります。
制限事項を理解してLambdaを正しく使いましょう。

例えばですが、

* 処理時間は15分に制限される
* ソースファイルのアーカイブは50MB、展開後は250MBの上限がある
* 1度のイベントで2回以上実行されることがある
* 同時実行回数はアカウント内で1,000（ただしAWSへの申請で引き上げは可能）
* リクエストレスポンスのペイロードサイズに制限がある

等があります。
詳細はこちらの公式ドキュメントになります。

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/gettingstarted-limits.html

## まとめ
お疲れ様でした！

今回は参考書籍で勉強した内容をLambdaの基礎から実際の現場で使える運用方法、新機能で解決された既存の問題を中心に記載しました。
プログラムに集中出来るサーバーレスLambdaがもっと便利になって、開発者はより業務ロジックに集中出来る世の中になれば良いなと思います(^^)

本投稿が少しでも皆さんの良いサーバーレスライフの助けになればと思います(_ _)

## 資格取得に向けてAWSサービスを実際に利用してみるシリーズの投稿一覧

[とりあえず30分でAWS利用料金の高額請求に備える～予算アラート設定・MFA・料金確認～](https://qiita.com/fkooo/items/67c53c997508e25605ea "とりあえず30分でAWS利用料金の高額請求に備える～予算アラート設定・MFA・料金確認～")
[AWS ECSでDocker環境を試してみる](https://qiita.com/fkooo/items/d7d98e16e91921508450 "AWS ECSでDocker環境を試してみる")
[Amazon Cognitoを使ってシンプルなログイン画面を作ってみる](https://qiita.com/fkooo/items/660cab0090a80861155b "Amazon Cognitoを使ってシンプルなログイン画面を作ってみる")
[AWS NATゲートウェイを使ってプライベートサブネットからインターネットにアクセスする](https://qiita.com/fkooo/items/a474db0f3c2e72448d36 "AWS NATゲートウェイを使ってプライベートサブネットからインターネットにアクセスする")
[API GatewayをPrivateで作成してみる](https://qiita.com/fkooo/items/577831abd9803eb91b16 "API GatewayをPrivateで作成してみる")
[AWSのAI(Rekognition/Polly/Transcribe/Comprehend/Translate/Textract)サービスを試してみる](https://qiita.com/fkooo/items/10f75bafd08c60fc0a66 "AWSのAI(Rekognition/Polly/Transcribe/Comprehend/Translate/Textract)サービスを試してみる")
[AWS Lambda 同時実行数、エイリアス、環境変数とか実際の現場で使える機能を勉強してみる](https://qiita.com/fkooo/items/1b24e01252e145a378db "AWS Lambda 同時実行数、エイリアス、環境変数とか実際の現場で使える機能を勉強してみる")（本記事）
[AWS Lambda SAMとは？～AWS SAMを使ってPythonのLambdaプログラムを簡単に作成する～](https://qiita.com/fkooo/items/2dc5ded5149379d7305a "AWS Lambda SAMとは？～AWS SAMを使ってPythonのLambdaプログラムを簡単に作成する～")
[AWS SAMを使って最もシンプルにLambda × APIGatewayのWebAPIを構築する](https://qiita.com/fkooo/items/e08cdea0f73f60dabd3d "AWS SAMを使って最もシンプルにLambda × APIGatewayのWebAPIを構築する")
[AWS SAMを使って最もシンプルにLambda × S3 のS3イベント駆動プログラムを構築する](https://qiita.com/fkooo/items/fa1d03c1d9e532956e2d "AWS SAMを使って最もシンプルにLambda × S3 のS3イベント駆動プログラムを構築する")
[Cloud9でAWS Amplifyの公式チュートリアルGetting startedをやってみる](https://qiita.com/fkooo/items/84f8f57e2317b0973496 "Cloud9でAWS Amplifyの公式チュートリアルGetting startedをやってみる")