@kyanny's blog

My thoughts, my life. Views/opinions are my own.

mackerel で cron ジョブの所要時間を監視するアイデア

cron ジョブを監視したい。監視したい項目は以下。

  1. cron ジョブの実行が開始されたかどうか
  2. cron ジョブが正常終了したかどうか
  3. cron ジョブの所要時間

このような課題を解決するソリューションとして Dead Man’s Snitch を使ったことがあるが、他にも複数の類似サービスが存在する。 Cron Job Monitoring というジャンルのようだ*1

Dead Man’s Snitch は 1 の監視に主眼をおいたサービス(だと思う)。 Cronitor3 の監視もカバーしているっぽい(が試してはいない)。他のサービスもおそらく似たようなものだろう。

cron ジョブ監視専門のサービスを導入するのも良いが、 mackerel で実現できないか、と思ったので考えてみた。

1 と 3 は、

  1. cron ジョブの所要時間を計測してサービスメトリックとして投稿する
  2. そのメトリックに対する監視ルールとしてサービスメトリック監視を作成し、サービスメトリックの途切れ監視を有効化する

という組み合わせで監視できる。

2 はmkr wrap コマンドで監視できる。 cron ジョブとして実行するアプリケーションの実装次第で Sentry のようなエラー監視システムに通知することもできる(そうした方が良いだろう)。

試してみた設定やプログラムは以下。 Production 環境で運用する場合は、計測とメトリック投稿を担う wrapper コマンドを用意したり、 cron ジョブの「突き抜け」が発生しないようにロックをかけたりする必要があるだろう。

crontab

* * * * * /usr/bin/time -f '\%e' -o /tmp/elapsed.txt mkr wrap -- ruby cronjob.rb >/tmp/cronjob.log 2>&1; ruby metric.rb

cronjob.rb

ランダムに数十秒かかるコマンド、ということでこんな風にした。余談だがdd(1) で空ファイルを作るときは /dev/zero より seek - flatlineの日記らしい。

#!/usr/bin/env ruby
exec "dd if=/dev/zero of=/tmp/a.dat bs=1MB count=#{[1,1,2,3,5].sample}K"

metric.rb

#!/usr/bin/env ruby
require 'json'
elapsed = File.read('/tmp/elapsed.txt').to_f.ceil
data = [{name: "Cronjob.elapsed", time: Time.now.to_i, value: elapsed}]
File.write('/tmp/metric.json', data.to_json)
apikey = File.read('/etc/mackerel-agent/mackerel-agent.conf').scan(/^apikey\s+=\s+"(.*)"$/)[0][0]
system %!curl https://api.mackerelio.com/api/v0/services/test/tsdb -H 'X-Api-Key: #{apikey}' -H 'Content-Type: application/json' -X POST -d @/tmp/metric.json!

サービスメトリックのグラフの例。縦軸の単位は秒。

f:id:a666666:20200819021418p:plain

アラート画面の例。

f:id:a666666:20200819022218p:plain


1 と 2 は同じことだともいえるし、違うことだともいえる。 Cron Job Monitoring サービスは * * * * * command && curl https://example.com/a78e9dd/complete のように、コマンド実行後に所定の URL へリクエストを送信することで cron ジョブの正常終了を通知する API インタフェースを用意している。これは簡単に使えるが、 && を使うので 1 と 2 の監視を厳密に区別できない(ジョブが kick されなかったことと、 kick されたが異常終了したことの差がわからないはず)。 mkr wrap は 2 の監視をより精緻に行う方法だといえる。

所要時間を監視したい動機は、例えば外部システムと通信するプログラムなど、所要時間の見積もりがしづらい場合がある、というのが一つ。次に、データ量に応じて徐々に所要時間が伸びる場合がある、というのが一つ。いずれも実行開始から一定時間を過ぎたら graceful shutdown させる仕組みを用意した方が良いが、そこまで作り込むのか?という論点はあるし、所要時間を減らすチューニングをするにせよ、 cron の実行間隔を伸ばすにせよ、一回の実行にどのくらい時間がかかるのか?を計測してからの方が良い解決方法を判断できるのは間違いない。

*1:正直、ジャンルがあるほど盛り上がってるとは思わなかった