🐶

Datadog Forwarder は最新化すべし

こんにちは。ヴィエス・オカモッティです。
本日の記事でお伝えしたいことはタイトルに記載いたしました。

いかがでしたか? 次回の記事もお楽しみに!
ここまでお読みいただき、ありがとうございました。

……だけで締めたかったのですが、さすがに叱られそうなので、事の経緯や弊社における Datadog の使いっぷり等についてご興味ある方がもしおられましたら、以下をご笑覧くださいませ。

起(Datadog Forwarder について)

まず前提として、弊社のシステムはそのほとんどが AWS で稼働しており、対象システムの監視については Datadog を利用しております。ドキュメントの「製品」の項を参照いただければ分かる通り、多種多様な機能で様々な形で弊社システムのオブザーバビリティの向上に寄与してくれており、大変重宝しております。
https://docs.datadoghq.com/ja/
今回は、その中の一つである Log Management に関する記事となります。

https://docs.datadoghq.com/ja/logs/

Log Management はその名の通り、多種多様なデータソースにおいて出力される様々なログを Datadog に集約させ、適切にインデックス、もしくはアーカイブさせたりメトリクスとして数値化することで柔軟なログの管理やモニタリングを実現できる機能です。上記リンク先に世界観を表現した分かりやすいインフォグラフィックがありますのでぜひご覧ください。

例えば物理 or 仮想を問わず、サーバで生成されるログであればインストールされた Datadog Agent がログを転送してくれますし、コンテナログであれば Datadog Agent が動作するコンテナを sidecar させることで転送が可能です。では AWS のようなクラウドサービスが Amazon S3 や Amazon CloudWatch Logs といったサービス上に生成するログを集約するためにはどうするのでしょうか? そこで登場するのが Datadog Forwarder です。

Datadog Forwarder は Python で書かれたアプリケーションで、 AWS Lambda 関数として動作します。この時点で勘の良い読者様はお気付きかもしれませんが、つまり S3 や CloudWatch Logs において発生したログファイルの書き込みイベントを同関数の発火条件として設定できるため、 S3 や CloudWatch Logs をログ出力先として指定できる多数の AWS サービスのログを Datadog へ転送させることを可能にしてくれるというわけです。

S3 や CloudWatch Logs をトリガーとして設定できる

かわいいね。

そして Lambda 関数ということはその設定に伴う諸々の検討事項として、例えばランタイムはどれにしよう、実行ロールにはどんな権限を付与しよう、などといった悩みが付きまとうことになります。しかしそこはあらゆるサービスとの手軽な連携を売りとしている Datadog ということもあり、親切にも Datadog Forwarder の設定に必要なリソース一式をデプロイしてくれる CloudFormation Stack が用意されております。
https://docs.datadoghq.com/ja/logs/guide/forwarder/?tab=cloudformation#インストール

弊社でも Datadog 導入段階でこちらの恩恵に与り、即座に AWS サービスログの Datadog への転送設定を実現することができました。そして Datadog に転送されたログはパイプライン[1]の処理によってパースされた上で、ユーザのログ管理設定に基づいて Log Explorer での閲覧を可能にするためインデックスしたり、後から参照できるよう指定のストレージ上にアーカイブ化させたりといった処理が可能になります。

パイプライン
いろんなロゴが並んでいて、かわいいね。

承(Step Function のログ監視について)

以前の記事でも触れている通り、弊社ではバッチ処理の基盤として AWS Step Functions (以降 SFn と表記)を利用しております。元々 SFn の導入段階から、処理の成否をモニタリングするために SFn が CloudWatch に生成する成否数のメトリクス監視は仕込んでありました。その中で、もう少し具体的に動作状況を把握したいというニーズが生じたため、では SFn が生成するログを Datadog でモニタリングできないだろうか?と、試してみることにしました。

SFn はその動作ログを CloudWatch Logs に出力することが可能です。 Datadog のドキュメントでも監視設定の手順として、 SFn におけるログ出力の有効化と共にログの Datadog への転送を設定するよう明記されているのですが、ここに記載されている以下の注意事項がポイントとなります。

注: Datadog がログのソースを識別し、自動的にパースするために、CloudWatch のロググループのデフォルトのプレフィックス /aws/vendedlogs/states を使用します。

https://docs.datadoghq.com/ja/integrations/amazon_step_functions/#収集データ

なるほど、任意の path 名で設定するのではなく VendedLogs[2]であることを表現した path 名で設定すべきということですね。幸い、今回設定対象としておきたかった SFn のログ出力先は AWS が設定時にサジェストしてくれる /aws/vendedlogs/states/XXXXX という path に出力するよう設定済みでしたので、この点は問題無さそうです。

では Datadog Forwarder 関数の起動トリガとして、この CloudWatch Logs への書き込みイベントを条件に設定してみましょう。

この状態で SFn のログが CloudWatch Logs に書き込まれると、 Datadog Forwarder によって Datadog に転送され、パイプラインの処理によって SFn のログとして判定&パースされることを期待していたのですが……確かに対象のログは Datadog に到達できているものの、なぜか SFn ではなく CloudWatch Logs のログとしか判定されず、いや、それはそうなんだけど、そういうことではなく、 SFn のログとして判別してもらった上でログ全体の JSON をパースして欲しいのに……という形で躓きが生じてしまいました。

転(トラブルシューティング)

では、そもそもパイプラインはどの情報を元にそのログの判別を実現しているのでしょうか? 今回のポイントである Datadog Forwarder によって転送されるログに限らず、 Datadog Agent 等のサーバにインストールされるアプリケーション等によって転送されるケースであっても、ログには多様なタグ情報が付与された形で転送される仕組みとなっています。

このタグ情報はログだけではなく、 Datadog のために採取されたあらゆる観測データにも同様に付与され、特定のリソースに関連するあらゆる種別のデータを横断的に紐付けてくれることで、システムのオブザーバビリティを高める役割を果たしてくれます。そしてパイプラインは、そのタグのうち source というタグの value に基づいてログの種別を判断してくれる機能というわけです。

Step Functions Pipeline

どうやら今回のケースにおいては source:stepfunction というタグがログに付与さえされていれば期待動作が得られそうですね。

ということは、タグの付与とログの転送を担ってくれている Datadog Forwarder がサボっているのでは?ということで、普段のヴィエス・オカモッティなら即サポートに泣きつかせていただくところなのですが、この時はちょっと魔が差したのでしょうか。切り分けできるところまで切り分けてみて、ダメなら問い合わせてみようというモチベーションが湧き、コードは書けないけど Python ならぼんやり読めるのでは……と勇気を振り絞り、コードの奥地に潜り込んでみることにしました。

https://github.com/DataDog/datadog-serverless-functions

コード本体はこちらのリポジトリで運用されているので、まずはどこでこのタグ付与が行われているのかを探るため、タグとして付与されることが期待されている stepfunction という文字列でコード検索をしてみます。

https://github.com/DataDog/datadog-serverless-functions/blob/b259452608a35702bfdc911d291a38d238e89feb/aws/logs_monitoring/steps/enums.py#L139

あっ、なんかそれらしい箇所がすぐ見つかりました。なるほどこの箇所で CloudWatch Logs のロググループ名から SFn のログであると判別できるようにすべく、繰り返しの引用となりますがドキュメントで以下の注釈が明記されていたわけですね。

注: Datadog がログのソースを識別し、自動的にパースするために、CloudWatch のロググループのデフォルトのプレフィックス /aws/vendedlogs/states を使用します。

ではなぜ適切なロググループ名で CloudWatch Logs に出力させているにも関わらず、 source タグを付与してくれないのでしょう?

この段階でようやく、そういえば今コード検索で発見したのはあくまで最新の Datadog Forwarder であって、弊社環境で動作させている Datadog Forwarder はそういえば導入時点からそのままありがたく使い続けているな……と思い至り、弊社環境で動作しているバージョンのコードを同様に検索してみたところ……そもそもコード全体において step という単語自体が全くヒットしないという結果になりました。おやおや。

結(Datadog Forwarder は最新化すべし)

あとはもうオチへと突き進むだけです。
https://docs.datadoghq.com/ja/logs/guide/forwarder/?tab=terraform#新しいバージョンにアップグレードする
ドキュメントの記述に従い、 CloudFormation Stack を Datadog が用意してくれている最新のテンプレートを参照させる形で更新します。

Update CloudFormation

すると、 Datadog Forwarder のコードが最新化され…
Log Explorer

期待通り、対象のログを SFn のログとして判別してくれた上で、用意された手厚いパース定義に基づいて非常に美しい形でログをインデックスしてくれるようになりました。よかったですね。

またこれは完全に予期せぬ収穫でしたが、関数のバージョンアップ前後で Duration を確認したところ、以下の通り目に見える形で処理時間の改善を確認することもできました。コードの差分を確認する限りでもかなり大胆な構成変更が加わっていたことが見て取れましたので、バージョンアップによって機能追加だけではなく処理の最適化も実現されていたものと推測されます。
Duration

この結果を踏まえると、 Datadog Forwarder は最新化すべし、という結論が導き出せそうですね。お疲れさまでした。

おわりに

そもそもハマった根本原因として、それまで問題なく使えていたことを口実に Lambda 関数のバージョンアップを怠っていたことであるためお恥ずかしい事例ではあるのですが、敢えてご紹介させていただきました。ハマりを解消できたことは良かったものの、 Lambda 関数のランタイムの寿命という観点に置いても、然るべきタイミングでのバージョンアップ作業は怠るべきではないと反省した次第です。

物量に反してあまり中身の無い記事になってしまった印象があり心苦しいのですが、なるべくどなたにも読み進めていただける内容を目指した結果ということで、ご寛恕いただけますと幸いです。

いかがでしたか? 次回の記事もお楽しみに!
ここまでお読みいただき、ありがとうございました。

脚注
  1. 様々なアプリケーションが様々なフォーマットで送りつけてくる JSON ログを、その出力ソースを判別した上で適切にパースしてくれる機能 ↩︎

  2. 一般的なログと異なり AWS サービスがネイティブに発行するログとして従量割引価格で取り扱えるログ種別 ↩︎

Discussion