Perl Hackers Hub

第25回 cron周りのベストプラクティス(2)

この記事を読むのに必要な時間:およそ 4 分

crontabをテストする

cronはサービスの一部であり,crontabの内容も日々変化していくものです。指定の誤りがあった場合に大事故になる可能性があります。そういった性質上,crontabをテストすることは重要です。

テスト項目には次のようなものが挙げられます。

  • a.crontabにシンタックスエラーがないか
  • b.意図しない危険な指定がされていないか(うっかり毎分指定,日付と曜日の重複指定など)
  • c.ログを捨てていないか
  • d.規約に従って記述をしているか
  • e.コマンドに実行パーミッションが付いているか
  • f.指定コマンドやファイル名をtypoしていないか
  • g.実行時間のチェック(意図した時間に実行されるか,実行されたくない時間に誤って実行されないか)

これだけのテスト要求項目があり,それらを通さずに本番に反映してしまうことは危険です。

Parse::Crontabを使ったcrontabのテスト

crontabのテストには拙作のParse::Crontabが便利です。先ほど取り上げたテスト項目を,次のようなテストコードで一通りチェックできます。

use strict;
use warnings;
use Test::More;
use File::Which qw/which/;
use Parse::Crontab;
# 11 2 * * * $MYPROJ_RUN script/batch.pl \
# DailyPartition 2>&1 | $LOGGER cron.daily_partition -
# 上記のように指定されているか確認する

my $crontab = Parse::Crontab->new(
  file => 'data/crontab.txt'
);
# a crontab のシンタックスに誤りがないか
ok $crontab->is_valid
    or diag $crontab->error_messages;

# b 危険な指定の確認
ok !$crontab->warning_messages
  or diag $crontab->warning_messages;

# ジョブ一覧を取得する
for my $job ($crontab->jobs) {
  my $command = $job->command;
  # c $LOGGER でログを出力しているか
  like $command, qr!2>&1\s+\|\s+\$LOGGER!, 'logger ok';

  # d $MYPROJ_RUN を使う規約を守っているか
  ok $command =~ m!^\$MYPROJ_RUN!;
  my ($opt, $rest_command) =
    $command =~
    m!^\$MYPROJ_RUN\s*(*?)?\s*--\s*(.*)!;
  my $cmd = +(split /\s+/, $rest_command)[0];

  # e 実行権がちゃんと付与されているか
  ok -e $cmd or $cmd = which($cmd);
  ok -x $cmd;
  if (my ($module) =
    $rest_command =~
      m!^script/batch\.pl\s+([A-Za-z0-9]+)!
  ) {
    # f batch.pl への指定モジュールが存在するか
    my $module_file = "lib/MyApp/Batch/$module.pm";
    ok -e $module_file;
  }
  # g スケジュールオブジェクトを取り出して
  # 必要に応じて実行時間のテストをする
  my $schedule = $job->schedule;
  ok $schedule->match(hour => 2, minutes => 15 ...);
}
done_testing;

aでcrontabのシンタックス確認,bで危険な指定の確認,cでログ出力の確認,dで規約に従った記述をしているかの確認,eで実行権の確認,fで指定コマンドをtypoしていないかの確認,gで実行時間のチェックを行っています。先ほどの項目を一通り網羅していることがわかるでしょう。

crontabの本番環境

本番反映の方法

crontabコマンドは引数にファイルを指定すると,その内容をcrontabに反映してくれます。

次のようにしてcrontabを反映すればよいでしょう。筆者のプロジェクトではcronやワーカ類を動かす専用のサーバを立てているので,デプロイ後にsshでバッチサーバに入り,反映を行っています。

% ssh myproj-batch01
# 差分確認
% diff -u <(crontab -l) data/crontab.txt
# 反映
% crontab data/crontab.txt

上記はデプロイ時に自動反映してもよいとは思いますが,危険な動作をしたバッチを緊急的に止めるなど一時的にcrontabを直接編集していることもあり得るので,念のため上記の手順を踏むようにしています。

crontabの本番反映漏れを防ぐTips

さて,上記のように反映手順が増えると,逆にcrontabの反映漏れが心配になります。一時的に本番でコメントアウトしていたバッチの戻し忘れなども心配です。

そういうミスを防ぐために,デプロイ時にバッチサーバのcrontabの内容を確認して差分がないかを検査し,差分があった場合は目立つように警告を赤字で出して,crontabの同期を促すようにしています。

<続きの(3)こちらから。>

著者プロフィール

松木雅幸(まつきまさゆき)

idのSongmuの由来は「松木」の中国語発音表記。

中国でのIT起業,語学営業職,SIer,ソーシャルゲーム開発などの紆余曲折を経て,現在は,はてな東京オフィスでチーフエンジニアとサーバー監視SaaS,Mackerelのプロダクトオーナーを務める。ISUCONに過去3度優勝するなど,インフラを意識してアプリケーションコードを書くことが得意。趣味はCPANにモジュールを上げまくることだったが,最近はGoがお気に入りで多数のオレオレツールをGitHubに公開している。

著書に『みんなのGo言語[現場で使える実践テクニック]』,『Mackerelサーバ監視[実践]入門』(共に共著,技術評論社)

Blog:http://www.songmu.jp/riji/
GitHub:Songmu