コンテナイメージを使用した Lambda 関数を作成してみた

# 背景・目的
AWS Lambdaでは、コンテナイメージをサポートしています。こちらについて基本的な知識の整理と簡単な動作確認を行います。

# まとめ
下記に特徴をまとめます。

| 特徴 | 説明 |
|:--|:--|
|Lambdaのコード|下記で構成される<BR><BR>・スクリプト<BR>・コンパイルされたプログラム<BR>・上記の依存関係|
|デプロイ方法|デプロイパッケージを使用し、Lambdaに関数コードをデプロイする|
|Lambdaがサポートするデプロイパッケージ|・コンテナイメージ<BR>・zipファイル|
|Lambda関数のコンテナイメージを構築する方法|・Lambda の AWS ベースイメージを使用する<BR>・AWS の OS 専用ベースイメージを使用する<BR>・非 AWS ベースイメージを使用する|
|Lambda の AWS ベースイメージを使用する|Lambda に AWS ベースイメージの 1 つを使用して、関数コードのコンテナイメージを構築する<BR><BR>ベースイメージには、Lambdaでコンテナイメージを実行するために必要な言語ランタイムと、その他のコンポーネントがプリロードされる|
|AWS の OS 専用ベースイメージを使用する|AWS OS専用のベースイメージには、下記が含まれている<BR><BR>・Amazon Linux ディストリビューション<BR>・ランタイムインターフェイスエミュレータ|
|非 AWS ベースイメージを使用する|以下のいずれかのイメージマニフェストの形式に準拠するイメージをサポートする<BR>・Docker Image Manifest V2 Schema 2<BR>・Open Container Initiative (OCI) 仕様<BR><BR>Lambda は、すべてのレイヤーを含めて最大 10 GB の非圧縮のイメージサイズをサポートする<BR><BR>イメージに Lambda との互換性を持たせるには、当該言語のランタイムインターフェイスクライアントをイメージに含める必要がある|
|要件|AWS CLI、Docker CLIがインストールされていること<BR><BR>コンテナイメージには、カスタムラインタイムに、LambdaランタイムAPIが実装されていること<BR><BR>コンテナイメージは、読み取り専用のファイルシステム上で実行可能であること<BR><BR>アプリケーションコードが外部のLinuxユーザによる実行が制限されているファイルに依存しないこと<BR><BR>Linuxベースのコンテナイメージのみがサポートされる<BR><BR>マルチアーキテクチャのコンテナイメージを使用する関数をサポートしない|
|関数のライフサイクル|関数を呼び出す準備ができたことを示すため、Lambdaはすべての関数の関数設定に状態フィールドを含める<BR><BR>Stateは、関数の現在の関数ステータスに関する情報を提供する<BR><BR>関数の状態は下記のものがある。<BR>・Pending<BR>・Active<BR>・Failed<BR>・Inactive|
|LambdaのランタイムAPI|Lambdaは、カスタムランタイムのHTTP APIを呼び出して、Lambdaの呼び出しイベントを受け取り、レスポンスをLambdaの実行環境に送り返す|

# 概要
下記の記事を基に整理します。

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/images-create.html#runtimes-images-provided

> AWS Lambda 関数のコードは、スクリプトまたはコンパイルされたプログラム、さらにそれらの依存関係で構成されます。デプロイパッケージを使用して、Lambda に関数コードをデプロイします。Lambda は、コンテナイメージと .zip ファイルアーカイブの 2 種類のデプロイパッケージをサポートします。

- Lambdaのコードは、下記で構成される
    - スクリプト
    - コンパイルされたプログラム
    - 上記の依存関係
- デプロイパッケージを使用して、Lambdaに関数コードをデプロイする
- Lambdaは、下記のデプロイパッケージをサポートする
    - コンテナイメージ
    - zipファイル

> Lambda 関数のコンテナイメージを構築するには 3 つの方法があります。
> - Lambda の AWS ベースイメージを使用する
> AWS ベースイメージには、言語ランタイム、Lambda と関数コード間のやり取りを管理するランタイムインターフェイスクライアント、ローカルテスト用のランタイムインターフェイスエミュレーターがプリロードされています。

> - AWS の OS 専用ベースイメージを使用する
AWS OS 専用ベースイメージには、Amazon Linux ディストリビューションおよびランタイムインターフェイスエミュレータが含まれています。これらのイメージは、Go や Rust などのコンパイル済み言語や、Lambda がベースイメージを提供していない言語または言語バージョン (Node.js 19 など) のコンテナイメージの作成によく使用されます。OS 専用のベースイメージを使用してカスタムランタイムを実装することもできます。イメージに Lambda との互換性を持たせるには、当該言語のランタイムインターフェイスクライアントをイメージに含める必要があります。

> - 非 AWS ベースイメージを使用する
Alpine Linux や Debian など、別のコンテナレジストリの代替ベースイメージを使用することもできます。組織が作成したカスタムイメージを使用することもできます。イメージに Lambda との互換性を持たせるには、当該言語のランタイムインターフェイスクライアントをイメージに含める必要があります。

- Lambda関数のコンテナイメージを構築するには下記の3つの方法がある
    - LambdaのAWSベースイメージを使用する
    - AWS の OS 専用ベースイメージを使用する
    - 非 AWS ベースイメージを使用する


## 要件
> 「AWS Command Line Interface (AWS CLI) バージョン 2」および「Docker CLI」をインストールします。さらに、次の各要件にも注意してください。
> - コンテナイメージには、カスタムランタイムに Lambda ランタイム API を使用する が実装されている必要があります。AWS からの、オープンソースのランタイムインターフェイスクライアントには、この API が実装されています。ランタイムインターフェイスクライアントを必要なベースイメージに追加することで、Lambda と互換性を持たせることができます。

> - コンテナイメージは、読み取り専用のファイルシステム上で実行可能である必要があります。機能コードは、512 MB から 10,240 MB の間で、1 MB 刻みで書き込み可能な /tmp ディレクトリにアクセスできます。

> - デフォルトの Lambda ユーザーは、関数コードを実行するために必要なすべてのファイルを読み取ることができる必要があります。Lambda は、最小特権のアクセス許可を持つデフォルトの Linux ユーザーを定義することで、セキュリティのベストプラクティスに従います。ご自身のアプリケーションコードが、外部の Linux ユーザーによる実行が制限されているファイルに依存していないことを確認します。

> - Lambda では、Linux ベースのコンテナイメージのみがサポートされます。

> - Lambda は、マルチアーキテクチャのベースイメージを提供します。ただし、関数用に構築するイメージは、アーキテクチャの 1 つだけをターゲットにする必要があります。Lambda は、マルチアーキテクチャのコンテナイメージを使用する関数をサポートしません。

- 下記の要件がある
    - AWS CLI、Docker CLIがインストールされていること
    - コンテナイメージには、かすタイムラインタイムに、LambdaランタイムAPIが実装されていること
        - AWSから提供されているオープンソースのラインタイムインターフェイスには、このAPIが実装されている
        - ランタイムインタフェイスクライアントをベースイメージに追加することでLambdaと互換性をもたせる事が可能
    - コンテナイメージは、読み取り専用のファイルシステム上で実行可能である必要がある
    - アプリケーションコードが外部のLinuxユーザによる実行が制限されているファイルに依存しないこと
        - デフォルトのLambdaユーザは、関数コードを実行することでセキュリティのベストプラクティスに従う
    - Linuxベースのコンテナイメージのみがサポートされる
    - マルチアーキテクチャのコンテナイメージを使用する関数をサポートしない

### Lambda ランタイム API
下記を基に整理します。

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

> AWS Lambda では、カスタムランタイムの HTTP API を使用して Lambda の呼び出しイベントを受け取り、レスポンスデータを Lambda の実行環境に送り返します。このセクションでは、Lambda ランタイム API の API リファレンスについて説明します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/ab9965b7-fc54-3e2d-5944-4c081dd0186e.png)

※出典：[カスタムランタイムに Lambda ランタイム API を使用する](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/runtimes-api.html)

- Lambdaは、カスタムランタイムのHTTP APIを呼び出して、Lambdaの呼び出しイベントを受け取り、レスポンスをLambdaの実行環境に送り返す

## Lambda の AWS ベースイメージを使用する
> Lambda に AWS ベースイメージの 1 つを使用して、関数コードのコンテナイメージを構築します。ベースイメージには、Lambda でコンテナイメージを実行するために必要な言語ランタイムおよびその他のコンポーネントがプリロードされます。関数コードと依存関係をベースイメージに追加し、コンテナイメージとしてパッケージ化します。

- Lambda に AWS ベースイメージの 1 つを使用して、関数コードのコンテナイメージを構築する
- ベースイメージには、Lambdaでコンテナイメージを実行するために必要な言語ランタイムと、その他のコンポーネントがプリロードされる
- 関数コードと依存関係をベースイメージに追加し、コンテナイメージとしてパッケージ化する

> AWS では、Lambda 用の AWS ベースイメージの更新を定期的に実施しています。Dockerfile の FROM プロパティにイメージ名が含まれている場合、Docker クライアントは Amazon ECR リポジトリから最新バージョンのイメージを取り出します。更新されたベースイメージを使用するには、コンテナイメージを再ビルドして、関数のコードを更新する必要があります。

- Lambda用のAWSベースイメージは定期的に更新される

> Node.js 20、Python 3.12、Java 21、.NET 8、Ruby 3.3 以降のベースイメージは、Amazon Linux 2023 の最小コンテナイメージに基づいています。以前のベースイメージでは Amazon Linux 2 が使用されています。AL2023 ランタイムには、デプロイのフットプリントが小さいことや、glibc などのライブラリのバージョンが更新されていることなど、Amazon Linux 2 に比べていくつかの利点があります。

- 下記以降のベースイメージは、Amazon Linux 2023最小コンテナイメージに基づいている
    -  Node.js 20
    -  Python 3.12
    -  Java 21
    -  .NET 8
    -  Ruby 3.3
- Amazon Linux2と比較して、上記により、デプロイのフットプリントが小さい点や、glibcなどのライブラリのバージョンが更新されている

> AL2023 ベースのイメージでは、Amazon Linux 2 のデフォルトのパッケージマネージャである yum の代わりに microdnf (dnf としてシンボリックリンク) がパッケージマネージャとして使用されています。microdnf は dnf のスタンドアロン実装です。AL2023 ベースのイメージに含まれるパッケージのリストについては、「Comparing packages installed on Amazon Linux 2023 Container Images」の「Minimal Container」列を参照してください。AL2023 と Amazon Linux 2 の違いの詳細については、AWS コンピューティングブログの「Introducing the Amazon Linux 2023 runtime for AWS Lambda」を参照してください。

- AL2023のベースイメージでは、microbnfがパッケージマネージャーとして使用されている
    - microbnfはdnfとしてシンボリックリンクが作成されている
    - Amazon Linux2のデフォルトのパッケージマネージャーはyumである

## AWS の OS 専用ベースイメージを使用する
> AWS OS 専用ベースイメージには、Amazon Linux ディストリビューションおよびランタイムインターフェイスエミュレータが含まれています。これらのイメージは、Go や Rust などのコンパイル済み言語や、Lambda がベースイメージを提供していない言語または言語バージョン (Node.js 19 など) のコンテナイメージの作成によく使用されます。OS 専用のベースイメージを使用してカスタムランタイムを実装することもできます。イメージに Lambda との互換性を持たせるには、当該言語のランタイムインターフェイスクライアントをイメージに含める必要があります。

- AWS OS専用のベースイメージには、下記が含まれている
    - Amazon Linux ディストリビューション
    - ランタイムインターフェイスエミュレータ
- Go や Rust などのコンパイル済み言語や、Lambda がベースイメージを提供していない言語または言語バージョン (Node.js 19 など) のコンテナイメージの作成によく使用される
- OS 専用のベースイメージを使用してカスタムランタイムを実装することもできる
- イメージに Lambda との互換性を持たせるには、当該言語のランタイムインターフェイスクライアントをイメージに含める必要がある


https://github.com/aws/aws-lambda-base-images/blob/provided.al2023/Dockerfile.provided.al2023

https://github.com/aws/aws-lambda-base-images/blob/provided.al2/Dockerfile.provided.al2

## 非 AWS ベースイメージを使用する
> Lambda では、次のいずれかのイメージマニフェストの形式に準拠するイメージをサポートしています。
> - Docker Image Manifest V2 Schema 2 (Docker バージョン 1.10 以降で使用)
> - Open Container Initiative (OCI) 仕様 (v1.0.0 以降)

> Lambda は、すべてのレイヤーを含めて最大 10 GB の非圧縮のイメージサイズをサポートします。

> 注記
イメージに Lambda との互換性を持たせるには、当該言語のランタイムインターフェイスクライアントをイメージに含める必要があります。

- 以下のいずれかのイメージマニフェストの形式に準拠するイメージをサポート
    - Docker Image Manifest V2 Schema 2
    - Open Container Initiative (OCI) 仕様
- Lambda は、すべてのレイヤーを含めて最大 10 GB の非圧縮のイメージサイズをサポートする
- イメージに Lambda との互換性を持たせるには、当該言語のランタイムインターフェイスクライアントをイメージに含める必要がある

## ランタイムインターフェイスクライアント
> OS 専用ベースイメージまたは代替のベースイメージを使用する場合、イメージにランタイムインターフェイスクライアントを含める必要があります。ランタイムインターフェイスクライアントは、Lambda と関数コード間のやり取りを管理する カスタムランタイムに Lambda ランタイム API を使用する を拡張する必要があります。AWS では、オープンソースのランタイムインターフェイスクライアントを次の言語で提供しています。

> - Node.js
> - Python
> - Java
> - .NET
> - Go
> - Ruby
> - Rust — Rust ランタイムクライアントは実験的なパッケージです。これは変更される可能性があり、評価のみを目的としています。

- OS専用ベースイメージ、代替のベースイメージを使用する場合には、イメージにランタイムインターフェイスクライアントを含める必要がある
- ランタイムインターフェイスクライアントは、Lambda と関数コード間のやり取りを管理する カスタムランタイムに Lambda ランタイム API を使用する を拡張する必要がある

## 関数のライフサイクル
> 新規または更新済みのコンテナイメージをアップロードすると、Lambda は、関数が呼び出しを処理する前にイメージを最適化します。最適化プロセスには数秒かかる場合があります。この関数は、プロセスが完了するまで Pending 状態のままです。その後、関数は Active 状態に移行します。Pending 状態のときに関数を呼び出すことはできますが、関数に対する他のオペレーションは失敗します。イメージ更新の進行中に発生した呼び出しは、以前のイメージのコードを実行します。

- 新規、更新済みのコンテナイメージをアップロードすると、Lambdaは関数が呼び出しを処理する前にイメージを最適化する
    - 最適化プロセスには数秒かかる場合がある
    - 関数はプロセスが完了するまでPending状態
    - その後、Active状態になる
    - Pending状態のときには、関数に対する他のオペレーションは失敗する
    - イメージ更新の進行中に発生した呼び出しは以前のイメージのコードを実行する

> 数週間にわたって関数が呼び出されない場合、Lambda は最適化されたバージョンを再利用し、関数は Inactive 状態に移行します。関数を再度アクティブにするには、関数を呼び出す必要があります。Lambda は最初の呼び出しを拒否し、関数は Lambda がイメージを再最適化するまで Pending 状態に入ります。その後、関数は Active 状態に戻ります。

- 数週間関数が呼び出されない場合、Lambdaは最適化されたバージョンを再利用し、関数はInactive状態になる
    - 再度アクティブにするには、関数を呼び出す
    - Lambdaは最初の呼び出しを拒否し、関数はLambdaがイメージを再最適化するまでPendingになる
    - その後、関数はActive状態になる

> Lambda は、Amazon ECR リポジトリから関連するコンテナイメージを定期的に取得します。対応するコンテナイメージが Amazon ECR に存在しなくなった場合、またはアクセス許可が失効した場合、関数は Failed 状態になり、Lambda が関数呼び出しに対して失敗を返します。

- LambdaはECRから関連するコンテナイメージを定期的に取得する
- 対応するコンテナイメージがECRに存在しない、アクセス許可が失敗するとFailedになる

### Lambda 関数の状態
下記を基に整理します。

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


> 関数を呼び出す準備ができたことを示すために、Lambda はすべての関数の関数設定に状態フィールドを含めます。State は、関数の現在の関数のステータスに関する情報 (関数を正常に呼び出すことができるかどうかなど) を提供します。関数の状態は、関数呼び出しの動作や関数によるコードの実行方法を変更するものではありません。関数の状態は以下のとおりです。
> - Pending — Lambda は関数を作成した後、関数の状態を保留に設定します。保留状態の場合、Lambda は関数のリソース (VPC や EFS リソースなど) の作成または設定を試みます。Lambda は、保留状態の関数を呼び出しません。関数に対する呼び出しやその他の API アクションは、すべて失敗します。

> - Active — Lambda がリソース設定とプロビジョニングを完了した後、関数はアクティブ状態に移行します。関数は、アクティブな間のみ正常に呼び出すことができます。

> - Failed — リソース設定またはプロビジョニングでエラーが発生したことを示します。

> - Inactive — Lambda 用に設定された外部リソースを再利用するのに十分な時間アイドル状態を維持した関数は非アクティブになります。非アクティブな関数を呼び出そうとすると、呼び出しは失敗し、関数リソースが再作成されるまで、Lambda は関数を保留状態に設定します。Lambda がリソースの再作成に失敗した場合、関数は非アクティブ状態に戻ります。関数が非アクティブ状態のままである場合、関数の StatusCode 属性と StatusCodeReason 属性を参照して、さらにトラブルシューティングを行ってください。エラーを解決し、関数を再デプロイしてアクティブ状態に復元する必要がある場合があります。


- 関数を呼び出す準備ができたことを示すため、Lambdaはすべての関数の関数設定に状態フィールドを含める
- Stateは、関数の現在の関数ステータスに関する情報（※）を提供する
    - ※関数を正常に呼び出すことができるか
- 関数の状態
    - Pending
        - Lambda は関数を作成した後、関数の状態を保留に設定する
        - 保留状態の場合、Lambda は関数のリソース (VPC や EFS リソースなど) の作成または設定を試みる
        - Lambda は、保留状態の関数を呼び出さない。関数に対する呼び出しやその他の API アクションは、すべて失敗する
    - Active
        - Lambda がリソース設定とプロビジョニングを完了した後、関数はアクティブ状態に移行する
        - 関数は、アクティブな間のみ正常に呼び出す事が可能
    - Failed
        - リソース設定またはプロビジョニングでエラーが発生したことを示す
    - Inactive
        - Lambda 用に設定された外部リソースを再利用するのに十分な時間アイドル状態を維持した関数は非アクティブになる
        - 非アクティブな関数を呼び出そうとすると、呼び出しは失敗し、関数リソースが再作成されるまで、Lambda は関数をPending状態に設定する
        - Lambda がリソースの再作成に失敗した場合、関数は非アクティブ状態に戻る


> SDK ベースのオートメーションワークフローを使用している場合、または Lambda のサービス API を直接呼び出す場合は、呼び出し前に関数の状態をチェックして、関数がアクティブであることを確認します。この操作は、Lambda API アクションの GetFunction を使用するか、AWS SDK for Java 2.0 を使用してウェーターを設定することによって実行することができます。

下記のコードにより、アクティブであるか確認できる
```bash
aws lambda get-function --function-name my-function --query 'Configuration.[State, LastUpdateStatus]'
[
 "Active",
 "Successful" 
]
```

### Lambda 実行環境のライフサイクルを理解する
下記を基に整理します。

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/e3403490-da46-e782-9644-7ce457e2f6ff.png)

出典：[Lambda 実行環境のライフサイクルを理解する](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-runtime-environment.html)


> Lambda は、実行環境で関数を呼び出します。これにより、安全で分離されたランタイム環境が提供されます。実行環境は、関数の実行に必要なリソースを管理します。また、関数のランタイム、および関数に関連付けられた外部拡張機能のライフサイクルサポートも提供します。

- Lambdaは、実行環境で関数を呼び出す
- 安全で分離されたランタイム環境が提供される
- 実行環境は関数に必要なリソースを管理する
- 関数のラインライム、関数に関連付けられた外部拡張機能のライフサイクルもサポートする

> 関数のランタイムは、ランタイム API を使用して Lambda と通信します。拡張機能は、拡張機能 API を使用して Lambda と通信します。拡張機能は、Telemetry API を使用することで、関数からログメッセージとその他のテレメトリを受け取ることもできます。

- 関数のランタイムは、ランタイムAPIを使用してLambdaと通信する
- 拡張機能は、拡張機能APIを使用してLambdaと通信する
- 拡張機能は、Telemetry APIを使用して、関数からログメッセージとその他のテレメトリを受け取ることも可能

#### Lambda実行環境のライフサイクル
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/7437949f-78c8-bd3f-2d91-5b8c1a07c139.png)

※出典：[Lambda 実行環境のライフサイクル](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-runtime-environment.html)

> 各フェーズは、ランタイムと、登録されているすべての拡張機能に Lambda を送信するイベントから始まります。ランタイムと各拡張機能は、Next API リクエストを送信することで完了を示します。Lambda は、ランタイムと各拡張機能が完了して保留中のイベントがなくなると、実行環境をフリーズします。


##### initフェーズ
> Init フェーズでは、Lambda は次の 3 つのタスクを実行します。
- すべての拡張機能を起動する (Extension init)
- ランタイムをブートストラップする (Runtime init)
- 関数の静的コード (Function init) を実行する
> 任意の beforeCheckpoint ランタイムフックを実行する (Lambda SnapStart のみ)

- initフェーズは下記の3つのタスクを実行する
    - Extension init:すべての拡張機能を起動
    - Runtime init:ランタイムをブートストラップ
    - Funcitoon init:関数の静的コードを実行


> この Init フェーズは、ランタイムとすべての拡張機能が Next API リクエストを送信して準備完了を示したときに終了します。Init フェーズは 10 秒に制限されています。3 つのタスクすべてが 10 秒以内に完了しない場合、Lambda は最初の関数呼び出し時に、設定された関数タイムアウトで Init フェーズを再試行します。


- initフェーズは、ランタイムとすべての拡張機能がNextAPIリクエストを送信して準備完了したときに終了する
- initフェーズは、10秒に制限されている
- 3つのタスクすべてが10秒以内に完了しない場合、関数タイムアウトでinitフェーズを再試行する

> Lambda SnapStart がアクティブ化されると、関数バージョンの発行時に Init フェーズが開始されます。Lambda は、初期化された実行環境のメモリとディスク状態のスナップショットを保存し、暗号化されたスナップショットを永続化して、低レイテンシーアクセスのためにスナップショットをキャッシュします。beforeCheckpoint ランタイムフックがある場合、コードは Init フェーズの最後に実行されます。

- Lambda SnapStartがアクティブ化されると、関数バージョン発行時にinitフェーズが開始される
- 初期化された実行環境のメモリとディスク状態のスナップショットを保存し、暗号化されたスナップショットを永続化し、低レイテンシーアクセスのためにスナップショットをキャッシュする


下記の記事によると、Lambda SnapStartは、Javaランタイムのみ（Java11以降）サポートしているようです。

> SnapStart は、Java 11 以降の Java マネージドランタイムをサポートします。他のマネージドランタイム (nodejs20.x や python3.12 など)、OS 専用ランタイム、およびコンテナイメージはサポートされていません。

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

##### invokeフェーズ
> Next API リクエストに応答して Lambda 関数が呼び出されると、Lambda はランタイムと各拡張機能に Invoke イベントを送信します。

- Next APIリクエストに応答して、Lambda関数が呼び出されるとLambdaはランタイムと各拡張機能にInvokeイベントを送信する

> 関数のタイムアウト設定は、Invoke フェーズ全体の所要時間を制限します。例えば、関数のタイムアウトを 360 秒に設定した場合、関数とすべての拡張機能は 360 秒以内に完了する必要があります。独立した呼び出し後フェーズはないことに注意してください。所要時間は、すべての呼び出し時間 (ランタイム + 拡張機能) の合計であり、関数とすべての拡張機能の実行が終了するまで計算されません。

- 関数のタイムアウト設定は、Invoke フェーズ全体の所要時間を制限する
    - 例）関数のタイムアウトを 360 秒に設定した場合
        - 関数とすべての拡張機能は 360 秒以内に完了する必要がある
        - 所要時間は、すべての呼び出し時間 (ランタイム + 拡張機能) の合計

> 呼び出しフェーズはランタイム後に終了し、すべての拡張機能は Next API リクエストを送信して、完了したことを示します。

- Invokeフェーズは、ランタイム後に終了し、すべての拡張機能は Next API リクエストを送信して、完了したことを示す

###### invokeフェーズの失敗
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/e1e7215c-b2a3-deec-5892-ad19978821db.png)

※出典：[呼び出しフェーズ中の失敗](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-runtime-environment.html#runtimes-lifecycle-init-errors)

> Lambda 関数が Invoke フェーズ中にクラッシュするかタイムアウトすると、Lambda は実行環境をリセットします。次の図は、呼び出しに失敗した場合の Lambda 実行環境の動作を示しています。

- クラッシュ or タイム・アウトするとLambdaは実行環境をリセットする

> ある時点で、関数で呼び出しエラー (関数のタイムアウトやランタイムエラーなど) が発生したとします。INVOKE WITH ERROR というラベルの付いた 3 番目のフェーズは、このシナリオを示しています。これが発生すると、Lambda サービスはリセットを実行します。リセットは Shutdown イベントのように動作します。まず、Lambda はランタイムをシャットダウンし、登録された各外部拡張機能に Shutdown イベントを送信します。イベントには、シャットダウンの理由が含まれます。この環境が新しい呼び出しに使用される場合、Lambda は次の呼び出しとともに拡張機能とランタイムを再び初期化します。

- Lambdaサービスはリセットを実行する
- リセットはshutdownイベントのように動作する
    - Lambdaはランタイムをシャットダウンする
    - 登録された各外部拡張機能にShutdownイベントを送信
        - イベントにはシャットダウンの理由が含まれる
    - 新しい呼び出しに使用されるときには、Lambdaは次の呼び出しとともに各拡張機能とランタイムを再び初期化する

##### Shutdownフェーズ
> Lambda は、ランタイムをシャットダウンしようとする際、Shutdown イベントを登録された各外部拡張機能に送信します。拡張機能は、この時間を最終的なクリーンアップタスクに使用できます。Shutdown イベントは、Next API リクエストに対するレスポンスです。

- Lambdaはランタイムをシャットダウンする際に、シャットダウンイベントを登録された各外部拡張機能に送信する
    - 拡張機能は、この時間を最終的なクリーンアップタスクに使用できる
    - Shutdown イベントは、Next API リクエストに対するレスポンス

> 期間: Shutdown フェーズ全体の上限は 2 秒です。ランタイムまたは拡張機能が応答しない場合、Lambda は通知 (SIGKILL) によりそれを終了します。

- 期間
    - Shutdownフェーズ全体の上限は2秒
    - ランタイムまたは拡張機能が応答しない場合、Lambda は通知 (SIGKILL) により終了する

> 関数とすべての拡張機能が完了した後、Lambda は別の関数の呼び出しを想定して、実行環境をしばらく維持します。ただし、Lambda は数時間ごとに実行環境を終了し、ランタイムの更新とメンテナンスを許可します。継続的に呼び出される関数であっても、この終了は発生します。実行環境が無期限に保持されると想定すべきではありません。詳細については、「関数にステートレスを実装する」を参照してください。


- 関数とすべての拡張機能が完了した後、Lambda は別の関数の呼び出しを想定して、実行環境をしばらく維持する
- ただし、Lambda は数時間ごとに実行環境を終了し、ランタイムの更新とメンテナンスを許可する
- 継続的に呼び出される関数であっても、この終了は発生する
    - 実行環境が無期限に保持されると想定すべきできない


> 関数が再び呼び出されると、再利用のため、Lambda によって環境が解凍されます。実行環境の再利用には、次のような意味があります。

> - 関数ハンドラーメソッドの外部で宣言されたオブジェクトは、初期化されたままとなり、関数が再度呼び出されると追加の最適化を提供します。例えば、Lambda 関数がデータベース接続を確立する場合、連続した呼び出しでは接続を再確立する代わりに元の接続が使用されます。新しい接続を作成する前に、接続が存在するかどうかを確認するロジックをコードに追加することをお勧めします。

> - 各実行環境は、/tmp ディレクトリに 512 MB と 10,240 MB 間を 1 MB 刻みで提供します。ディレクトリのコンテンツは、実行環境が停止された際に維持され、複数の呼び出しに使用できる一時的なキャッシュを提供します。キャッシュに保存したデータが存在するかどうかを確認するための追加コードを追加できます。デプロイのサイズ制限の詳細については、「Lambda クォータ」を参照してください。

> - Lambda 関数によって開始され、関数が終了したときに完了しなかったバックグラウンドプロセスまたはコールバックは、Lambda 関数が実行環境を再利用したときに再開します。コードのバックグラウンド処理またはコールバックは、コード終了までに完了させてください。

- 関数が再び呼び出されると、再利用のため、Lambda によって環境が解凍される
- 実行環境の再利用には下記のような意味がある
    - 関数ハンドラーメソッドの外部で宣言されたオブジェクト
        - 初期化されたままとなり、関数が再度呼び出されると追加の最適化を提供する
        - 例えば、Lambda 関数がデータベース接続を確立する場合
            - 連続した呼び出しでは接続を再確立する代わりに元の接続が使用される。新しい接続を作成する前に、接続が存在するかどうかを確認するロジックをコードに追加することを推奨する
    - 各実行環境は、/tmp ディレクトリに 512 MB と 10,240 MB 間を 1 MB 刻みで提供する
        - ディレクトリのコンテンツは、実行環境が停止された際に維持され、複数の呼び出しに使用できる一時的なキャッシュを提供する
        - キャッシュに保存したデータが存在するかどうかを確認するための追加コードを書くこと
    - Lambda 関数によって開始され、関数が終了したときに完了しなかったバックグラウンドプロセスまたはコールバックは、Lambda 関数が実行環境を再利用したときに再開する
        - コードのバックグラウンド処理またはコールバックは、コード終了までに完了させる


# 実践
## Python の AWS ベースイメージを使用してデプロイする
下記を基に試します。

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/python-image.html#python-image-base

https://aws.amazon.com/jp/blogs/compute/introducing-the-amazon-linux-2023-runtime-for-aws-lambda/

https://gallery.ecr.aws/lambda/python

### 前提
下記がインストールされていること
- docker
- AWS CLI

### ローカルで開発とテスト
1. app.pyを作成します
    ```python
    def handler(event, context):
    
        message = "Hello, World!"
    
        print(message)
    
        return message
    ```

1. Dockerファイルを作成します
    ```dockerfile
    FROM public.ecr.aws/lambda/python:3.12
    
    # Copy function code
    COPY app.py ${LAMBDA_TASK_ROOT}
    
    # Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile)
    CMD [ "app.handler" ]
    ```

1. ビルドします
    ```bash
    docker build -t docker-lambda-python-image .
    $ docker image ls                             
    REPOSITORY                       TAG       IMAGE ID       CREATED              SIZE
    docker-lambda-python-image       latest    XXXX   About a minute ago   496MB
    ```

1. 実行します
    ```bash
    $ docker run -p 9000:8080 docker-lambda-python-image
    15 Sep 2024 07:09:52,094 [INFO] (rapid) exec '/var/runtime/bootstrap' (cwd=/var/task, handler=)
    ```

1. 別のターミナルで呼び出します
    ```bash
    $ curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"payload":"test"}'        
    "Hello, World!"
    $
    ```

### Lambdaにデプロイ
#### ECRの作成
下記を基に試します。

https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/repository-create.html

1. AWSにサインインし、ECSに移動します
1. ナビゲーションペインで、ECRをクリックします
1. ナビゲーションペインで、「Private Repositry>Repositories」をクリックします
1. 「リポジトリを作成」をクリックします
1. 下記を入力し、「作成」をクリックします
    - リポジトリ名：任意
    - ミュータビリティ：Immutable
    - 暗号化設定：AES-256
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/1d6c762e-7175-62e7-35f5-2e938738bfe1.png)

#### ECRプライベートリポジトリにプッシュ
下記を基に試します。

https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/docker-push-ecr-image.html

1. ECRレジストリに対して、Dockerクライアントを認証します
    ```bash
    $ aws ecr get-login-password --profile ${PROFILE} | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com
    Login Succeeded
    $
    ```
1. タグを付けます
    ```bash
    $ docker images
    REPOSITORY                       TAG       IMAGE ID       CREATED          SIZE
    docker-lambda-python-image       latest    XXXXX   25 minutes ago   496MB
    $
    $ docker tag ${TAG} ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/${REPOSITRY}:${TAG}
    $ 
    $ docker images
    REPOSITORY                                                                          TAG            IMAGE ID       CREATED          SIZE
    XXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/YYYY/ZZZZZ   XXXXXX   XXXXXX   30 minutes ago   496MB
    docker-lambda-python-image                                                          latest         XXXXXXX   30 minutes ago   496MB
    ```

1. イメージをプッシュします
    ```bash
    $ docker push ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/test/docker-lambda-python-image:1b785655d69b
    The push refers to repository [XXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/YYYY/ZZZZZ]
    $
    ```
1. リポジトリにPushされました
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/5a667244-b3c4-39c9-8c4c-968596ad8533.png)

#### Lambda関数の作成

下記を基に試します。

https://aws.amazon.com/jp/blogs/compute/introducing-the-amazon-linux-2023-runtime-for-aws-lambda/

1. Lambdaに移動します
1. 「関数の作成」をクリックします
1. 下記を指定して、⑤「関数の作成」をクリックします
    - ①：コンテナイメージを選択
    - ②：関数名：任意
    - ③：コンテナイメージURI：作成したリポジトリを指定
    - ④：アーキテクチャ：arm64 or x86_64
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/5579778a-d41c-b946-a43a-8660a4707732.png)

#### テスト
1. 「テスト」タブをクリックします
1. 下記を入力して、③「テスト」をクリックします
    - ①:イベント名
    - ②:イベントJSON
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/8dcf49b9-6f23-9616-fa96-98d39a3fd12d.png)

1. 成功しました
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/bae54f73-e7cf-368a-1508-6dea9f3dc07c.png)

# 考察
今回、コンテナイメージを作成してLambdaから実行してみました。コンテナイメージを使用すると下記のメリットがあるので、今後も利用していこうと思います。

1. 依存関係の柔軟な管理
    - コンテナイメージを使用することで、アプリケーションの実行環境や依存関係を完全にカスタマイズできる
    - 特定のバージョンのライブラリやパッケージが必要な場合、Lambdaのデフォルト環境ではサポートされていない依存関係を含めることができる

1. イメージサイズの拡張（最大10GB）
    - Lambdaの通常のデプロイ（ZIPファイルやJARファイル）では、アーティファクトのサイズに制限があるが、コンテナイメージを使用する場合、最大10GBまでのイメージをデプロイできる

1. 開発・テストの一貫性
    - ローカル環境でDockerを使用してテストし、同じコンテナをLambdaで実行することで、動作の一貫性を保つことができ、ローカル環境と本番環境での不整合を避けられる


# 参考

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

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

https://github.com/aws/aws-lambda-base-images/blob/provided.al2023/Dockerfile.provided.al2023

https://github.com/aws/aws-lambda-base-images/blob/provided.al2/Dockerfile.provided.al2

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

https://github.com/aws/aws-lambda-runtime-interface-emulator/

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

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

https://aws.amazon.com/jp/blogs/compute/introducing-the-amazon-linux-2023-runtime-for-aws-lambda/