DatadogでCIを可視化する
概要
- この記事は Datadog Advent Calendar 2021 5日目の記事です
- CIのテストがコケる問題の調査の一環で行った対応のメモです
この記事のターゲット
- Datadogを導入している方
- これからCIのモニタリングをやっていくぞという方
あらすじ
Railsで書かれたとあるWebサービスがあり、それはCircleCIでビルド〜デプロイを行っています。
ある日、CIのテストが落ちまくってるから見てくれという相談がありました。
失敗していたCIのジョブを見てみたところ、yarn runの箇所で静かにkillされていました。
yarn run {なんかバージョン指定}
$ webpack --config ./config/webpack/production.js
Browserslist: caniuse-lite is outdated. Please run next command `yarn upgrade`
Received "killed" signal
恐らくメモリ不足であろうという事から、暫定対応としてCircleCIのresource_classをmedium+からlargeに変更し、NODE_OPTIONSに下記を設定することで現象を回避できました。
environment:
+ NODE_OPTIONS: --max_old_space_size=4096
- resource_class: medium+
+ resource_class: large
本題
恐らくで片付けちゃって大丈夫か?ちゃんとトレースできてないけど本当に大丈夫か?と考えてみて、ちゃんとCIのメトリクスも見ていく必要があるよねという事で下記の対応を行いました。
1. DatadogのCircleCI Integration
DatadogはCircleCIのIntegrationを有効にする事で下記のメトリクスを取得することが可能になります。CircleCIのInsightsで見れる情報ですね。
メトリクス名 | 内容 |
---|---|
circleci.completed_build_time.sum | (count)Total build time of completed (not canceled) builds (Shown as millisecond) |
circleci.completed_build_time.avg | (gauge)Average build time of completed (not canceled) builds (Shown as millisecond) |
circleci.finished_builds.count | (count) Count of all finished builds(Shown as build) |
circleci.completed_builds.count | (count)Count of all completed (not canceled) builds (Shown as build) |
これにより、まずはビルドのジョブ単位での実行時間とビルドの成功状況をDatadogにて可視化することができました。
Datadog Dashboard for CI Metrics
ジョブ別の平均実行時間
とあるリポジトリ名(repo_name)のビルド時間をジョブ(job_name)単位で表示しています。
# Datadog Metrics
autosmooth(avg:circleci.completed_build_time.avg{repo_name:xxxxxxx} by {job_name})
ビルドの成功状況
とあるリポジトリ名(repo_name)の実行結果(outcome)単位でカウントしています。
# Datadog Metrics
autosmooth(avg:circleci.finished_builds.count{repo_name:xxxxxxx} by {outcome}.as_count())
2. CIのジョブ → Datadog Logs
CircleCI Integrationで全体を俯瞰することはできるようになりました。
しかし今回問題となったExecutorのメモリ不足と思われるメトリクスを得ることはまだできていません。
もしかしたらCircleCIの仕組みとして良い感じの何かがあるのではと思ってサポートに問い合わせてみたところ、下記の記事を紹介してくれました。
使っていたExecutorがDockerだったので、下記のようにアレンジにしてジョブ単位のメモリ使用状況を取得してDatadog Logsに送信する処理を追加しました。(API リファレンス > ログ)
# Jobのmemory使用量を取得 & Datadog Logsに送信
input_datadog:
parameters:
job_status:
type: enum
enum: [check_start, check_end]
steps:
- run:
name: 'check memory usage'
command: |
max_usage_in_bytes=`cat /sys/fs/cgroup/memory/memory.max_usage_in_bytes`
curl -X POST "https://http-intake.logs.datadoghq.com/v1/input" \
-H "Content-Type: text/plain" \
-H "DD-API-KEY: ${DD_API_KEY}" \
-d "{\"job_status\":\"<< parameters.job_status >>\",\"branch\":\"${CIRCLE_BRANCH}\",\"PR\":\"${CIRCLE_PULL_REQUEST}\",\"Job\":\"${CIRCLE_STAGE}\",\"ddsource\":\"circleci\",\"service\":\"circleci\",\"memory.max_usage_in_bytes\":\"${max_usage_in_bytes}\"}"
when: always
これにより、CIのジョブのメモリ使用状況がDatadog Logsで確認できるようになりました。
Memory Usage in Datadog Logs
3. Datadog Logs → カスタムメトリクス
Datadog Logsからカスタムメトリクスを生成することができます。
必要な材料は既に揃っているので、memory_usageをMeasureにしてこんな感じでメトリクスを生成します。
カスタムメトリクスの生成
これによりジョブの開始前後のメモリ使用状況が可視化され、現在のresource_classで大丈夫なのか、ジョブのメモリ使用量は増加傾向にないかをチェックすることができるようになりました。
Datadog Dashboard for CI Metrics
ジョブ別のメモリ使用状況
CircleCIのメモリ使用状況(memory_usage)をジョブ(job)単位でカウントしています。
# Datadog Metrics
avg:circleci.memory_usage{*} by {job}
まとめ
- DatadogのIntegrationを活用してCIのメトリクスも取得していきましょう
- Integrationで取得できないものはLogsに飛ばしてからカスタムメトリクスにしましょう
余談
というのを実装した後でDatadogがContinuous Integration Visibilityという新機能をリリースしていました🎉(記事執筆時の 2021/12/03 時点ではまだベータ)
今後はそちらを使うのがスマートかと思いますので、使ってみた方は使い勝手をこっそり教えて下さい。
余談2
12/8のCircleCIのリリースによりジョブのCPU使用率とメモリ使用率が確認できるようになりました!
メモリ不足で失敗したジョブの例
DatadogのIntegrationでこの値を取得できるのかは確認中ですが、本記事の当初の目的であった「ジョブのエラーがメモリ不足に起因しているかどうか」を切り分けるために活躍してくれそうです。
Discussion