この記事は、AWS re:Invent 2024のセッション「SVS314 | Managing AWS Lambda runtimes at scale」のレポートです。
このセッションは、Lambdaのランタイム管理の仕様やベストプラクティスをテーマに、AWS社の講師の方が解説するセッションでした。
Lambdaは私自身も好きなメジャーなサービスですし、セッション形式が参加者側も発言する機会のあるチョークトーク形式のセッションだったためオンデマンド配信がなく、現地で聴く価値があると思って参加してきました。
資料はこちらで公開されています。
Lambdaのランタイムとは
ユーザーの開発したコードをLambdaで稼働させる際には、以下のようなコンポーネントがランタイムとして提供されるとのことです。
- Runtime Interface Clients :図のIntegration(RIC)に対応。私はこのセッションで初めて知ったのですが、ユーザーコードとLambdaのサービスを仲介するのに使われるツールだそうです
- AWS SDK :図のSDKに対応
- 言語ランタイム:図のLanguageに対応
- システムライブラリ:図のSystem libsに対応
さらに、これらの基盤として Amazon Linux OS もランタイムとして提供されるそうです。図のMicro VMの部分に対応するという理解です。
ランタイムをアップデートすると、これら全てがアップデートされる可能性があるそうです。
ランタイムのバージョン表記
例えばNode.jsだと、Node.js 18.v1 のような表記となります。
18 の部分は、正式な呼び方が見つからなかったのでここでは便宜的に メジャーバージョン とします。
v1 の部分は パッチバージョン と呼ぶようです。
ランタイム管理のコンセプト
パッチバージョンとメジャーバージョンに分けて紹介します。
パッチバージョンについて
新しいパッチバージョンには下位互換性のあるセキュリティ更新などが含まれます。
以前はAWSにて自動更新されていたのが2023年1月にユーザーにて制御する機能 ランタイム管理設定 が追加されました。(AWS Compute Blog - Introducing AWS Lambda runtime management controls)
デフォルトだとAWSによって自動更新される動きとなります。
ランタイム管理設定の詳細はクラスメソッドさんの「[アップデート] AWS Lambdaで新しいランタイム管理設定」が追加されました」という記事にまとまっています。
メジャーバージョンについて
一方、新しいメジャーバージョンには、ユーザーコードの非互換を含むようなランタイムの変更が入ります。
パッチバージョンのような自動更新の機能はなく、バージョンアップはユーザーがドライブしていく必要があります。
このあと、それぞれの管理にまつわるベストプラクティスが紹介されました。
パッチバージョンの管理
パッチバージョン更新時に起きうる問題
新しいパッチバージョンは基本的には非互換を含まないと言われているのですが、パッチバージョン更新時にLambda関数で問題が起きることがあるそうです。
以下が一例として紹介されていました。
Lambda関数をPythonで開発してzip形式でデプロイしています。
ユーザーコード側では
- REQUESTSというライブラリを使うロジックがあり、REQUESTSはURLLIB3という別のライブラリへの依存関係を持っています。
- さらにランタイムに含まれるAWS SDKを呼び出すロジックも含んでいます。
実はAWS SDK側でもURLLIB3への依存関係を持っています。
この時点ではユーザーコードとAWS SDKは同じバージョンのURLLIB3に対して依存関係を持っていて、関数の実行で問題は起きていません。
そこで、パッチバージョンが更新されます。
すると、ランタイム側のAWS SDKは新しいバージョンのURLLIB3を前提とした内容に更新されました。
ユーザーコード側には変更がないので、前提とするURLLIB3のバージョンは元のままです。
ここでビルドの動きに着目すると、ランタイムとユーザーコードではユーザーコード側が先にビルドされるので、URLLIB3はユーザーコード側のURLLIB3が使われる動きとなります。
この状態でLambda関数が実行されると、ユーザーコードからランタイム側のAWS SDKを呼び出すロジックでは、新しいパッチバージョンのAWS SDKを呼び出します。
そしてAWS SDKが依存するURLLIB3を呼び出そうとする際には、ユーザーコード側の古いバージョンのURLLIB3を呼び出してしまい、エラーが起きてしまいます。
問題を防ぐには
デフォルト通りパッチバージョンの自動更新をオンにした状態で、以上のようなランタイム側への依存関係に起因する問題を避けるためには、ユーザーコード側にAWS SDKや依存関係を含めてしまうことが推奨だそうです。
問題が起きた時の調査・対応方法
Lambdaで実際にエラーが起きて、パッチバージョンの更新が関係しているかを調べる際にはLambda実行時にCloudWatch Logsに出力されるINIT_STARTのログメッセージでパッチバージョンを確認できます。
もしパッチバージョンの更新が原因と思われる場合には、先ほどのランタイム管理設定で手動更新をオンにすることでパッチバージョンをロールバックすることを考えましょう。
ロールバック先を指定する際にはログに出力されるランタイムバージョンARNを使います。
メジャーバージョンの管理
EOLの流れ
メジャーバージョンはEOL(End Of-Life)に合わせてバージョンアップする運用をとっているユーザーが多いと思います。
あるメジャーバージョンのEOLが決まると、AWSから6ヶ月以上前に非推奨をメール、Health、Trusted Advisor等で通知するそうです。
時期が来ると、関数の実行は続けられますが、ユーザーコードの更新ができなくなるとのことです。
Lambda関数を大量に使っているユーザーは、非互換対応が大変になるということで、計画的・効率的に対応するためのベストプラクティスが紹介されていました。
EOLのバージョンで稼働する関数の特定と優先順位付け
バージョン更新が必要なLambda関数を確認する方法は、LambdaやConfigのコンソール画面で確認するなど手段はいろいろありますが、講師の方がおすすめしていたのはTrusted Advisorを使うやり方でした。
Trusted Advisorの画面では非推奨のランタイムを使うLambda関数一覧と、1日あたりの実行頻度が確認できるので、対象の洗い出しと優先順位付けに便利とのことでした。
同じ情報はTrusted AdvisorのOrganizational Viewでも確認可能とのことで、Organizations単位で効率的に確認することができます。
稼働バージョンの統制
稼働バージョンにガバナンスを効かせる方法として、2つのアプローチが紹介されていました。
CloudFormation Guardを用いてLambdaの稼働バージョンに統制を効かせる方法は「AWSドキュメント- AWS CloudFormation Guard による Lambda のプロアクティブコントロール」 にて紹介されています。
バージョン更新のための修正
修正の進め方については、以下を取り入れることが推奨とのことでした。
- All at onceでなくIncrementalに対応
- テストの自動化
- コード生成サービスの利用
所感
セッション内容について
パッチバージョン更新時に起きうる問題や回避策はとても重要ですが、私自身も知らなかった内容ですし、まだ周知されていないように感じるので積極的に周知したいと思いました。
この記事もその一環で執筆しました。
また、Lambda関数が稼働する際のメジャーバージョンの統制について、今まで参画するプロジェクトで検討したことがなかったのですが、今後特にLambda関数の数が多いシステムを扱う際には検討したいと思いました。
インタラクティブなセッション形式について
インタラクティブなセッション形式も楽しみに参加しましたが、総じて講師の方が上手くファシリテートされているなという印象でした。
まず、セッションの冒頭で答えやすい質問を設定して意思表示しやすい雰囲気を醸成していました。
「自分こそは一番遠方から来たと思う人は?」という簡単な質問に挙手させて、手を挙げた参加者にそれぞれにどこから来たか答えさせていました。
私も手を挙げていたので「日本」と答えました。
次に「Lambda関数を社内でいくつ使ってる?」「100以上?」「1000以上?」という感じで挙手形式で利用状況をヒアリングしていました。
その後、セッションの本題に入ってからは「Lambdaのランタイムって一言で言うと何だと思う?」「パッチバージョン更新時に起きうる問題をどう回避すればいいと思う?」といった難しい質問の投げかけがありましたが、きっと序盤の簡単な質問の効果もあってたくさんの参加者が発言してました。
一方で、参加者への質問やキーメッセージを予め決めておくことで上手く進行をコントロールしていました。
ディスカッションが発散しすぎることもなく、時間通りに要点を押さえて内容を紹介してもらえたと思います。
内容だけでなく進行についてもとても満足度の高いセッションでした。