Lambdaのランタイム管理のベストプラクティス #AWS re:Invent 2024

この記事は、<font color="#0000CS">**AWS re:Invent 2024**</font>のセッション「SVS314 | Managing AWS Lambda runtimes at scale」のレポートです。
このセッションは、<font color="#0000CS">**Lambdaのランタイム管理の仕様やベストプラクティスをテーマに、AWS社の講師の方が解説**</font>するセッションでした。

Lambdaは私自身も好きなメジャーなサービスですし、セッション形式が参加者側も発言する機会のあるチョークトーク形式のセッションだったためオンデマンド配信がなく、現地で聴く価値があると思って参加してきました。

資料は[こちら](https://reinvent.awsevents.com/content/dam/reinvent/2024/slides/svs/SVS314_Managing-AWS-Lambda-runtimes-at-scale.pdf)で公開されています。

### Lambdaのランタイムとは

ユーザーの開発したコードをLambdaで稼働させる際には、以下のようなコンポーネントがランタイムとして提供されるとのことです。

![スクリーンショット 2025-01-25 10.09.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271327/fdafb2a7-5c39-39fe-2339-c07e17c72f3d.png)

- <font color="#0000CS">**Runtime Interface Clients**</font> ：図のIntegration（RIC）に対応。私はこのセッションで初めて知ったのですが、ユーザーコードとLambdaのサービスを仲介するのに使われるツールだそうです
- <font color="#0000CS">**AWS SDK**</font> ：図のSDKに対応
- <font color="#0000CS">**言語ランタイム**</font>：図のLanguageに対応
- <font color="#0000CS">**システムライブラリ**</font>：図のSystem libsに対応

さらに、これらの基盤として <font color="#0000CS">**Amazon Linux OS**</font> もランタイムとして提供されるそうです。図のMicro VMの部分に対応するという理解です。

<font color="#0000CS">**ランタイムをアップデートすると、これら全てがアップデートされる可能性**</font>があるそうです。

### ランタイムのバージョン表記

例えばNode.jsだと、<font color="#0000CS">**Node.js 18.v1**</font> のような表記となります。
<font color="#0000CS">**18**</font> の部分は、正式な呼び方が見つからなかったのでここでは便宜的に <font color="#0000CS">**メジャーバージョン**</font> とします。
<font color="#0000CS">**v1**</font> の部分は <font color="#0000CS">**パッチバージョン**</font> と呼ぶようです。

### ランタイム管理のコンセプト

パッチバージョンとメジャーバージョンに分けて紹介します。

#### パッチバージョンについて

![スクリーンショット 2025-01-25 10.32.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271327/8ea2778e-c019-78ae-da40-db6cd62044f9.png)
新しいパッチバージョンには<font color="#0000CS">**下位互換性のあるセキュリティ更新**</font>などが含まれます。

以前はAWSにて自動更新されていたのが2023年1月にユーザーにて制御する機能 <font color="#0000CS">**ランタイム管理設定**</font> が追加されました。（[AWS Compute Blog - Introducing AWS Lambda runtime management controls](https://aws.amazon.com/jp/blogs/compute/introducing-aws-lambda-runtime-management-controls/)）
![スクリーンショット 2025-01-28 17.37.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271327/a7efaf38-6b23-5cb1-1123-aa47bbd10e34.png)

デフォルトだと<font color="#0000CS">**AWSによって自動更新**</font>される動きとなります。

ランタイム管理設定の詳細はクラスメソッドさんの[「[アップデート] AWS Lambdaで新しいランタイム管理設定」が追加されました」](https://dev.classmethod.jp/articles/aws-lambda-supports-runtime-management-controls/)という記事にまとまっています。



#### メジャーバージョンについて
![スクリーンショット 2025-01-25 10.22.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271327/5f8d17c7-2011-c225-0e42-857b711f41b8.png)
一方、新しいメジャーバージョンには、<font color="#0000CS">**ユーザーコードの非互換を含む**</font>ようなランタイムの変更が入ります。
パッチバージョンのような自動更新の機能はなく、バージョンアップは<font color="#0000CS">**ユーザーがドライブ**</font>していく必要があります。

このあと、それぞれの管理にまつわるベストプラクティスが紹介されました。

### パッチバージョンの管理

#### パッチバージョン更新時に起きうる問題
新しいパッチバージョンは基本的には非互換を含まないと言われているのですが、パッチバージョン更新時にLambda関数で問題が起きることがあるそうです。

以下が一例として紹介されていました。

![スクリーンショット 2025-01-28 18.15.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271327/9af7c826-c0eb-d7d1-c210-f89efc11e08d.png)


Lambda関数をPythonで開発してzip形式でデプロイしています。
ユーザーコード側では
- REQUESTSというライブラリを使うロジックがあり、REQUESTSはURLLIB3という別のライブラリへの依存関係を持っています。
- さらにランタイムに含まれるAWS SDKを呼び出すロジックも含んでいます。

実はAWS SDK側でもURLLIB3への依存関係を持っています。

この時点ではユーザーコードとAWS SDKは同じバージョンのURLLIB3に対して依存関係を持っていて、関数の実行で問題は起きていません。


そこで、パッチバージョンが更新されます。
すると、ランタイム側のAWS SDKは新しいバージョンのURLLIB3を前提とした内容に更新されました。
ユーザーコード側には変更がないので、前提とするURLLIB3のバージョンは元のままです。

![スクリーンショット 2025-01-28 18.15.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271327/33bcf25f-f994-6801-7d8d-d2698900cdee.png)

ここでビルドの動きに着目すると、ランタイムとユーザーコードではユーザーコード側が先にビルドされるので、URLLIB3はユーザーコード側のURLLIB3が使われる動きとなります。

![スクリーンショット 2025-01-28 18.15.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271327/84ebcf0d-92a7-e80f-51e0-33f9b512af11.png)

この状態でLambda関数が実行されると、ユーザーコードからランタイム側のAWS SDKを呼び出すロジックでは、新しいパッチバージョンのAWS SDKを呼び出します。

![スクリーンショット 2025-01-29 12.31.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271327/c1db9068-2d2a-fc6f-53bb-94b51a36a039.png)


そしてAWS SDKが依存するURLLIB3を呼び出そうとする際には、ユーザーコード側の古いバージョンのURLLIB3を呼び出してしまい、エラーが起きてしまいます。

![スクリーンショット 2025-01-28 18.16.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271327/e75128e8-909d-d726-6176-9d2e47bd81b0.png)

#### 問題を防ぐには

デフォルト通りパッチバージョンの自動更新をオンにした状態で、以上のようなランタイム側への依存関係に起因する問題を避けるためには、<font color="#0000CS">**ユーザーコード側にAWS SDKや依存関係を含めてしまう**</font>ことが推奨だそうです。

#### 問題が起きた時の調査・対応方法

Lambdaで実際にエラーが起きて、パッチバージョンの更新が関係しているかを調べる際にはLambda実行時にCloudWatch Logsに出力される<font color="#0000CS">**INIT_STARTのログメッセージ**</font>でパッチバージョンを確認できます。

もしパッチバージョンの更新が原因と思われる場合には、先ほどのランタイム管理設定で手動更新をオンにすることで<font color="#0000CS">**パッチバージョンをロールバック**</font>することを考えましょう。
ロールバック先を指定する際にはログに出力される<font color="#0000CS">**ランタイムバージョンARN**</font>を使います。

![スクリーンショット 2025-01-28 18.40.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271327/83da22ce-3cc2-7233-24b7-7e755fa2a4da.png)


### メジャーバージョンの管理

#### EOLの流れ

メジャーバージョンはEOL（End Of-Life）に合わせてバージョンアップする運用をとっているユーザーが多いと思います。

あるメジャーバージョンのEOLが決まると、AWSから<font color="#0000CS">**6ヶ月以上前**</font>に非推奨をメール、Health、Trusted Advisor等で<font color="#0000CS">**通知**</font>するそうです。
時期が来ると、関数の実行は続けられますが、<font color="#0000CS">**ユーザーコードの更新ができなくなる**</font>とのことです。

Lambda関数を大量に使っているユーザーは、非互換対応が大変になるということで、計画的・効率的に対応するためのベストプラクティスが紹介されていました。

#### EOLのバージョンで稼働する関数の特定と優先順位付け

バージョン更新が必要なLambda関数を確認する方法は、LambdaやConfigのコンソール画面で確認するなど手段はいろいろありますが、講師の方がおすすめしていたのは<font color="#0000CS">**Trusted Advisor**</font>を使うやり方でした。
Trusted Advisorの画面では<font color="#0000CS">**非推奨のランタイムを使うLambda関数一覧**</font>と、<font color="#0000CS">**1日あたりの実行頻度**</font>が確認できるので、対象の洗い出しと優先順位付けに便利とのことでした。

![スクリーンショット 2025-01-28 19.09.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271327/a47d6df7-ef5c-cf96-5963-7ae45cf5582f.png)

同じ情報はTrusted AdvisorのOrganizational Viewでも確認可能とのことで、Organizations単位で効率的に確認することができます。


#### 稼働バージョンの統制

稼働バージョンにガバナンスを効かせる方法として、2つのアプローチが紹介されていました。
- 静的コード解析やCICD等を用いた<font color="#0000CS">**想定しないメジャーバージョンで稼働するLambda関数の検知**</font>
- 以下のようなサービス・ツールを用いた<font color="#0000CS">**稼働バージョンのプロアクティブな統制**</font>
![図1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271327/001cfd69-91de-ceb6-5b09-d1ba5cf8b1a1.jpeg)

CloudFormation Guardを用いてLambdaの稼働バージョンに統制を効かせる方法は「[AWSドキュメント- AWS CloudFormation Guard による Lambda のプロアクティブコントロール](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/governance-cloudformation-guard.html)」 にて紹介されています。

#### バージョン更新のための修正　

修正の進め方については、以下を取り入れることが推奨とのことでした。
- All at onceでなくIncrementalに対応
- テストの自動化
- コード生成サービスの利用

### 所感

#### セッション内容について
パッチバージョン更新時に起きうる問題や回避策はとても重要ですが、私自身も知らなかった内容ですし、まだ周知されていないように感じるので積極的に周知したいと思いました。
この記事もその一環で執筆しました。

また、Lambda関数が稼働する際のメジャーバージョンの統制について、今まで参画するプロジェクトで検討したことがなかったのですが、今後特にLambda関数の数が多いシステムを扱う際には検討したいと思いました。

#### インタラクティブなセッション形式について

インタラクティブなセッション形式も楽しみに参加しましたが、総じて講師の方が上手くファシリテートされているなという印象でした。

まず、セッションの冒頭で答えやすい質問を設定して意思表示しやすい雰囲気を醸成していました。
「自分こそは一番遠方から来たと思う人は？」という簡単な質問に挙手させて、手を挙げた参加者にそれぞれにどこから来たか答えさせていました。
私も手を挙げていたので「日本」と答えました。
次に「Lambda関数を社内でいくつ使ってる？」「100以上？」「1000以上？」という感じで挙手形式で利用状況をヒアリングしていました。
その後、セッションの本題に入ってからは「Lambdaのランタイムって一言で言うと何だと思う？」「パッチバージョン更新時に起きうる問題をどう回避すればいいと思う？」といった難しい質問の投げかけがありましたが、きっと序盤の簡単な質問の効果もあってたくさんの参加者が発言してました。

一方で、参加者への質問やキーメッセージを予め決めておくことで上手く進行をコントロールしていました。
ディスカッションが発散しすぎることもなく、時間通りに要点を押さえて内容を紹介してもらえたと思います。

内容だけでなく進行についてもとても満足度の高いセッションでした。