Classi開発者ブログ

教育プラットフォーム「Classi」を開発・運営するClassi株式会社の開発者ブログです。

Amazon EventBridge(CloudWatch Events)で動かしているバッチをDatadogで監視する仕組みを構築した話

開発本部プロダクト開発部 認証連携チームでエンジニアをしている、id:ruru8net です。

これはClassi developers Advent Calendar 2021の9日目の記事です。
昨日の記事はこちらです。 Hardening 2021 Active Fault 参加レポート - 桐生あんずです

以前のClassi Advent Calender 2019では新卒が入社半年で社内サービスをリリースしてエンジニア楽しいってなったお話を書かせていただきましたが、あれから2年の間に業務の中で様々な経験をし、さらに知識やスキルを身につけていくことができました。

今日はその中でも自分が担当しているサービスの、バッチ監視の仕組みを考えたので紹介させてください。

背景

担当チームでは毎日深夜2時にDBからデータを削除するバッチを動かしています。
他にも社内では様々なバッチが動いていますが、これらを監視する仕組みは社内で確立されていませんでした。
そのためサービス稼働に影響の少ないバッチは実行中に問題があったり、そもそも実行されていなかったりしても検知されず、見過ごされてしまうことが多かったです。
弊社ではサービスの監視にDatadogを使用しているため、この監視体制にそのままバッチの監視を組み込むことでバッチの監視ができていない状態を是正したいと考えました。

前提

バッチファイル

Ruby on Railsを使い、rake taskとして実行させています。

バッチの仕組み

Amazon EventBridgeにてECS Fargateのタスクを起動させ、実行しています。これは既にdatadog-agentコンテナが動いている前提です。datadog-agentコンテナの設定方法は以下のURLを参考にしました。

https://docs.datadoghq.com/ja/integrations/ecs_fargate

f:id:ruru8net:20211208124211p:plain
バッチの構成図

使用する監視、通知ツール

  • Datadog
  • Slack

監視したいこと

バッチ実行において監視したいことは以下です。

  1. 定期的な実行の成功と失敗

    • バッチ実行中に例外が発生した場合の検知
    • バッチ実行用のタスクの起動自体がされなかった場合の検知
    • 例外を発生せずに何らかの理由でバッチ実行のコンテナが終了してしまった場合の検知
  2. 実行時間の異常

今回は定期的な実行の成功と失敗をメインとして、

  • バッチ実行中に例外が発生した場合の検知

    • →発生した例外をDatadog Eventとしてエラーを送信。DatadogのMonitorにてエラー通知を監視するMonitorを作成しエラーが送られてきた場合はslackにアラートを送信する。
  • バッチの起動自体がされなかった場合の検知

    • →バッチ実行の成功をDatadog Eventとして送信。DatadogのMonitorにて成功通知を監視するMonitorを作成し、成功通知が送られてこなかった場合はSlackにアラートを送信する。

という監視の仕組みを作っていきます。

f:id:ruru8net:20211208124333p:plain
バッチ実行を監視する仕組み

手順

1. dogstatsd-rubyを使ってバッチのスクリプトファイルにDatadogへEventを送信するよう書く

DatadogにEventを送信する方法は4つあります。

docs.datadoghq.com

  • Custom Agent Check
  • DogStatsD
  • Email
  • Datadog API

今回のようにsidecarコンテナとしてdatadog-agentを起動させているのであればDogStatsDを使ってEventを送るのがやりやすいと思います。

今回はRubyで書いているので基本的にはDatadogのドキュメントに書いてあるExampleと、使用するgemであるdogstatsd-rubyのドキュメントを参考にコードを書きました。 docs.datadoghq.com github.com

▽作成したバッチのスクリプトファイル

require 'datadog/statsd'

task batch: :environment do
  begin
    begin
      # バッチの実行時間を計測
      execution_time = Benchmark.measure do
        ###
        # 実行処理内容は省略
        ###
      end

      statsd = Datadog::Statsd.new(logger: logger, single_thread: true, buffer_max_pool_size: 1)
      begin
        # バッチの実行が完了したら成功Eventを送る
        statsd.event(
          'データを削除するバッチ', # Eventのタイトル
          "バッチ実行時間 #{execution_time.real}s", # 好きな内容をメッセージとして送れる
          alert_type: 'success',
          tags: ['env: development', 'service:rails-app'] # タグを指定
        )
      rescue => e
        logger.error e
      ensure
        statsd.close()
      end
    rescue => e
      begin
        # バッチ実行中に問題が発生した場合はエラーEventを送る
        statsd = Datadog::Statsd.new(logger: logger, single_thread: true, buffer_max_pool_size: 1)
        statsd.event(
          'データを削除するバッチ',
          "#{e.class}:#{e.message}",
          alert_type: 'error',
          tags: ['env: development', 'service:rails-app']
        )
        logger.info 'Datadogへのエラー通知送信完了'
      rescue => e
        logger.error e
      ensure
        statsd.close()
      end
    end
  end
end

def logger
  Rails.logger
end

解説

statsd = Datadog::Statsd.new(logger: logger, single_thread: true, buffer_max_pool_size: 1)

statsdのインスタンスを作成します。
dogstatd-rubyのバージョンや必要に応じてオプションをつけてください。

https://github.com/DataDog/dogstatsd-ruby#migrating-from-v4x-to-v5x https://www.rubydoc.info/github/DataDog/dogstatsd-ruby/Datadog/Statsd

begin
  # バッチの実行が完了したら成功Eventを送る
  statsd.event(
    'データを削除するバッチ', # Eventのタイトル
    "バッチ実行時間 #{execution_time.real}s", # 好きな内容をメッセージとして送れる
    alert_type: 'success',
    tags: ['env: development', 'service:rails-app'] # タグを指定
  )
rescue => e
  logger.error e
ensure
  statsd.close()
end

eventメソッドが取れるパラメータやオプションはこちらに書いてあります。 www.rubydoc.info

またドキュメントに書いてある通り、DogStatsDのクライアントが不要になった時には適切に破棄をするためにstatsd.close()します。

2.バッチを走らせてeventがDatadogに送られているかを確認する

https://app.datadoghq.com/event/stream にてeventの一覧が確認できます。 左上の検索欄に、eventのタイトルやタグで検索ができます。
このときの検索で、eventが一意に絞り込めるようなタイトル、タグをつけるようにしてください。

またメッセージの内容も一緒に出力されます。
ですので、ここに実行時間や、実行完了したときに欲しい情報を出力させておくと確認がしやすいです。

event例

状態
成功時
f:id:ruru8net:20211208124846p:plain
成功のevent
例外発生時
f:id:ruru8net:20211208124740p:plain
エラーのevent

3. Datadog Monitorを作成する

Monitors > + New Monitor > Event を選択します。
するとMonitor作成画面になります。今回は「バッチ実行中に例外が発生した場合の検知」と「バッチの起動自体がされなかった場合の検知」をする2つのMonitorを作成します。

バッチ実行中に例外が発生した場合の検知

エラーeventのみを絞り込むように設定し、alert conditionsをセットします。今回は24時間に一回動くバッチのため、24hoursを選択、また1つでもエラーeventを受け取ったらalertとして発火させたいのでAlert Thresholdを1にしています。

f:id:ruru8net:20211208125012p:plain
エラーeventを受け取った時にalertを発砲するDatadog Monitor 作成画面

バッチの起動自体がされなかった場合の検知

成功eventのみを絞り込むように設定します。
またeventをカウントする期間を24hoursにしてしまうと、前回のeventからきっかり24時間以内にeventが来ないとalertとなってしまうので、余裕を持たせるために25hoursにしておきます。
対象期間1つも成功eventがない場合はバッチの起動がされなかったとみなしalertを送るように、Alert Thresholdを1にします。

f:id:ruru8net:20211208125343p:plain
成功通知がない場合にalertを発砲するDatadog Monitor 作成画面

③で通知させたい先のslackチャンネル(slack-{チャンネル名}となっているもの)を選択します。
(DatadogとSlack連携のセットアップはこちら
https://docs.datadoghq.com/ja/integrations/slack/?tab=slackapplicationus)

④ではslackに投稿する際のテンプレートを作成します。
ここでは色々な変数やMarkdownが使えます。

4. Monitorで設定した通りにslackに通知が来ることを確認

Monitor作成時の右下にあるTest Notificationsで確認ができます。

f:id:ruru8net:20211208125518p:plain
Test Notifications

下のようにSlackに通知が送られるようになりました。

バッチ実行中にエラーが発生した場合の通知 バッチの実行確認ができなかった場合の通知
f:id:ruru8net:20211208125610p:plain
f:id:ruru8net:20211208125906p:plain

おわりに

実装について

DogstatsDによるEvent送信はバッチ処理中への埋め込みがしやすくとても使いやすかったです。
またバッチに限らず監視の仕組みを考える時にはまず、「何を監視したいのか」を整理するのがとても大事だなと思います。
今回は実行の監視のみしかできていませんが、今後は実行時間がかかり過ぎていた場合にalertを発報できるような仕組みも監視の項目に入れていきたいです。
(現状はeventのメッセージに対してMonitorを作成する方法が見つからず、別の方法を模索中です。)

監視の仕組み構築について

社内で確立されていなかったバッチの監視に対して、この仕組みを社内展開することができ、他のチームの人たちからも喜んでいただけたので嬉しかったです。
自分のチームだけでなく他のチームにとっても役に立つような仕組みづくりというのを意識して今後も頑張っていきたいです。

明日のClassi developers Advent Calendar 2021の担当はTomoya Namekawaさんです。お楽しみに。

© 2020 Classi Corp.