Lambda からトレースを送りたいだけなのに

この記事はAWS Lambda と Serverless Advent Calendar 2024 八日目の記事です。

二年前にコンテナでデプロイした Lambda から OpenTelemetry で X-Ray にトレースを送るを公開したときはサンプルコードを公開していなかったのですが、今年の四月に(aws-samples にですが)サンプルコードを公開しました。AWS Blog ではこのサンプルコードの解説記事を寄稿しました。解説記事だとこうすればやりたいことができるという方法中心になっていたので、この記事ではなぜやりたいことをやろうとするとこんなに複雑になってしまうのかを中心に書きます。

コンテナでデプロイした AWS Lambda から OpenTelemetry SDK を使って AWS X-Ray にトレースを送る

やりたいことを実現するのがなぜこんなに大変なのか

やりたいことはタイトル通り「コンテナでデプロイした AWS Lambda から OpenTelemetry SDK を使って AWS X-Ray にトレースを送る」です。ポイントは三つあります。

  1. コンテナで AWS Lambda をデプロイする
  2. OpenTelemetry SDK を使う
  3. AWS X-Ray にトレースを送る

この三つが組み合わさることで実現方法が複雑になります。

Lambda をコンテナでデプロイしようとしている

Lambda は Zip またはコンテナでデプロイすることができ、コンテナでデプロイすると Lambda layer を使うことができません1。Lambda layer を使えないのはしんどいです。

まず OpenTelemetry Lambda に関連するリポジトリやサイトを紹介しておきます。OpenTelemetry のドキュメントには FaaS のページがあり、この中で OpenTelemetry Lambdaがリンクされています。AWS Distro for OpenTelemetry Lambda のページの方は少し詳しくて、AWS managed の Lambda layer と開発者自身で Lambda layer を開発する場合の参考ページがリンクされています。

AWS Distro for OpenTelemetry ( AWS がサポートする OpenTelemetry ディストリビューションのことで略して ADOT 。以下 ADOT )と X-Ray 、Lambda を統合することを考えるとドキュメントでは AWS managed Lambda layers for ADOT が紹介されています。AWS managed の Lambda layer を使うことができれば OpenTelemetry Collector のビルドなどを AWS 側にオフロードすることができますが、コンテナで Lambda をデプロイすると Lambda layer を使えないので OpenTelemetry Collector をビルドして Lambda extensions に登録する必要があります。

OpenTelemetry SDK を使おうとしている

X-Ray にトレースを送るだけなら X-Ray SDK を使った方が設定は簡単だと思います。ドキュメントにあるように Lambda の設定で X-Ray を有効にすることと Lambda にアタッチした IAM Role に X-Ray への書き込み権限を足すだけで良いです(開発者側で X-Ray daemon を Lambda layer に入れて、みたいなことは不要)。

あとは Lambda function を X-Ray SDK で計装するだけです。ドキュメントによれば X-Ray SDK がサポートしている言語は Go, Java, Node.js, Python, .NET, Ruby の 六種類で ADOT はこれらに PHP を加えた 七種類の言語をサポートしています。OpenTelemetry ならもう少し多くの言語をサポートしているので使っている言語によっては X-Ray SDK は厳しいとなるかもしれません。

OpenTelemetry SDK を使おうとすると X-Ray SDK を使う場合は考えなくていいような OpenTelemetry Collector をビルドして Lambda extensions に登録して云々を考えなくてはいけません。

X-Ray にトレースを送ろうとしている

OpenTelemetry Lambda の collector の中身を見てみると awsxrayexporter が含まれていないことがわかります。つまりただ OpenTelemetry Lambda のリポジトリをクローンしてきてビルドする(これだけでも結構手間です)だけだと awsxrayexporter が含まれていないので X-Ray にトレースを送ることができません。X-Ray にトレースを送るならリポジトリをクローンした上で依存ライブラリに awsxrayexporter を加えた上でビルドする必要があります。

やりたいことを実現するには

公開したブログで手順を解説しています。ブログではまず OpenTelemetry Lambda のリポジトリをクローンしてきて、awsxrayexporter を依存ライブラリに追加した上で Collector をビルドしこの Collector を ECR にプッシュし、Lambda 関数用の Dockerfile のなかでこの Collector をコピーしてきてデプロイしています。Lambda 関数用のコンテナの中に Collector を含めることができればいいので他の方法でも可能です。例えば Collector のコードと Lambda 関数のコードを同じリポジトリに含めて Lambda 関数用コードをビルドするときに Collector をビルドしてもいいでしょうし、Collector をビルドしたものを zip か何かに固めて Lambda 関数の中に解凍して配置しても動くと思います。

コンテナでデプロイした AWS Lambda から OpenTelemetry SDK を使って AWS X-Ray にトレースを送ることはできる、けど…

正直な感想

コンテナでデプロイした AWS Lambda から OpenTelemetry SDK を使って AWS X-Ray にトレースを送るにはだいぶややこしい手続きが必要だと感じたのではないかと思います。実際私がトレースを送れるようになるまでかなり時間がかかりました。目的が Lambda を含む分散システムにおいてオブザーバビリティを獲得することにあるなら他の方法をとった方が手間がかからないと思います(コンテナではなく zip でデプロイすることにすれば AWS managed Lambda layer を使えますし、コンテナでデプロイするにしても X-Ray SDK を使えば設定項目はかなり少ない)。トレースを表示できて終わりではなくこのあとシステムを運用していくことを考えるとブログで紹介した方法を続けていくのは以下の理由で負荷が大きいのではないでしょうか。

  • Collector を管理するチームと Lambda を管理するチームの境界線をどこに引くか。
    • Lambda の数が 10,100 と増えていったときに Lambda を管理するチームがそれぞれ自分たちで Collector をビルドするのは理想的ではあるもののチームメンバーが入れ替わる中でやり続けるのは大変そう。
    • とすると Collector を管理するチームを分けることになるが、Collector をアップデートしたときに全ての Lambda の Dockerfile を更新しないといけない。Collector を保存するコンテナリポジトリと Lambda のコードを管理するリポジトリが分かれるのでビルドの際にリポジトリへのアクセス権を付与しないといけないし、リポジトリが増えるたびに権限の更新が必要。
  • 不具合が生じたとき Lambda 関数の中で起きた不具合は責任共有モデル上開発者の責任範囲になる。
    • AWS managed な Lambda layer を使っていて何か不具合が起きたときはサポートケースを起票すればサポートに調査を依頼することができます。仮にサポート範囲外だと言われたとしてもユーザー側のコードや設定に問題がなければ AWS managed な Lambda layer に何かあったのではないかと主張することはできるでしょう。
    • 一方で OpenTelemetry Lambda リポジトリをユーザー側で改変し、コンテナにまとめてデプロイしている場合何か不具合が生じても現実問題サポート側で調査することは難しいと思われます。

Lambda と OpenTelemetry の相性

サーバーレスとオブザーバビリティでいうと AWS の場合いくつか選択肢があります。AWS サービスで固める場合は X-Ray でトレースを、CloudWatch でメトリクスやログを表示することができるでしょうし、オブザーバビリティ周りの SaaS を使うこともできるでしょう2。選択肢の一つである Lambda と OpenTelemetry はコンテナでデプロイする場合はあまり簡単な道ではなさそうです(zip でデプロイする場合は比較的歩きやすい道には感じます。SaaS の場合も Lambda layer を使う場合は始めやすそうでした)。

AWS Lambda や AWS の他のサーバーレスコンピュートサービスは必要な時だけ処理を実行しそれ以外は何もしません3。この必要な時だけ処理をするサーバーレスの世界観と、常時バックエンドのサービスと通信するためのエージェントを起動しておく OpenTelemetry やオブザーバビリティ SaaS の世界観があまり相性が良くないように感じました。バックエンドのサービスとの通信やアプリケーションレイヤーより下のレイヤーのテレメトリを収集するためにアプリケーション部分と関心を分離する上でエージェントを切り出す方向性と、必要な時だけ処理をするので必要なものは全てまとめておく方向性と、どちらもそれ自体は妥当に感じるが組み合わせようと思ったときに噛み合わせが悪いそんなイメージを持っています。

Lambda と X-Ray の組み合わせの場合はどちらも AWS のサービスであり内部で開発チームごとにコミュニケーションを取って色々最適化することができ、何より SDK の送り先が X-Ray ただ一通りだけなので(実際にこの通りに動いてるかはわかりませんが) Lambda worker 上に常時 X-Ray daemon を起動しておくというアプローチを取ることができるかもしれません。しかし OpenTelemetry はさまざまな SaaS や OSS 向けにコンポーネントが開発されているので常時一種類の Collector を動かしておくことは難しいでしょう(New Relic にデータを送るユーザーもいれば Datadog に送るユーザーもいてそれぞれバラバラなので、全てのユースケースに備えた Collector を動かすと無駄になるリソースが増える一方で、ユースケース決めうちだと一部のユーザーしか満足させられない上にサービス提供側から見て非効率すぎる)。となると Collector を Lambda の依存物の中に含めるしかなく Lambda layer を使うことでなんとか開発者の関心から Collector を分離しようとしているがコンテナの場合はまだ道半ばということのようです。

X-Ray SDK と OpenTelemetry SDK どちらを使うか

繰り返し書いているように今この瞬間にコンテナでデプロイした Lambda を含むシステムのオブザーバビリティを高める場合は X-Ray SDK を使う方が開発にかかる時間は短くて済むと思います。OpenTelemetry SDK と X-Ray SDK どちらを選ぶといいかはドキュメントに書かれているのでこれを読んで開発者の状況ごとに推奨された方を使えばいいと言えばそれまでです。ただ(将来のことは誰にもわからないということは置いておいても)推奨された通りに手放しで突き進むことに躊躇する部分があります。

まず第一に SDK を使用するユーザーの規模が違います。X-Ray SDK for Go のスター数は今日見たところ 280 でしたが OpenTelemetry-Go の場合は 5.4k と一桁大きいです。スター数が全てではないですが使っているユーザー数はどちらか選ぶときの重要なファクターであることに変わりはないです。使用するユーザーが多いほど何かの不具合が発見される可能性や関連するライブラリやミドルウェアが開発される可能性が高いでしょう。また SDK を開発する側としてもついているユーザーがあまりに少ないと開発リソースが継続して投入されないかもしれません。この点では OpenTelemetry SDK の方が魅力的に感じます。

第二に異なるバックエンド向けに実装を切り替えなくていいという OpenTelemetry の利点が AWS のサーバーレスと組み合わせたときにどこまで効いてくるかという点があります。OpenTelemetry はベンダー中立な仕様を定めていてその仕様に基づいた SDK で計装していれば別のバックエンドに切り替えることができる、アプリの書き換えが不要でサービスにロックインされない、というのは魅力的に映ります。しかしベンダーに依存しない機能だけ使っているとあるベンダー製品の真価を 100% は発揮できない(ベンダー製品ごとに差別化ポイントがありそれを使わなければロックインは避けられるだろうけど便利ポイントも捨てることになる)でしょうし、今利便性を捨ててロックインを避ける選択がいいのかどうかは後になってからでないとわからないでしょう。さらに AWS のサーバーレスサービスを使っている時点でロックインしているわけで(おそらく Lambda 関数用の Dockerfile そのままだと他社のサービスにはデプロイできない) Lambda でしか動かないアプリのコードが他のオブザーバビリティバックエンドに切り替えられる自由度を持っていることがどれだけ嬉しいのか現時点では評価できません。こう考えると OpenTelemetry SDK でいこうとフルスイングできなくなります。

第三に Lambda で OpenTelemetry SDK を使うのはいいとしてシステムは Lambda だけで構成されるわけではなく、他のサービスと連携したときに OpenTelemetry が使えるのかという点があります。X-Ray の場合連携できるサービスがドキュメントにまとまっていて例えば S3 にファイルをアップロードするときに trace header を Lambda に転送することができます。実は OpenTelemetry と X-Ray でトレース ID の形式が異なる4ので他のサービスが OpenTelemetry 形式のトレース ID を伝播させてくれないといくら Lambda を OpenTelemetry で計装していても途中でトレースが切れてしまいます。S3 にファイルをアップロードし、そのイベントをトリガーに Lambda が発火するときの一連のトレースを取得する場合は、S3 へのファイルアップロード処理と Lambda を OpenTelemetry で計装するだけでなく、S3 が Lambda を呼び出す処理においても OpenTelemetry 形式のトレースが伝播されないといけません。ドキュメントには If a service traces requests by using the X-Ray SDK, Amazon S3 can send the tracing headers to downstream event subscribers such as AWS Lambda, Amazon SQS, and Amazon SNS. と書かれているので OpenTelemetry SDK でリクエストが計装されている場合の挙動は undocumented です。そもそも伝播されるかわからないし今伝播されていても今後もその挙動が続くかはわかりません( undocumented なので)。Step Functions のステートマシンが Lambda や他の AWS サービスを呼ぶ場合や SNS+SQS でファンアウトする場合などイベントドリブンなアーキテクチャにおいてそれを構成するサービスが OpenTelemetry 形式のトレースを伝播するかを調査するのは骨が折れそうです。AWS サービスはtwo-pizza teams と呼ばれる少人数の独立したチームで開発されており、その全てのチームが OpenTelemetry に対応する日が来るのかを考えると、X-Ray SDK を採用したくなります。

このように OpenTelemetry SDK と X-Ray SDK それぞれ一長一短ありどちらかを自信を持って選ぶのが難しいです。アプリケーションロジックからできるだけオブザーバビリティの計装を分離し、最悪 SDK を入れ替えられるように意識しておくというある意味当たり前のことを考えておくしかなさそう。

Appendix

脚注


  1. ドキュメントには You can include up to five layers per function. Also, you can use layers only with Lambda functions deployed as a .zip file archive. For functions defined as a container image, package your preferred runtime and all code dependencies when you create the container image. と書いています。 ↩︎

  2. 試してはいないですが SplunkNew RelicDatadogdynatraceなどはドキュメントが見つかりました。 ↩︎

  3. ドキュメントの言い方を借りると The Lambda service runs your function only when needed and scales automatically.  ↩︎

  4. OpenTelemetry の仕様によれば TraceID は 16 randomly generated bytes = 128 bits = 32 hex digits で X-Ray はドキュメントにあるように 1-(8 hex digits)-(24 hex digits) ↩︎