Google Cloud Run
Datadogが大規模なクラウドのモニタリングサービスをリードします。
https://docs.datadoghq.com/ja/serverless/google_cloud_run/
Photo by Rafaëlla Waasdorp on Unsplash
こんにちは。ウォンテッドリーで Visit の開発をしている市古(@sora_ichigo_x)です。
先日、学習目的で Cloud Run※1 インスタンスへの Datadog※2 導入を検証する機会がありました。その過程で、プロダクト開発では普段意識することが少ない「どうやってサーバーから Datadog にデータ送信されているのか」という仕組みに触れることができました。
ウォンテッドリーでは長らく Datadog を活用しているので、業務に活かせる学びとして Cloud Run から Datadog にデータ送信した話をまとめようと思います。
※1 Google Cloud が提供する完全マネージドなコンテナ実行環境
※2 インフラストラクチャ、アプリケーション、ログ、トレースなどを統合的に監視・分析できる SaaS プラットフォーム
ログ・メトリクス・トレースの違い
1. ログ:テキストとして出力される情報
2. メトリクス:数値で表現される時系列データ
3. トレース:リクエストの流れを追跡するデータ
Cloud Run のケース:Agent をおけない環境でどう送るか
serverless-init コンテナを Cloud Run サイドカーで動かす
アプリケーションの構成
補足:Cloud Run サイドカー登場以前のアプリケーション構成
余談:実際に動かした時の話
前提として、Datadog に送るデータには大きく3種類あります(参考:What Is Observability? | Datadog)。それぞれ送信経路が異なるため、まずはこれらの違いを明確にしておきます。特に Cloud Run では、ログは GCP が吸い上げてくれるものの、メトリクス・トレースにはサーバーと Datadog の間に何らかの Agent が必要になります。
ログとは、アプリケーションが stdout や stderr に出力するテキストベースの情報です。リクエストごとの処理結果やエラー、デバッグ用の出力など、開発中には最も目にすることが多い種類のデータです。
Cloud Run の場合、これらのログは Cloud Logging に自動的に送られます。そして、Datadog との連携が設定されていれば、Cloud Logging 経由で Datadog に転送されます。
メトリクスは、CPU 使用率やレイテンシ、リクエスト数、キューの深さなど、数値で可視化できる情報です。
このようなメトリクスは、DogStatsD という仕組みを使って Datadog Agent(またはそれに準ずる何か)に UDP 送信されます。Datadog 本体に送信するのではなく、Datadog Agent が中継している点がポイントです。
トレースとは、1つのリクエストがサービス内やマイクロサービス間をどう流れたかを可視化するための情報です。Datadog の APM(Application Performance Monitoring) 機能では、このトレース情報をもとに「どの処理にどれだけ時間がかかっているか」「どこで遅延や失敗が発生しているか」といった分析ができます。
トレースは、各リクエストの実行区間を表す「スパン(span)」という単位で記録され、複数のスパンが関連づけられて1つのリクエスト全体の流れ(トレース)になります。このトレース情報は、主に以下のようなライブラリで生成されます。
これらは、Datadog Trace Agent に HTTP で POST されます。ここでも同様に、受け口となる Agent の存在が必要となります。
ここまでで、ログは Cloud Logging を経由して Datadog に届く一方、メトリクスやトレースは Agent が中継する必要があることを整理しました。
しかし Cloud Run はサーバーレス環境であり、シングルコンテナ = シングルプロセスの前提では長時間動作するプロセス (Agent) を常駐させることができません。そこで、今回は Datadog が提供する軽量 Agent である serverless-init を Cloud Run のサイドカーとして起動する方法を選択しました。
フルスペックの datadog/agent を Cloud Run サイドカーで動かすのは非推奨なので今回は触れません。
Cloud Run のマルチコンテナは同一のネットワーク名前空間を共有するため、アプリケーションが localhost に送信したデータは、同じインスタンス内の別コンテナである serverless-init が受け取ることができます。その後、serverless-init が Datadog API にデータを転送してくれることで、APM やメトリクスに可視化されます。
つまり、アプリケーション開発者としては「localhost に送るだけ」でよく、それを Datadog に中継してくれる仕組みが裏にあるという構造です。これは Cloud Run に限らず、ウォンテッドリーにおいては k8s の DeamonSet というリソースを用いて実現しています。
また、今回は Go の GraphQL サーバーを想定したため、トレーサーには github.com/DataDog/dd-trace-go/contrib/99designs/gqlgen/v2 パッケージを利用しています。
最終的には、次のように APM トレーシングを表示することができました。
ちなみに、Cloud Run がマルチコンテナ(サイドカー)構成をサポートするようになったのは比較的最近のことです(GAは 2023年5月)。
それまでは Datadog の serverless-init を使う場合、アプリケーションの起動スクリプトをラップする方式を取る必要がありました。しかしこの方式には serverless-init とアプリケーションがシングルコンテナで動くことで障害分離が十分にできないなどの問題がありました。
ENTRYPOINT ["/datadog/serverless-init", "--"]
CMD ["./myapp"]
現在では Cloud Run におけるマルチコンテナ構成の登場により、Datadog の連携部分をアプリケーションから切り離して、サイドカーとして独立させる構成が標準的になっています。
Cloud Run に Datadog を組み込むにあたっては、Terraform を自前で書いてもよいですし、Google Cloud Console で手動設定することもできました。ただし今回は DataDog/terraform-google-cloud-run-datadog という Terraform モジュールが活発に開発されていることを知り、使えそうだったのでこちらを導入してみました。
実際に使ってみると、Cloud Run コンテナに環境変数を設定する部分で value_source がうまく反映されないというバグに直面しました。App コンテナに必要な環境変数を Secret Manager 経由で渡す構成だったのですが、それが反映されず起動に失敗するという状況です。
これはモジュール内部の google_cloud_run_v2_service の記述に起因していたため、Claude Code を活用して原因を特定し修正 PR を作成しました。近年は AI を活用することで専門外の領域にも踏み込んでコントリビュートしやすくなっています。
私自身の対応が遅れてしまい、最終的には同様の問題に対する別の PR (hotfix) が先にマージされましたが、モジュールの改善につながったので良かったと思います。
ウォンテッドリーでは、一緒にプロダクト開発を行うエンジニアを募集しています。このブログに興味を持ってくれた方がいれば、ぜひお話ししましょう!