こんにちは。 コドモンの決済推進チームの杉山です。
この記事では私が所属している決済推進チームでどのようにDatadog APMを入れたかを紹介します。 Datadog APMを入れた経緯などはこの記事では触れません。
導入時にこちらのガイドを参考にしました。 このガイドにはSpringBootで動かす場合の説明などがありませんでした。また各データの紐付けなど少し調べる必要があったので、誰かのためになるかと思いで記事にしています。
この記事では以下の説明をします。
- Datadog Agentを用いて、アプリケーションに関する情報をDatadogのAPMで見れるようにする
- SpringBoot(Kotlin)で動いているアプリケーションのそれぞれのリクエストトレースやログ・スパンデータを関連付ける
- ECSで動いているコンテナのモニタリング情報
- fluentbitを使いlog_router経由でdatadogにログを送信する
イメージを掴むために先に今回の記事で導入したDatadog Agentを入れた場合の構成図を貼っておきます。ECS上にサイドカーとしてDatadog AgentとLog Routerのコンテナを作成し、Datadogにデータを送信しています。
構成
- アプリケーション: Kotlin + SpringBoot
- インフラ: AWS ECS
- デプロイツール: ecspresso
アプリケーションに関する情報をDatadogのAPMで見れるようにする
APMで見れるようにするためにはメトリクスをDatadogに送信してくれるAgentをアプリケーションに入れる必要があります。
公式ドキュメントではインスツルメントするために、以下の対応が必要とあります。
IDE、Maven または Gradle アプリケーションスクリプト、java -jar コマンドから、Continuous Profiler、デプロイ追跡、ログ挿入(Datadog へログを送信する場合)を使用してアプリケーションを実行するには、-javaagent JVM 引数と、該当する以下のコンフィギュレーションオプションを追加します。
java -javaagent:/path/to/dd-java-agent.jar -Ddd.profiling.enabled=true -XX:FlightRecorderOptions=stackdepth=256 -Ddd.logs.injection=true -Ddd.service=my-app -Ddd.env=staging -Ddd.version=1.0 -jar path/to/your/app.jar
以下のアプリケーションコンテナの設定でも書きますが、起動時にJAVA_TOOL_OPTIONS
を使うことで説明にある-javaagent
を指定することができ、アプリケーション起動時にDatadog Agentも一緒に起動することができます。
私のチームではecspressoでデプロイしておりecspresso実行前にアプリケーションのDocker Imageを作成する必要があります。JAVA_TOOL_OPTIONS
に渡しているdd-java-agent.jar
を実行するには、Docker Imageにdd-java-agent.jar
を一緒にビルドする必要があります。
ワークフローで起動時に必要なDatadog AgentをImageに入れる
ビルドはgithub actionsで行っており、以下のようなワークフローでImageを作成しています。
# .github/workflows/build-and-push-gradle-with-datadog.yml name: __ build and push gradle app with Datadog agent # このワークフローは、他のワークフローから実行されます # 説明に必要な部分のみ記載しています on: workflow_call: inputs: environment: required: true type: string commit-hash: description: Commit hash required: true type: string image-tag: description: Docker Image Tag required: true type: string source-directory: description: Source code directory relative to project root required: true type: string gradlew-bootbuildimage-options: description: gradlew bootBuildImage Options required: false type: string default: '' datadog-dir: description: Datadog Directory required: true type: string jobs: build-and-push: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: ref: ${{ inputs.commit-hash }} # APM用のJavaエージェントをcurlで取得し、対象のディレクトリ(src/main/resources)に保存。 - name: Configure Datadog run: | [ ! -d ${{ inputs.datadog-dir }} ] && mkdir -p ${{ inputs.datadog-dir }} curl -L 'https://dtdg.co/latest-java-tracer' -o ${{ inputs.datadog-dir }}/dd-java-agent.jar # ...Gradleのセットアップのstep # src/main/resourcesに配置されたdatadogのJavaエージェントを一緒にビルドすることで起動時にJAVA_TOOL_OPTIONSで指定できるようになる - name: Build and Push Image working-directory: ./${{ inputs.source-directory }} env: GRADLEW_OPTIONS: ${{ inputs.gradlew-bootbuildimage-options }} run: | ./gradlew bootBuildImage --imageName {repository_uri}:${{ inputs.image-tag }} $GRADLEW_OPTIONS docker push {repository_uri}:${{ inputs.image-tag }}
SpringBootアプリケーションでは、デフォルトでsrc/main/resources
ディレクトリ配下のファイルはビルド時にJAR/WARファイルにバンドルされます。
そのため、このワークフローのようにcurlでDatadog Agentを取得し指定の場所に置くことでImageに入れることができます。
このワークフローを以下のように各サービスが利用します。
build-and-push-public-api: uses: ./.github/workflows/build-and-push-gradle-with-datadog.yml with: // ...必要な変数 datadog-dir: app/src/main/resources/datadog
これでアプリケーションの起動までに必要な準備は整いました。
次にAPMの設定を入れていくためにタスク定義の設定ファイルを修正します
アプリケーションコンテナのecspressoのタスク定義
{ "containerDefinitions": [ { "name": "{{ must_env `ECS_SERVICE_NAME` }}", "image": "{{ must_env `IMAGE` }}", "environment": [ { "name": "ENV", "value": "{{ must_env `ENV` }}" }, { "name": "JAVA_TOOL_OPTIONS", "value": "-javaagent:/workspace/BOOT-INF/classes/datadog/dd-java-agent.jar ...その他の設定" }, { "name": "DD_ENV", "value": "{{ must_env `ENV` }}" }, { "name": "DD_LOGS_INJECTION", "value": "true" }, { "name": "DD_SERVICE", "value": "ここにサービス名" }, { "name": "DD_TRACE_SAMPLE_RATE", "value": "1.0" }, { "name": "DD_VERSION", "value": "{{ must_env `IMAGE_TAG` }}" }, { "name": "DD_GIT_REPOSITORY_URL", "value": "ここにリポジトリURL" }, { "name": "DD_GIT_COMMIT_SHA", "value": "{{ must_env `DD_GIT_COMMIT_SHA` }}" } ] } ] }
設定はこのようになっています。must_envはecspressoのテンプレート機能で、環境変数から値を取得することができます。各環境によって変わる情報にはこれを使用します。
それぞれのDatadogのenvironment(DD_から始まる環境変数)に関する説明します。公式ドキュメントが複数にまたがっていたので、それぞれの説明にはドキュメントから引用をします。
DD_ENV
アプリケーション環境をトレースとログに設定するために使用されます。prd・stgなどの値を取ります。
DD_VERSION
アプリケーションのバージョンを指定します。私のチームではIMAGE_TAGを指定しています。アプリケーションではバージョンではなくCommit hashを使うことが多く、IMAGE_TAGはCommit hashを使ったタグ名になっているのでIMAGE_TAGを指定することで検索時にCommit hashからログなどを検索することができます。
DD_SERVICE
同一のジョブを実行するプロセスセットの名前。アプリケーションの統計のグループ化に使われます。
画面上にServiceとして表示されます。
DD_LOGS_INJECTION
Datadog のトレース ID とスパン ID に対する MDC キーの自動挿入を有効にします。
ログとトレースの相関付けを有効化します。trueに設定するとログにトレースIDとスパンIDが自動挿入され、Datadog上でログとAPMトレースを連携させることが可能になります。
DD_TRACE_SAMPLE_RATE
サンプリングレートを設定します。
デフォルトでも1.0です。1.0は全量を取得します。0.0〜1.0の範囲で指定します。例として設定していますが、ここの値はトラフィック量に応じて調整すると良いと思います。
DD_GIT_REPOSITORY_URL・DD_GIT_COMMIT_SHA
これらはテレメトリーを Git リポジトリと連携させ、関連するソースコード行にアクセスしてスタックトレースやスロープロファイルなどの問題をデバッグすることができます。 設定しておいて損はないと思います。
以下は参考にしたドキュメントです。
注意点
タスク定義のCPUとメモリの設定に関して注意していただきたい点があります。
私のチームではアプリケーションコンテナの設定を元々以下のようにしていました。
CPU:0.5vCPU
メモリ:2 GB
Datadog APMを導入した際に同じ状態の設定でリリースした際に、アプリケーションコンテナが起動しなかったり、起動しても落ちてしまうような事象がありました。そのため私のチームではDatadog Agentを入れる際は最低でも以下の設定にすることにしました。
CPU:1vCPU
メモリ:4 GB
テストではうまく動いたとしても、リリース時にAgentがCPUに影響を及ぼす可能性があるので導入時にはここの設定に気をつけながらリソースをちゃんと監視してリリースできると良いと思います。
fluentbitを使いlog_router経由でdatadogにログを送信する
公式ドキュメントとほとんど同じになりますが、設定は以下になります。
... [FILTER] Name grep Match * Exclude health check Exclude user_agent Datadog Agent/* [OUTPUT] Name datadog Match * Host http-intake.logs.datadoghq.com TLS on compress gzip apikey ${ここにAPI_KEY} dd_service ${SERVICE_NAME} dd_source java dd_tags env:${ENVIRONMENT},version:${IMAGE_TAG} ...
ここで設定されているSERVICE_NAMEは先ほどAPMで設定したものと同じものを設定する必要があります。そうすることでAPMのLogsのタブで見ることができるようになります。
また、必要に応じてFilterで不要なログを落としてください。私のチームの設定ではヘルスチェックとDatadogAgentに関するログは落とすようにしています。
これで、ひと通りの設定は完了です!
別途必要だった対応
ログとトレースの紐付け🔗
諸々の設定を入れた後APMやログが見れるようになりましたが、リクエストトレースとログが紐づいていないような状態でした。画面(APM > 対象のサービス > 画面上のTraces )から対象のトレースを選択し、Logsのタブを開いた際にNo logs foundと表示されてしまいます。
調べたところこちらの記事にあるようにログとtrace_idを手動で紐付ける作業が必要でした。
なので、画面(Logs > 右上のLog Configuration > Pipelines > pipelinesのPreprocessing for JSON) から設定を編集し、以下を追加の対応をしました。
- Trace Id attributesにcontext.dd.trace_idを追加
- Span Id attributesにcontext.dd.span_idを追加
このようにすることでリクエストトレースのLogsタブでそのリクエストに紐づいたログを見ることができるようになりました。
ログのレベルが全てINFOになってしまうℹ️
{ "container_id": "000000000", "container_name": "app-api", "timestamp": "1234567890", "Environment": "dev", "source": "stdout", "log": { "level": "ERROR" } }
上記のログのように情報は正しく出力できていますが、画面上では全てのログがINFOログ(青色のログ)として表示されていました。
こちらにあるように、前提としてDatadogのログレベルはstatus属性として保持しています。
このデフォルトの
status
は、ログ自体に含まれる実際のステータスを必ずしも反映しているとは限りません。
このようにログの情報から自動で判定してくれる場合もありますが、今回はDatadogがログのレベルを判定するのに必要な情報が付与されておらず、log.levelをログのレベルとして認識できていないことが問題でした。
そのためlog.levelをログのレベルとして認識させる設定変更が必要でした。
Status Remapperの機能を使用してlog.levelをログレベルとして認識されるように調整しました。
Status RemapperはLogs > 右上のLog Configuration > Pipelines から作成することができます。