AWS Lambda SnapStartを有効にする時にハマったポイント
AWS Lambda SnapStartを有効にする時にハマったポイントを備忘録として残します。
結論
- SnapStartはAWS X-Rayをサポートしていない。
- よって、AWS X-Ray Active tracingを有効にしてトレースができない。
- トレースしたい時は、AWS Distro for OpenTelemetryを使用しよう。
- SnapStartを有効にすると、Javaランタイムは自動で環境変数のクレデンシャルではなくContainer credentialsを使用する。
Lambdaの環境
- Lambdaランタイム: Java11
- アーキテクチャ: x86_64
- region: ap-northeast-1
ハマりポイント
SnapStartはAWS X-Rayをサポートしていない
2022/12/15時点では、SnapStartはAWS X-Rayをサポートしていません。
なので、Lambda関数をトレース&可視化する時に有効化されることが多いAWS X-Ray Active tracingを有効にすることができませんでした。
解決策
どうしてもAWS X-Rayでトレーシング・可視化したいケース
現状の解決策はAWS X-Rayがサポートされることを待つしかないかもです。
ツールにこだわりなくトレーシング・可視化したいケース
こちらのケースであれば解決できそうです。
公式ドキュメントでもおすすめされているAWS Distro for OpenTelemetry(ADOT)を使いましょう。
ADOTはAWS Distro for OpenTelemetry Lambdaを提供しています。
ドキュメントにも記載されていますが、プラグアンドプレイでLambdaのトレースができます。
ADOT LambdaのGetting startedを参考にすれば導入は簡単です。
トレースしたいLambdaのLambdaレイヤーにADOTを追加し、いくつか環境変数を追加するだけです。Lambda関数自体のコード修正は必要ありません。
そして、ここからが超大事なのですが、ADOTはデフォルトでトレースデータをエクスポートする先にAWS X-Rayを設定しています。なので、トレースをするためにはAWS X-Ray Active tracingを有効にする必要があります。
え?それじゃダメじゃない??
そうです、デフォルトの設定のままだと、SnapStartを有効にしている限りはトレースすることができないと思います。
なので、OpenTelemetry Collectorのconfigを変更し、エクスポーターをAWS X-Ray以外のものに変更しましょう。
Custom configuration for the ADOT Collector on Lambdaを読めば難なくconfigを変更できると思います。設定ファイルの変更だけで簡単にツールを変更できちゃう点は、さすがOpenTelemetryさんという気持ちになります。
現時点でサポートされているエクスポーターはこちらです。
私のケースでは、Lambda関数のボトルネックを探る調査タスクだったため、比較的導入が手軽そうなloggingをエクスポーターに指定しました。
receivers:
otlp:
protocols:
grpc:
http:
exporters:
logging:
loglevel: debug
service:
pipelines:
traces:
receivers: [otlp]
exporters: [logging]
Lambda関数実行後、CloudWatch Logsでログが出力されることを確認できました。
アクセスキーが見つからない
SnapStartを有効にし、関数のテストを実行したところ下記のようなエラーが発生しました。あれ、有効にする前はテスト通っていたんだけどな?
Unable to load credentials from system settings.
Access key must be specified either via environment variable (AWS_ACCESS_KEY_ID)
or system property (aws.accessKeyId).:
software.amazon.awssdk.core.exception.SdkClientException
エラーの発生元を辿ると、以下のように環境変数からアクセスキーを取得するように指定していました。SnapStartを有効にする前は、このコードでアクセスキーを取得できていたはずだぞ??
val sesClient = SesClient
.builder()
.credentialsProvider(EnvironmentVariableCredentialsProvider.create())
.httpClient(apacheHttpClient)
.build()
実は、SnapStartを有効にすると、Javaランタイムは環境変数のアクセスキーでなくContainer Credentialsを使いアクセスキーを取得します。
ドキュメントを読む限りの推測ですが、通常はLambdaの初期化時に環境変数に一時的なアクセスキーが保存されるため、SnapStartを有効にするとアクセスキーが更新されず、そのアクセスキーの有効期限が切れてしまう。という問題が発生するのかなと推測しました。
なので、Container Credentialsで特定のURIにリクエストをして、アクセスキーを取得するようになっているのかと。いや、ここは自信ないです。
解決策
解決策の1つとしてデフォルトの認証情報プロバイダーチェーンを使用する方法を記載しておきます。以下のように認証情報プロバイダーを設定しないことでデフォルトを自動的に使用してくれます。
val sesClient = SesClient
.builder()
.httpClient(apacheHttpClient)
.build()
これによって、環境変数に設定されている場合も、Container Credentialsの場合でもアクセスキーを取得して関数を実行してくれます。
Lambda関数のパフォーマンスを最適化したい場合は、ちゃんと認証情報プロバイダーを指定した方が良いかもしれません。
まとめ
AWS Lambda SnapStart を有効にするためにハマったポイントを記載しました。
SnapStart x AWS Distro for OpenTelemetry によって、JVM言語で実装したLambda関数を実戦投入する機会が今後増えるかもしれないなと思いました。とても楽しみです!
明日は @curry_on_a_rice さんです。
Discussion