キーポイント
- AWS Lambda is a key ingredient of many cloud-native applications and use-cases
- The nature of AWS Lambda requires special care for observability
- Distributed tracing is all but necessary to succeed in running complex, Lambda-based applications
- The distributed tracing needs of Lambda emphasize the need to comprehensive, drop-in, low-maintenance distributed tracing
AWS Lambdaは、おそらく過去数年間のソフトウェア開発におけるクラウドネイティブシフトを定義するテクノロジーの1つです。公式サイトによると:
“AWS Lambda を使用することで、サーバーのプロビジョニングや管理をすることなく、コードを実行できます。料金は、コンピューティングに使用した時間に対してのみ発生します。”
AWS Lambdaは、Node.jsのサポートが2014年に始まり、Go、Java、Python、Ruby、C#などのさまざまなプログラミング言語でのFunctionの開発とデプロイをサポートするようになりました。
AWS Lambdaの立ち上げにより、サーバレスコンピューティング (Function-as-a-Serviceコンピューティング) がメインストリームのクラウドネイティブパラダイムとして紹介されました。以降、Google Cloud FunctionsやMicrosoft Azure Functionsなどの他の主要なクラウドプラットフォームや、KnativeやApache OpenWhiskなどのオープンソースプロジェクトによって同様のサービスが提供されています。
立ち上げから半年以上経った今でも、AWS Lambdaは間違いなく最も知られ採用されているサーバレスプラットフォームであり、その採用に関する正確な数値が不足しているにもかかわらず、「アーリーアダプタ(Early Adopters)」フェーズを超えて成熟していると言っても過言ではありません。
それにもかかわらず、AWS Lambdaは依然として、クラウドネイティブの他のトピックの1つ(可観測性)の定義といささか対立しています。LambdaはメトリックスとログのCloudWatch、分散トレースのX-Rayと統合されていますが、プロダクション環境の関数の何が問題であるかを理解することは依然として大きな課題です。
分散トレースに重点を置いて、この記事では、AWS Lambdaが今日のコンピューティングランドスケープで活用されているユースケースに基づいて、可観測性を取得してAWS Lambda関数に活用するためのベストプラクティスについて説明します。
Lambdaはなぜこれほど成長しているのでしょうか?
AWS Lambdaを使用すると、ユーザは、たとえばHTTPリクエストを処理するために同期的に、または他のAWSサービスによって生成されたイベントに反応するために非同期的に実行される関数を定義します。AWS Lambda関数をトリガできるイベントのリストは広範であり、成長し続けています。最も採用されているイベントタイプには次のものがあります:
- CloudWatch events、AWSリソースの変更を説明する単純なルールを使用して定義できます
- S3 events、S3バケットでオブジェクトが作成または削除されたときに発行されます
- SQS events、SQSでキューに入れられたメッセージを処理のためにLambda関数に渡します
しかし、AWS Lambdaができることより先にある、利用者がもはや扱う必要がない次の明確な特徴があります:
- インフラストラクチャの管理が不要: AWS Lambdaは、関数を実行するために割り当てられたインフラストラクチャの管理を自動化し、コンピューティングリソースを拡大および縮小します。月曜日の朝にアプリケーションが提供する必要のある負荷が増大し、人々がデスクに戻ると、AWS Lambdaはバックグラウンドで自動的に関数のインスタンスの量を増やします。また、負荷が下がると、稼働日の終わりに、十分に活用されていないインスタンスが自動的に削除されます。そして、AWS Lambdaの約束は、これらのどれも実際には開発者としてあなたが関与するべきものではないということです。
- 固定費が不要: ユーザは、割り当てられたCPU時間とメモリの量の観点から関数が使用されたワークロードに対してのみ支払い、ワークロードがない場合は費用がかかりません。
AWS Lambdaは上記の期待どおりでしょうか? ほとんどの場合、AWS Lambdaにコードを設定し、必要なときにのみ実行できるようにし、関数がワークロードを処理する時間だけを支払うことができます (ただし、最も近い100ミリ秒に切り上げられます)。ただし、これはパフォーマンスの面で予測できない可能性があります。AWSLambdaがインスタンスを起動して負荷を処理する場合、ランタイムを初期化する必要があるため、そのインスタンスを介して送信される最初のリクエストのレイテンシはかなり長くなります。この現象はコールドスタートと呼ばれ、機能を「ウォーム」に保つための創造的な解決策を導入してきました、最終的にAmazonが有償で一定の量のインスタンスをウォームに保つことを可能にしました。これは、間違いなく固定費であり、開発者がインフラストラクチャに関与していることの両方としてカウントされますが、Lambdaワークロードがレイテンシのスパイクに非常に敏感な場合に役立ちます。
Lambdaユースケースのトップ
すべての広い用途を持つコンピューティングプラットフォームと同様に、AWS Lambdaを使用してさまざまなことを実行できます。実際に、最も頻繁に使用されるケースは次のとおりです:
- プロトタイピングと初期段階の開発: 事前のインフラストラクチャコストがないため、AWS Lambdaは、特に新規製品と機能のための非常に魅力的なプロトタイピングプラットフォームを作り出します。特に、仮想マシンや永続的なコンテナのデプロイメントを維持するためにスタッフとお金を費やしたくない、または費やしたくない小規模な企業向けです。次に、製品の成熟度が高まり、そのワークロードが予測可能になるにつれて、次の理由により、AWS LambdaからEC2やFargateなどのより自己管理型のコンピューティングプラットフォームに移行する傾向があります:
- コスト: Lambdaの「ゼロへのスケール」がワークロードに不要であり、コンテナーまたは仮想マシンを自分でスケールアップおよびスケールダウンすることに意欲がある場合、規模が大きくなると、その柔軟性とオンデマンドの性質のためにAWS Lambdaに支払う特定のコスト増が著しくなります。
- 複雑さ: 大規模なコードベースをAWS Lambdaにデプロイすることを実際に妨げるものは何もありませんが (デプロイメントパッケージに250MB使用可能であり、これは多くのコードに相当します)、特にプロダクションの監視とデバッグが困難であるため、関数は比較的小さく簡単にすべきです。
- ビジネスプロセスとシステム統合: 多くのAWSサービスのイベントトリガの存在により、Lambda関数はシステム間の (ビジネス) プロセスの統合の自然な候補になります。たとえば、Instanaでは、インフラストラクチャの自動プロビジョニングなどの品質保証タスクから最新ビルドのテスト、サポートポータルのプロジェクト管理システムへの統合、顧客が開設したサポートチケットに対応するエンジニアの作業項目の自動作成まで、さまざまな種類の自動化にLambda関数を使用しています。
特にAWS Sagemakerと組み合わせて、機械学習のユースケースにAWS Lambdaを使用することに関心が高まっているようです。
Lambdaがビジネスプロセスとシステム統合のための優れたツールであるという事実の興味深い帰結として、(ほとんどの) Lambda関数は孤立したものではありません: Lambda関数は、多くの場合、他のLambda関数やLambdaで実行していないシステムを呼び出します。Lambda関数から呼び出されるこれらのシステムは、AWSまたはEC2、ECS、Fagateなどの他のAWSコンピューティングプラットフォームにデプロイされた他の顧客システム、またはオンプレミスで管理されるサービスのいずれかです。 これの関連性については、分散トレースAWS Lambda関数の要件に関して後で説明します。
Lambdaのラフエッジ
すべての計算パラダイムにはトレードオフがあり、Lambdaも例外ではありません。
- デバッグが難しい: 「サーバレス」であるということは、とりわけ、問題が不可避に発生した場合に、デバッグするための運用インフラストラクチャに基本的にアクセスできないことを意味します (もちろん「サーバ」は存在しますが、それを制御することはできないため、「サーバレス」のように見えます。)。確かに、AWSはLambdaコードをローカルで実行する方法を提供します。Serverless Frameworkにもローカルテストがあります。また、リモートデバッガーをLambda関数 (.NETやPythonなど) に接続する際の興味深い概念実証もあります。ただし、実際には、プロダクション環境で問題が発生すると、デバッグしなければならないすぐに使用できる機能が非常に制限され、「Cloud Printf」 (つまり、 CloudWatchへのログ記録を増やし、新しいLambdaバージョンを押し出し、指をクロスさせます) のゲームになる傾向があります。これは、障害を修正するためにスクランブルをかける場合にプレイするのは楽しいゲームではありません。さらに悪いことに、1つのAWS Lambda呼び出しのコストは (特に) 関数の実行時間に依存するため、AWS Lambda関数が予期せぬ大きなデータベース結果セットの処理のようにスタックするバグは、デバッグが難しく、クラウド予算の割にコストもかかります。それは私たちを導く...
- 「ステートレス」とは、どこかからステートをプルすることを意味します: ランタイムに関する限り、Lambdaは関数をステートレスにする必要があります。前のリクエストの処理からの状態を保持するために、1つのLambda関数に依存することはできません。ただし、処理するために状態を必要としないビジネスロジックを持つことは非常にまれです。したがって、ほとんどのLambda関数は他のサービスからいくつかの状態情報をロードする必要があり、予測できない実行時間になる可能性があり、多くの場合、Lambda関数もいくつかの状態変更を保存する必要があります。公平に言うと、AWSインフラストラクチャ内の入出力の問題はまれに見えますが、「RDSデータベースの半分をプルする」などのプログラミングの見落としははるかに少ないです。
- 分散された複雑さ: 多くの場合、複雑なシナリオには、イベントを介して相互に疎結合された大量のLambda関数が含まれます。著者が「AWSの典型的な100%サーバレスアーキテクチャ」と表現しているものを見てください。これは、追跡する必要のある可動部分の多くです。多くのLambda関数が非同期で動作していると考えると、どのリクエストの処理にどの関数が関与しているか、何が問題だったかを知ることは、100万ピースのジグソーパズルを解こうとするようなものです。
多くのAWS Lambdaアーキテクチャに固有の分散された複雑さ、およびAWSで「プロダクション」で実行されるAWS Lambdaの制限されたデバッグ機能では、Lambda関数で取得できるすべての可観測性を収集する必要があります。これは、CloudWatchの明らかなログとメトリクスに加えて、Lambda関数に分散トレースを採用することを意味します。
レスキューのための分散トレース
分散トレースは、2000年代初頭以来、アプリケーションパフォーマンス監視アプローチの不可欠な部分であり、最近、OpenTracing APIとZipkinやJaegerなどのオープンソースプロジェクトによるその実装だけでなく、OpenCensusやHTraceなどの無関係のプロジェクトも、監視と可観測性の議論の最前線に成長しました。OpenTracingおよびOpenCensusプロジェクトは中止されましたが、それらの後継であるOpenTelemetryは積極的に取り組んでいます。
要するに分散トレーシング
マイクロサービスとクラウドネイティブアーキテクチャの登場により、私たちの分散システムはこれまでになく分散化されたと言えます。その間、シグナルソフトウェアコンポーネントはより小さくなりました。ワークロードにサービスを提供するために (うまくいけば) 一斉に動く可動部分が増えるほど、必要な集合的および個別の動作、特にエンドユーザーへのサービスの提供方法に関する可視性が高まります。さまざまなマイクロサービスを接続する「パイプ」に焦点を合わせて、開発者の仕事は配管工の仕事にますます似ていると言う人もいます。
この分散と相互依存の増加が、分散トレースが非常に重要で価値のあるものになった理由です。分散トレースは、サービスが1つの要求を処理する際に実行するアクションを記述するスパンを集合的および協調的に記録することを含む、監視の実践です。同じリクエストに関連するスパンはトレースにグループ化されます。どのトレースが記録されているかを追跡するために、各サービスは、他のアップストリームサービスへの独自の要求にトレースコンテキストを含める必要があります。簡単に言えば、分散トレーシングはリレーレースと考えることができます。これは、アスリートが交代で交代でバトンを通過する陸上競技の分野です。リレーレースとしての分散トレースの例えでは、各サービスはアスリートであり、トレースコンテキストはバトンです。たとえば、サービスの1つがドロップした場合、またはサービス間のハンドオフが異なる分散トレースプロトコルを実装して成功しない場合トレースは壊れます。分散トレースとリレーのもう1つの類似点は、レースの単一セグメントのそれぞれが重要であり、レースに負けてしまう可能性がある一方で、各セグメントで高速である必要があることです。
AWS Lambda関数の分散トレース
AWS Lambdaのトレースに利用できるものを説明する前に、分散トレースソリューションが満たすべき機能的要件と非機能的要件について説明します:
- 多言語ランタイム: AWS Lambdaはさまざまな言語で記述でき、最も一般的に採用されているのはNode.jsとPythonですが、Java、Ruby、.NET Core、Powershellなど、さらに多くの言語があります。マイクロサービスに起こることと同様に (そしてLambda関数は事実上非常に小さなマイクロサービスであるため) 、チームは、採用したいライブラリとSDK、およびチームが使い慣れた言語の知識に基づいて、タスクに最も適していると思われる言語を選択します。私がやり取りしたほぼすべてのLambdaの採用者から聞いたのは、少なくとも2つの異なるAWS Lambdaランタイムを使用しているということです。
- 多言語プラットフォーム: 前述のように、AWS Lambda関数は孤立していません。AWS Lambda関数としてのみ実装されたアーキテクチャの例がありますが、私たちの経験では、それらは (かなり例外的な) 例外であり、ルールではありません。分散トレースで得られる洞察の価値は、相互接続されたシステムの量に応じて大きくなります。つまり、AWS Lambda関数で使用する分散トレースフレームワークは、インフラストラクチャの残りの部分でも使用できるはずです。これはLambdaが使用するインフラストラクチャーに特に当てはまりますが、ネットワークの影響は分散トレースに疑いなく適用されます。ちなみに、これは、分散トレース実装間の相互運用性の測定を提供することを目的としたW3C Trace Context仕様のまさにその理由です。補足として、Lambda関数で使用する言語は、たとえばデータセンタの「レガシー」アプリケーションで使用する言語と異なる場合があり、これによりLambdaトレースの実行時の多言語性が必要になります。
- 低オーバーヘッド: 分散トレースの実装によって発生するオーバーヘッドは、エンドユーザーのレイテンシの観点からだけでなく、AWSの請求書からも直接コストがかかります。Lambda関数は、CPU時間と最大メモリ割り当ての両方によって課金されます。自尊心のある分散トレース実装では、Lambda関数のメモリフットプリントに数十または数百メガバイトが追加されることはありません (ただし、他のプラットフォームでも確認しました)。ただし、特にLambdaのステートレスのため、Lambda関数が完了する前にトレースデータをAPMソリューションに送信する必要があるため、CPU時間は影響を受ける可能性があり、多くの場合、トレースデータがアップロードされるまで関数の完了がブロックされるため、これらのデータを失うリスクがあります。
上記のリストに「他のAWSサービスとの統合」というエントリを追加することについて、私は長く懸命に考えてきました。結局のところ、Lambda関数は、他のサービスによって生成されたイベントから非同期的に呼び出され、AWSのAPI GatewayとApplication Load Balancerから同期的に呼び出されます。たとえば、API Gatewayからの同じトレーススパン内にあると、分散システムと同じくらい古い「この遅延はどこから来るだろう?」という質問に答えるのに役立ちます。ただし、レイテンシのソースが使用中のロードバランサとLambda関数の間のAWSネットワークであることはめったにないため、AWS Lambda関数自体の依存関係の点で、ブロックするか、長時間実行の同期呼び出し待機するか、内部処理かであるため、私は反対しました。
インストルメンテーションのタイプ
トレーシングデータの収集は、特別な計測によって実行されます。一般に、インストルメンテーションは2つの大きなグループに分類できます。
- プログラムによるインストルメンテーションは、OpenTracingやAWSのX-Ray SDKなど、コーディング対象のAPIを提供する実装です。
- 自動インストルメンテーションは、追加のコードを必要とせずにコードとトレースデータの抽出に使用するフレームワークを変更することで機能します。たとえば、通常は、Node.jsとJavaのバイトコード操作によるモンキーパッチによって行われます。
ユーザが使用するフレームワークとライブラリに組み込まれたプログラムによるインストルメンテーション、またはAWS RDSなどのプロバイダーによって管理されるサービスの場合、インストルメンテーションの実装方法は自動であると感じられることに注意してください。これは、コードを維持する必要がないためです。そして、それはまさに私にとって問題の核心です: 優れたインストルメンテーションは機能し、あなたが所有および維持する必要のないコードです。
インストルメンテーションのデリバリー
しかし、必要なデータを収集するために、どのようにインストルメンテーションを運用システムに配信するのでしょうか。これも手動から自動への傾斜です:
- コードと一緒に、またはそれを実行するランタイムで出荷される組み込みのインストルメンテーション: 関数の新しいバージョンをデプロイするときは常に、それに組み込まれているインストルメンテーションコードが続きます。プログラムによるインストルメンテーションはほとんどが組み込みですが、一部のアプローチはAPIと実装の間で分割されており、追加の依存関係を利用できる必要があります。もちろん、組み込みの計測に必要な設定があるかもしれません。
- ドロップインインストルメンテーションは、インストルメンテーションをアクティブにするいくつかの依存関係と構成をランタイムに追加することで構成されます。たとえば、Lambdaレイヤーによって出荷され、実際のLambda関数ハンドラーに実行を委任するカスタムラッパースクリプト(これもLambdaレイヤーを介して配信される)などの構成オプションを介してアクティブ化されたインストルメンテーション (ちなみに、これはInstanaが行う方法です)。
自動構成がプログラムによる構成よりも優れていると私が思うのと同じように、計装を提供するために実行する必要がある作業の量が少ないほど、より良いのです。この観点から、プログラマティックインストルメンテーションも組み込まれている限り、そのメリットがあると考える人もいるかもしれません。私の経験では、そうではありません。プログラムによる計測を維持する作業は、ドロップインの計測を提供する作業よりもはるかにコストがかかります。ほとんどの場合、自動化の提供は、CI/CDパイプラインで一度に、または最小限のメンテナンスで設定できます。ただし、プログラムによるインストルメンテーションはコードを変更し続ける必要があります。これは、コードが変化し続ける限り支払うコストです。Lehman氏のソフトウェア進化の法則は、一言で言えば、ソフトウェアが有用であり続けるためには変化し続ける必要があると述べています。プログラムによる計測を検討する場合、ソフトウェアを有用な状態に保つために必要な変更により、計測を調整する必要がある場合があります。Lambdaでは、ソフトウェアシステムを統合するLambdaに適用する変更の多くはそれらとどのように相互作用するかであり、トレースデータの調整を必要とすることが多いため、計装の調整の必要性を引き起こす変更の可能性は通常よりも高いです。Lambdaコードベースの生存期間を通じて、プログラマティックインストルメンテーションへの必要な投資を収集し、維持する可能性があります。
最良のインストルメンテーションのタイプ
要約すると、私は以前に、最良のタイプのインストルメンテーションは自動化されたものであり、多くのオーバーヘッドなしでそれを提供することが非常に重要であると主張しました。それで、それはどのようにして達成できますか?最先端の技術では、これらの要件を達成できる2つのアプローチがあります:
- Lambdaのカスタムランタイム: AWS Lambdaを使用すると、Lambda関数にカスタムランタイムを提供できます。分散トレースソリューションの一部のベンダーは、ターンキーソリューションとしてカスタムLambdaランタイムを提供しています。ただし、これは、セキュリティとメンテナンスの更新、バグ修正、およびAWS Lambdaランタイムによって提供される新機能には、分散追跡プロバイダがゲートキーパーとして機能することを意味します。マイレージはさまざまかもしれませんが、私は個人的にはあまり良いものではありません。それは、Androidのリリースに対応し、古いデバイスを維持するという一般的に非常に悪い実績を持つモバイルディバイドメーカによるAndroidモバイルエコシステムの問題の多くを思い出させます。もちろん、あなたの経験がどれほど優れているかは、分散トレースベンダーの作業の実際の品質に依存します。これが誰もが使用する必要のないオプションであると言うつもりはありません。
- Lambdaレイヤーでのインストルメンテーション: AWS Lambdaレイヤを使用するように関数を構成できます。これは、Lambdaインスタンスのファイルシステムで使用できるようにする基本的な追加のファイルです。これを使用すると、ごくわずかな操作オーバーヘッドで関数をトレースするために必要な計測を提供できます。そして、多くの分散トレースベンダーは実際に次のことを実行します。たとえば、監視ソリューションのλ AWSome Lambda Layersリストに多数の分散トレースベンダーが見つかります。インストルメンテーションの起動がいかに簡単かという点で、最新の技術には違いがあります。Instanaのような一部のベンダーでは、いくつかの環境変数を設定するだけで済みます。他の人と一緒に、あなたは小さなコードの変更が必要です。コードの変更よりも構成の方が扱いやすいと思いますが、繰り返しになりますが、マイレージは異なる場合があります。
要約すると、私の目には、最良のインストルメンテーションは自動であり、可能な限り簡単な方法で提供されます。
結論
AWS Lambdaはサーバレスコンピューティングに関しては引き続き標準ですが、アプリケーション開発でのサーバレス機能の使用がかつてないほど高いにもかかわらず、企業はまだ、プロダクション環境でサーバレスコンピューティングがいかに効果的であるかを模索しています。
アプリケーション開発プロセスとプラットフォームの重要な部分としてサーバレス機能を使用する組織が増えるにつれ、プロダクションアプリケーションでサーバレス機能をどのように使用できるか、また必要な場合はより多くのサーバレス機能を使用できるようになるか検討しています。
特に、レガシーAPMツールが必要な可視性のレベルを達成するために苦労しているため、サーバレスプロダクションコードを実装するチームにとって依然として大きな課題である可観測性が懸念事項です。
AWS Lambdaは、開発者とオペレーターの両方にとって、サーバレスモニタリングの課題を理解し、アプリケーションの可観測性を取得するためのさまざまな方法を使用する専用のクラウドネイティブモニタリングツールの必要性を強調する大きな違いをもたらしました。
プロダクション環境の一部としてサーバレスを検討する場合は、エンドツーエンドの可観測性を必要条件として含め、クラウドネイティブテクノロジーで構築された分散システム全体、Lambdaなどのサーバレスプラットフォーム、サーバレスはめったに孤立していないので、Lambdaを介して統合された潜在的に古いテクノロジーを監視および追跡できるソリューションに焦点を当てる必要があります。
著者について
Michele Mancioppi氏 Instanaのシニアテクニカルプロダクトマネージャを務め、エージェント、分散トレース、Cloud Foundry、VMware Tanzuに関連するすべての製品開発を指揮しています。Instanaの前は、開発エキスパートであり、SAPのSAP Cloud Platform Performanceチームの技術リーダーでした。Michele Mancioppi氏は、Trento大学でコンピュータサイエンスの学士号と修士号を取得し、オランダのTilburg大学で情報システムの博士号を取得しています。