Amazon Web Services ブログ
AWS Compute Optimizer を使用した AWS Lambda のコストとパフォーマンスの最適化
本投稿は AWS Compute Optimizer のシニアプロダクトマネージャーである Brooke Chen、AWS Compute Optimizer のプリンシパルプロダクトマネージャーである Letian Feng、Amazon EC2 のプリンシパルデベロッパーアドボケイトである Chad Schmutzer による寄稿です。
コンピューティングリソースの最適化は、あらゆるアプリケーションアーキテクチャにとって重要な作業です。計算処理リソースの過剰なプロビジョニングは不要なインフラストラクチャコストにつながる一方で、不足すると、アプリケーションのパフォーマンスの低下につながります。
2019年12月に開始された AWS Compute Optimizer は、AWSコンピューティングリソースのコストとパフォーマンスの最適化のための推奨情報(リコメンデーション)を提示するサービスです。特定のワークロードに合わせた実用的な最適化の推奨事項を生成します。昨年、数千の AWS のお客様が、Compute Optimizer を使用してワークロードに最適な Amazon EC2 インスタンスタイプを選択することで、コンピューティングコストを最大25%削減しました。
お客様から最も頻繁にいただくリクエストの 1 つに、Compute Optimizer で AWS Lambda の推奨事項を提示してほしいというものがありました。これを受けて、2020年12月23日、Compute Optimizer が Lambda 関数の推奨メモリサイズをサポートすることを発表しました。これにより、Lambda ベースのサーバーレスワークロードのコストを最適化し、パフォーマンスを向上させることができます。開始するには、Compute Optimizer をオプトインして、推奨事項の検出に進みましょう。
概要
Lambda を使用すると、管理するサーバーがなく、自動的にスケーリングされ、使用した分だけの料金となるなどの、サーバーレスとしてのメリットが多くあります。ただし、Lambda 関数に適切なメモリサイズ設定を選択することは依然として重要なタスクです。Computer Optimizer は、機械学習ベースでメモリの推奨を行うことで、このタスクを支援します。
この推奨事項の提供機能は、Compute Optimizer コンソール、AWS CLI、AWS SDK、および Lambda コンソールから利用できます。Compute Optimizer は、Lambda 関数を継続的に監視し、過去のパフォーマンスメトリックを使用して、時間の経過とともにリコメンデーションを改善します。このブログ投稿では、この機能の使用方法を示す例を紹介します。
Compute Optimizer for Lambda の使用
このチュートリアルでは、AWS CLI v2 と AWS マネジメントコンソールを使用します。
このチュートリアルでは、US East リージョン(N.Virginia)で毎分実行される 2 つのコンピューティングジョブを使っています。1 つのジョブは他のジョブよりも CPU を集中的に使用します。最初のテストでは、両方のジョブの呼び出し時間は通常 60 秒未満であることが示されています。目標は、実行時間を大幅に増やすことなくコストを削減するか、コスト効率の高い方法で実行時間を短縮することです。
なお、こような実行要件に対してサーバーレスソリューションを利用しています。Amazon EventBridge は、ルールを使用して Lambda 関数の実行をスケジュールできます。それでは、関数がコストとパフォーマンスに対して最適化されていることを確認するために、Compute Optimizer のメモリ推奨サポートを使用しましょう。
AWS アカウントで、Compute Optimizer にオプトインして、AWS リソースの分析を開始します。まずは、適切な IAM 権限が設定されていることを確認してください – これはこの手順に従ってください。次にオプトインですが、マネジメントコンソールを使用する場合は、この手順に従います。CLI を用いる場合は、次のようにターミナルウィンドウで入力します。
$ aws compute-optimizer update-enrollment-status --status Active
Compute Optimizerを有効にすると、最後の 14 日間に少なくとも 50 回呼び出された関数を対象にしてスキャンが開始されます。次のセクションでは 2 つの Lambda 関数の例を示します。
Lambda 関数の例
CPU 処理負荷がそれほどではないジョブのコードは次のとおりです。lambda-recommendation-test-sleepという名前の Lambda 関数が、1024MB に設定されたメモリサイズで作成されます。EventBridge ルールが作成され、1分間の定期的なスケジュールで関数がトリガーされます。
import json
import time
def lambda_handler(event, context):
time.sleep(30)
x=[0]*100000000
return {
'statusCode': 200,
'body': json.dumps('Hello World!')
}
CPUを集中的に使用するジョブのコードは次のとおりです。lambda-recommendation-test-busyという名前の Lambda 関数が、128MBに設定されたメモリサイズで作成されます。EventBridge ルールが作成され、1 分間の定期的なスケジュールで関数がトリガーされます。
import json
import random
def lambda_handler(event, context):
random.seed(1)
x=0
for i in range(0, 20000000):
x+=random.random()
return {
'statusCode': 200,
'body': json.dumps('Sum:' + str(x))
}
Compute Optimizer の推奨事項の理解
Compute Optimizer は、推奨事項を提供するために、過去 14 日間に少なくとも 50 回の Lambda 関数の呼び出しの履歴を必要とします。推奨事項は、呼び出し回数、実行時間、エラー数、成功率などの CloudWatch メトリクスに加えて、メモリサイズ、タイムアウト、ランタイムなどの関数メタデータを分析することによって作成されます。
Compute Optimizer は、Lambda 関数の推奨メモリを提供するために必要な情報を収集し、48 時間以内に利用できるようにします。その後、これらの推奨事項は毎日更新されます。
これらは、CPU 処理負荷がそれほどではない関数の直近の呼び出しです。
関数の実行時間は約 31.3 秒で、メモリ設定は 1024 MB であるため、呼び出しごとに約 $ 0.00052 の利用コストが発生します。Compute Optimizer コンソールでのこの関数に対する推奨事項は次のとおりです。
メモリが過剰にプロビジョニングされている(Memory over-provisioned)と認識され、そのため関数が最適化されていない(Not optimized)と判定されています。CLI を介して同じ推奨事項を取得することもできます。
$ aws compute-optimizer \
get-lambda-function-recommendations \
--function-arns arn:aws:lambda:us-east-1:123456789012:function:lambda-recommendation-test-sleep
{
"lambdaFunctionRecommendations": [
{
"utilizationMetrics": [
{
"name": "Duration",
"value": 31333.63587049883,
"statistic": "Average"
},
{
"name": "Duration",
"value": 32522.04,
"statistic": "Maximum"
},
{
"name": "Memory",
"value": 817.67049838188,
"statistic": "Average"
},
{
"name": "Memory",
"value": 819.0,
"statistic": "Maximum"
}
],
"currentMemorySize": 1024,
"lastRefreshTimestamp": 1608735952.385,
"numberOfInvocations": 3090,
"functionArn": "arn:aws:lambda:us-east-1:123456789012:function:lambda-recommendation-test-sleep:$LATEST",
"memorySizeRecommendationOptions": [
{
"projectedUtilizationMetrics": [
{
"name": "Duration",
"value": 30015.113193697029,
"statistic": "LowerBound"
},
{
"name": "Duration",
"value": 31515.86878891883,
"statistic": "Expected"
},
{
"name": "Duration",
"value": 33091.662123300975,
"statistic": "UpperBound"
}
],
"memorySize": 900,
"rank": 1
}
],
"functionVersion": "$LATEST",
"finding": "NotOptimized",
"findingReasonCodes": [
"MemoryOverprovisioned"
],
"lookbackPeriodInDays": 14.0,
"accountId": "123456789012"
}
]
}
Compute Optimizer の推奨事項には、関数に関する有用な情報が含まれています。最も重要なことは、関数がメモリに対して過剰にプロビジョニングされているという結論です。findingReasonCodes 属性値が MemoryOverprovisioned を示している部分です。また、memorySizeRecommendationOptions において、Compute Optimizer から、実行時間 約 31.5 秒という期待値で 900 MBというメモリサイズの推奨結果が提示されています。
CPU 処理負荷がそれほどではないジョブの場合、関数のメモリ設定を減らしても、関数の実行時間に悪影響を与えないことがよくあります。この推奨事項により、メモリサイズを 1024MB から 900MB に減らすことができ、それでいて実行時間に大きな影響を与えることなく利用費用を節約できることが示されています。呼び出しによる利用コストは約 12% 節約されることになります。
Compute Optimizer コンソールは、この結果をこのように表示します。
以下は、CPU を集中的に使用する 2 番目の関数の直近の呼び出しです。
関数の実行時間は約 37.5 秒で、メモリ設定は 128 MB であるため、呼び出しごとに約$ 0.000078の利用コストが発生しています。この関数の推奨事項は Compute Optimizer コンソールに次のように表示されました。
この関数では、メモリが不足している(Memory under-provisioned)ため、最適化されていない(Not optimized)と判定されました。同じ推奨情報は CLI からも利用できます。
$ aws compute-optimizer \
get-lambda-function-recommendations \
--function-arns arn:aws:lambda:us-east-1:123456789012:function:lambda-recommendation-test-busy
{
"lambdaFunctionRecommendations": [
{
"utilizationMetrics": [
{
"name": "Duration",
"value": 36006.85851551957,
"statistic": "Average"
},
{
"name": "Duration",
"value": 38540.43,
"statistic": "Maximum"
},
{
"name": "Memory",
"value": 53.75978407557355,
"statistic": "Average"
},
{
"name": "Memory",
"value": 55.0,
"statistic": "Maximum"
}
],
"currentMemorySize": 128,
"lastRefreshTimestamp": 1608725151.752,
"numberOfInvocations": 741,
"functionArn": "arn:aws:lambda:us-east-1:123456789012:function:lambda-recommendation-test-busy:$LATEST",
"memorySizeRecommendationOptions": [
{
"projectedUtilizationMetrics": [
{
"name": "Duration",
"value": 27340.37604781184,
"statistic": "LowerBound"
},
{
"name": "Duration",
"value": 28707.394850202432,
"statistic": "Expected"
},
{
"name": "Duration",
"value": 30142.764592712556,
"statistic": "UpperBound"
}
],
"memorySize": 160,
"rank": 1
}
],
"functionVersion": "$LATEST",
"finding": "NotOptimized",
"findingReasonCodes": [
"MemoryUnderprovisioned"
],
"lookbackPeriodInDays": 14.0,
"accountId": "123456789012"
}
]
}
この関数の場合、Compute Optimizer は、関数のメモリが十分にプロビジョニングされていないと判断しています。findingReasonCodes の値が MemoryUnderprovisioned となっている部分です。メモリを 128MB から 160MB に増やすことが推奨されています。
この関数は呼び出しごとに 55MB のメモリしか使用しないため、この推奨事項は直感に反しているように思われるかもしれません。ただし、Lambda は、構成されたメモリの量に比例した形で CPU およびその他のリソースを割り当てているということに注意してください。メモリ割り当てを 160 MB に増やすと、予想される実行時間が約28.7秒に短縮される可能性があることを意味します。これは、CPUを集中的に使用するタスクでは、追加のメモリに伴うCPUパフォーマンスの向上から恩恵を受けるためです。
この推奨事項を適用した後の、呼び出しによる予想利用コストは約$ 0.000075 です。これは、実行時間による利用費がほぼ同じでありながら、ジョブの待機時間が 37.5秒から 28.7秒に短縮されることを意味します。
Compute Optimizer コンソールは、この結果を次のように表示します。
Compute Optimizer の推奨事項の適用
Compute Optimizer の推奨事項を使用して Lambda 関数を最適化するには、次の CLI コマンドを使用します。
$ aws lambda update-function-configuration \
--function-name lambda-recommendation-test-sleep \
--memory-size 900
関数を複数回呼び出した後、コンソールでこれらの呼び出しのメトリックを確認できます。これは、メモリサイズを 1024MB から 900MB に減らした後でも、実行時間が大幅に変更されていないことを示しています。Lambda 関数は、処理時間を増やすことなく、利用費を最適化することに成功しました。
CPU を集中的に使用する関数に推奨事項を適用するには、次の CLI コマンドを使用します。
$ aws lambda update-function-configuration \
--function-name lambda-recommendation-test-busy \
--memory-size 160
関数を複数回呼び出した後、コンソールは呼び出し時間が約 28 秒に短縮されたことを示します。これは推奨の予想と一致します。関数が大幅な利用費の増加なしにパフォーマンスが最適化されたことを示しています。
メモ
最後に注意事項を記載しておきます
- すべての関数に対して推奨を受け取れるわけではありません。Compute Optimizer は、推奨事項が利用費の削減または実行期間の短縮に役立つ可能性があると確信できる場合にのみ、推奨事項を提供します。
- 環境に変更を加える場合: 本番環境に適用する前に、推奨されるメモリサイズ構成をテストすることを強くお勧めします。
まとめ
Lambda 関数を使用して、サーバーレスワークロードに Compute Optimizer を使用できるようになりました。これは、ワークロードに最適な Lambda 関数構成オプションを特定するのに役立ちます。Compute Optimizer は、利用できるすべての AWSリージョンで Lambda 関数のメモリサイズの推奨事項をサポートします。これらの推奨事項は、追加費用なしで利用できます。単にコンソールから Compute Optimizer の使用を開始するだけです。
AWS Compute Optimizer の開始についてもご覧ください。
原文はこちらです。