🐥

cronジョブのログを出したい

2024/02/04に公開

経緯

最近ちょこちょこDBに異常なデータが入り込んでしまっていて、その関係でサービスが使えなくなったりすることがあります。すぐに原因がわからず、原因が判明されるまで、ユーザー影響を最小限にするために、その異常検知と修復のcronジョブを組みました。この中で出会った問題とその解決についての記録となります。

ジョブの頻度を〇〇秒毎にしたい

cronは作りましたが、デフォルトの設定では、頻度は1分毎になっています。

* * * * *  /bin/bash /abs/path/script.sh

ただ、これを例えば、10秒毎に実行したい時にどうすれば良いでしょう。

ワークアラウンドとして、同じジョブを複数作っておいて、sleepを使うことで擬似的に1分以下の頻度で実行させること。


* * * * *  /bin/bash /abs/path/script.sh
* * * * *  (sleep 10; /bin/bash /abs/path/script.sh)

sleepを使わないやり方もありますが(こちら )、手軽さから今回はsleepで調整しています。

ログを追加したい

デバッグのためにもなるが、クラウド上のログと合わせて、この異常データ検知と修復のジョブもログを出し、調査に有益な情報が得られると考えて、cron-jobのログを追加することにしました。

素朴なやり方

一番素朴なやり方では、シンプルにstdout/stderrを別ファイルにリダイレクトすることになります。

* * * * *   /abs/path/script.sh > /path/cron.log 2>&1

ログファイルのサイズを制御したい

上記のやり方はシンプルですが、一つデメリットがあって、ログファイルのサイズが場合によって大量になってしまうことがあります。

今回のジョブはCloudShell上で組んでいるため、ディスク容量5GBしかありません。下手にすると数日程度で尽きてしまいます。

これを解決するには、logrotateを導入しました。logrotateはほとんどのLinux系OSに備えているサービスです。それ自体も一つのcronジョブとなります。大量にログが生成されることを防ぐために、言葉通りローテーションしているので、設定以上のファイルを削除するようにしています。

設定ファイル/etc/logrotate.confから説明します。こちらはグローバル・デフォルトのコンフィグファイルとなっていますが、下に書いている通り、/etc/logrotate.d内の個別ファイルで上書きすることが可能です。そのため、グローバルの設定を変えたい時以外は、基本こちらのファイルは触らなくてもOKです。

# see "man logrotate" for details

# global options do not affect preceding include directives

# rotate log files weekly
weekly

# use the adm group by default, since this is the owning group
# of /var/log/syslog.
su root adm

# keep 4 weeks worth of backlogs
rotate 4

# create new (empty) log files after rotating old ones
create

# use date as a suffix of the rotated file
#dateext

# uncomment this if you want your log files compressed
#compress

# packages drop log rotation information into this directory
include /etc/logrotate.d

# system-specific logs may also be configured here.

ここの設定について簡単に説明すると

  • weekly これはローテーションを行う頻度の設定となります。他にhourly, daily, monthly, yearlyが有効値です。ただ、logrotateのサービスはデフォルトで1日1回の実行となるため、この値をhourlyにするには、別途logrotateの実行頻度も変更する必要があります。方法は、sudo mv /etc/cron.daily/logrotate /etc/cron.hourly でシステム上のlogrotate cronをdailyからhourlyに移行することです。
  • su root adm これはルートユーザーとアドミングループでログのローテーションが行われるよ、とのこと。特定のユーザーもしくはグループにログファイルの権限を渡す・制御したい場合は有用。
  • rotate 4 これはログファイルのローテーション閾値となります。つまり、weeklyの設定と合わせて、4週間分がたまると、次は一番古いログを削除します。0にすると古いファイルはすぐに削除されるため、ローテーションがなくなります。-1にすると、古いログはmaxageまで削除されません。
  • dateext これはログファイルのエクステンションを、YYYYMMDD形式にするオプションとなります。デフォルトでは、logfile.1, logfile.2となりますが、有効にすると日付形式に変わります。基本頻度がdailyの時に使いたいです。
  • include /etc/logrotate.d このフォルダーには個別のサービス・アプリケーション毎の設定ファイルを保存しています。今回利用する際にこちらのフォルダーに、ログファイルのパスと各種設定を追加しました。設定は以下となります。
# touch /etc/logrotate.d/my-cron-job
/path/to/log/file { # -> logs/*.log とかのパターンでもOK
  rotate 7
  daily
  missingok # -> ログファイルが見つからなくてもローテーションは止めないで
  notifempty # -> ログファイルが空っぽならローテートしないで
}

ローテーションは基本設定通りの毎日で行い、合計1週間分のログを保持するようにしています。ただ、ファイルサイズ上限を儲けたい場合は該当のオプションで調整可能です。詳細はsize, minsize, maxsizeとかをご参考ください。

logrotateを導入したcron-job

書き方的にはほとんど素朴なやり方と一緒ですが、ログのローテーションがあるので、ファイルサイズの心配もなく、うまくいけそうになりました。最終的にこのようなテンプレートになっています。

  • スクリプト
#!bin/bash
# スクリプトドキュメント

# 時間と実行ユーザーもログに出す
whoami
date

# ここからスタート

echo my-cron-job
# ...
  • cron-tabの設定
* * * * *  /bin/bash /abs/path/script.sh
* * * * *  (sleep 10; /bin/bash /abs/path/script.sh)
* * * * *  (sleep 20; /bin/bash /abs/path/script.sh)
* * * * *  (sleep 30; /bin/bash /abs/path/script.sh)
* * * * *  (sleep 40; /bin/bash /abs/path/script.sh)
* * * * *  (sleep 50; /bin/bash /abs/path/script.sh)
  • logrotateの設定
# /etc/logrotate.d/my-cron-job
/path/to/log/file {
  rotate 7
  daily
  missingok
  notifempty
}

終わりに

logrotateは今回初めて使ってみました。この記事を作成する際にこちらの記事を参考にしていました。非常に詳しく説明されているので、もっと知りたい方にはおすすめです。

あまりこのような場面と出会う機会がないかもしれませんが、非常にシンプルで便利なので、何かサーバー側でcronを組みたい時に、積極的に採用したいと思います。

ではでは、今回はこれで。

OPTIMINDテックブログ

Discussion