🤖

Cloud RunにDatadog Agentを導入してAPMしたい

2023/03/08に公開

SocialDog Growthチームエンジニアの中村です。
SocialDog Growthチームでは主にSocialDogプロダクト全体の機能改善やバグ修正、カスタマーサクセスの方からの問い合わせの調査、拡張機能の機能改善などを行なっています。

SocialDogでは今回、インフラやアプリケーションの監視のためDatadogを導入しました!
Datadogを導入することでみなさまにより安定したサービスの提供を行ったり、リリースを高速化しより早く価値が提供できるようにしたいと思っています。
Datadogの導入にあたり工夫した点やハマった点を共有しようと思います。

現時点(2023/3/8時点)ではDataDog公式で、フルマネージドのCloud Runをサポートしておりません。
Container Registryにはイメージが存在するため今回は独自にCloud RunサービスとしてDataDog Agentを構築しました。
※今後DataDog公式でサポートされればそちらの手順を使った方が良いかもしれません。

目的

Datadog AgentをCloud Runで動かして
別サービスとして動いているCloud RunからAPMを利用できるようにします。

上記の目的を達成するためインフラ構成の検討や実装を行いました。
その途中で課題があったので備忘録として書き起こします。

課題

同じプロジェクトでも、異なるCloud Runサービスは内部ネットワークで通信できない点が1つ目の課題となりました。
内部ネットワークで通信できないことから、今回の実装では、Ingressの制御を「すべて」としインターネットからのサービスに直接アクセスできるようにしました。

インターネットからサービスに直接アクセスできることからURLが他者に知られると誰でもtraceを送れてしまいます。
そこで、Datadog AgentのCloud Runサービスは認証が必要となるようにし、traceの送信時にはサービス間認証を行なってリクエストを処理するような設計としました。

上記設計にすると2つ目の課題として
Datadog APMでtraceを送信する際にサービス間認証を行うことができるのかが課題となります。
この課題はid_tokenのライブラリを導入し、Datadog APMのtrace送信時にいい感じに認証を行うような実装を行いました。

方法

  1. インフラを設計する。
    下図のようなサービス構成を検討しました。
    インフラ構成図

  2. Cloud RunでDatadog Agentを構築する。

課題に対処するために以下のポイントを実装しました。

  • run.googleapis.com/ingressallにする。
    →インターネットからサービスに直接アクセスできる。
  • Cloud Run起動元(roles/run.invoker)に②のGolang Applicationのサービスアカウントを設定する。
    →Cloud Runを起動するために認証が必須となる。
resource "google_service_account" "datadog-agent-service-account" {
  project      = var.project
  account_id   = "datadog-agent-cloudrun"
  display_name = "datadog-agent cloudrun service account"
}

// 作成したサービスアカウントにシークレットマネージャーに記録されている秘匿情報を見る権限を与える。
resource "google_secret_manager_secret_iam_binding" "datadog-agent-service-account" {
  project   = var.project
  secret_id = "DD_API_KEY"
  role      = "roles/secretmanager.secretAccessor"
  members   = [
    "serviceAccount:${google_service_account.datadog-agent-service-account.email}"
  ]
}

resource "google_cloud_run_service" "datadog-agent" {
  name = "datadog-agent-cloudrun"
  metadata {
    annotations = {
      "run.googleapis.com/launch-stage" = "BETA"
      "run.googleapis.com/ingress"      = "all"
    }
  }
  template {
    spec {
      service_account_name = google_service_account.datadog-agent-service-account.email
      containers {
        image = "gcr.io/datadoghq/agent"
        env {
          name  = "DD_HOSTNAME"
          value = "hostname"
        }
        env {
          name  = "DD_APM_ENABLED"
          value = true
        }
        env {
          name  = "DD_APM_NON_LOCAL_TRAFFIC"  // 今回の場合は外部からAPMのトラフィックを受け取るためtrueに設定する。
          value = true
        }
        env {
          name  = "DD_API_KEY"  // API_KEYは秘匿情報のためシークレットマネージャーから取得するようにする。
          value_from {
            secret_key_ref {
              name = "DD_API_KEY"
              key  = "latest"
            }
          }
        }
        env {
          name  = "DD_SITE"
          value = "us5.datadoghq.com" // 契約しているリージョンを指定する。
        }
        ports {
          container_port = 8126 // ①ここで指定されたポートがCloud Runで公開される。
        }
      }
      metadata {
        annotations = {
          "run.googleapis.com/execution-environment" = "gen2" // gen2じゃないと謎のエラーが発生することがあるらしい。
        }
      }
    }
  }
}

data "google_iam_policy" "auth" {
  binding {
    role    = "roles/run.invoker"
    members = [
        "hogehoge@hogehoge.com" // ここにこのAgentを使うCloud Runのサービスアカウントを設定する。
    ]
  }
}

resource "google_cloud_run_service_iam_policy" "auth" {
  location    = google_cloud_run_service.datadog-agent.location
  project     = google_cloud_run_service.datadog-agent.project
  service     = google_cloud_run_service.datadog-agent.name

  policy_data = data.google_iam_policy.auth.policy_data
}
  1. Golang ApplicationにDatadog APMを導入する。

課題に対処するために以下のポイントを実装しました。

  • 環境変数DD_TRACE_AGENT_URLにDatadog AgentのURLを設定する。
    Cloud Runで実装されたDatadog AgentはhttpsプロトコルのURLが割り当てられます。
    tracer.WithAgentAddr(addr string)でもアドレスを指定できますが、今回は環境変数DD_TRACE_AGENT_URLでAgentのURLを指定することにしました。
    理由として、tracer.WithAgentAddr(addr string)で指定した場合、プロトコルがhttp固定となってしまったためです。ソースコード
    環境変数DD_TRACE_AGENT_URLの場合、プロトコルにhttpsも指定できます。ソースコード
  • Datadog APMからCloud Runのサービス間認証を行なってtraceを送信するため、google.golang.org/api/idtokenライブラリを使用しました。
    下記ソースコードのようにNewClientでHTTPClientを生成し、tracer.WithHTTPClient()に渡すことでサービス間認証を簡単に実装することができました。

以下、実装したソースコードを抜粋

import (
  "context"
  "google.golang.org/api/idtoken"
  "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
)

ctx := context.Background()

tracerHTTPClient, err := idtoken.NewClient(ctx, "Datadog AgentのURL")
tracer.Start(
    tracer.WithHTTPClient(tracerHTTPClient), // Tracerを送信する際に使うHTTPClient
)
defer tracer.Stop()

動作確認

Datadog Agentのログを確認

ちゃんとtraceを受信できていそう!

Datadogの画面を確認

ちゃんとtraceが表示されている!

まとめ

今回はCloud RunでDatadog Agentを構築しました。
Cloud Runで構築することのメリットとして、

  • サーバーの面倒をみなくてもいい。
  • 自動でスケールしてくれる。

があげられます。

今回はいろいろなはまりポイントにハマって時間がかかってしまいました。
いろいろな実験に付き合ってくれたチームメンバーのみなさんありがとうございました!

参考サイト

Datadog Agent on CloudRunによるGCPトレーサビリティの向上

サービス間認証

SocialDog TechBlog

Discussion