【Linux】anacronの制御について検証しながら理解した際の話

Linux

はじめに

cronの説明は参考書でもインターネット上でも豊富に得られますが、anacronについては話題が少ない気がします。私自身、/etc配下に「anacrontab」というファイルがあったことは知っていましたが、「なにこれ?」と思うだけで、実際に調べて検証までする気になりませんでした。でも、知らない中で運用していると、思わぬ所で意図しない動作をすることがわかったので、今回はまとめることに至りました。

なお以下の記事については、私が個人的に検証している環境がCentOS8なので、CentOS8で検証等を行っています。

cronについて

cronについては、違う記事で書いたので、そちらを参考にしていただければと思います。

anacronの設定ファイルと動作

上記cronに対して、anacronって何?と思いますが、まずは設定ファイルから見ていきます。

anacronの設定ファイルは、/etc/anacrontabです。こんな記述があります。

[root@localhost zabbix_logtest]# cat /etc/anacrontab
# /etc/anacrontab: configuration file for anacron

# See anacron(8) and anacrontab(5) for details.

SHELL=/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
# the maximal random delay added to the base delay of the jobs
RANDOM_DELAY=45
# the jobs will be started during the following hours only
START_HOURS_RANGE=3-22

#period in days   delay in minutes   job-identifier   command
1       5       cron.daily              nice run-parts /etc/cron.daily
7       25      cron.weekly             nice run-parts /etc/cron.weekly
@monthly 45     cron.monthly            nice run-parts /etc/cron.monthly

細かい仕様はありますが、ポイントは最終の3行です。/etc/cron.daily、/etc/cron.weekly、/etc/cron.monthlyそれぞれの配下のファイルをrun-partsで実行することになっています。/etc/cron.hourlyにあるファイルはanacronの対象ではないことも明記しておきます。

anacrontabの内容を実行する0anacron

/etc/anacrontabを実際に実行するのは、「0anacron」というファイルです。0anacronは/etc/cron.hourly配下にあります。/etc/cron.hourly配下にあるファイルは、/etc/cron.d配下にある「0hourly」というファイルの記述よって、1時間ごと実行されます。つまり、cronが1時間ごとに0anacronを実行し、結果的にanacrontabの内容が実行されることになります。(あくまでも実行されるまでの流れであって、必ずしも実行されないことについては続きをお読みください)

[root@localhost log]# ls /etc/cron.hourly/
0anacron

0anacronは履歴を見て実行する

ここで疑問が。「0anacronで1時間に1回実行されるって?1日に1回とか1週間に1回に実行するという話はどこへいった?」と思いますよね。たしかに、1時間に1回「0anacron」は起動しますが、その際にcronのように必ず実行するとは限りません。

anacronは1時間に1回、「anacronが持っている実行履歴を見て」実行をするか判断します。つまり、実行するかどうかは履歴の情報次第となります。

anacronの実行履歴は、「/var/spool/anacron」配下にそれぞれdaily、weekly、monthlyの履歴が記録されています。

[root@localhost ~]# cat /var/spool/anacron/cron.daily
20201001
[root@localhost ~]# cat /var/spool/anacron/cron.weekly
20200930
[root@localhost ~]# cat /var/spool/anacron/cron.monthly
20200930

anacronは1時間ごとに上記の履歴を確認しに行き、その日やるべきjobが実行されたかを履歴をもとに確認します。実行されていれば何もしないし、実行されていなかったら実行するという仕組みをとっています。

お気づきの通り、cron.hourlyの記述はありません。/etc/cron.hourly配下のファイルについては、上記でも述べた通り/etc/cron.d配下にある0hourlyが実行します。これまでの話をまとめると、次のよう表が作れます。

ディレクトリ配下のファイル実行するファイル
/etc/cron.hourly//etc/cron.d/0hourly
/etc/cron.dailyanacron
/etc/cron.weeklyanacron
/etc/cron.monthlyanacron

anacronを知らないと発生する問題

かつてはanacronという仕組みはありませんでした。比較的に新しい仕組みのようです。

なので、anacronが無かったかつてのcronは、以下のようにcrontabに全て書かれていたようです。

[root@localhost ~]# cat /etc/cron.d/0hourly
# Run the hourly jobs
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
01 * * * * root run-parts /etc/cron.hourly
01 12 * * * root run-parts /etc/cron.daily
01 12 1 * * root run-parts /etc/cron.weekly
01 12 1 1 * root run-parts /etc/cron.monthly

上記のような書き方は、参考書などでも一例として書かれていることがあります(割と有名なIT資格の参考書とかね)。それは、anacronが動作していないことが前提になっています。

では、anacronも動いていて、crontabにも上記のように書いていたらエラーが発生するかというと、「エラーにならない」という答えになります。

上記設定を仮に書いてしまった場合、cronとanacronは独自して動くので1日1回実行して欲しいことでも、crontabに記載された分で1回、anacronで1回実行されます。

全部crontabで制御したい!という人はanacronが動作しないようにする必要があります。

全部crontabじゃだめ?

出来ないことはないけど、それなりに問題があったらかanacronが出来たという考えが自然でしょう。上記のcrontabには分かりやすい欠点がありました。それは、「指定した時刻にシステムが停止していると、実行できない」という問題です。

システム停止が続くなんてことは大事件なのでそう起こりませんが、システムメンテナンスのために定期的にシステムを一時的に落とすことはよくあります(よくと言っても毎日とかではないけどね)。メンテナンスの際に、cronのスケジュールを意識するのは、正直面倒なことですし、cronのスケジュールをその都度変えるのも億劫。ただ、実行されないのも困る…。ということが起こってしまいます。

時間の指定が曖昧なanacron

ご察しの通り、上記のような1日の中にシステムが停止している時間があっても関係なく実行してくれるのがanacronのメリットでもあります。以下、本番環境ではできないOSの時間を意図的に変えて、anacronの動作を見てみたいと思います。

検証

anacronのステータスを確認する

まず、直前に実行した履歴を確認します。

[root@localhost ~]# cat /var/spool/anacron/cron.daily
20201001
[root@localhost ~]# cat /var/spool/anacron/cron.weekly
20200930
[root@localhost ~]# cat /var/spool/anacron/cron.monthly
20200930

1日に1回実行するものは「2020年10月1日」が最後の実行履歴になっていますね。(週に1回、1か月に1回の実行するもの9月30日です)

日付を意図的にずらしてみる

dailyが20201001なので、日付をずらしてみます。

[root@localhost logrotate]# date 10021214
2020年 10月  2日 金曜日 12:14:00 JST

意図的に1日進めて、10月2日になった状態で待ちます。いきなり10月2日になったので、当然10月2日分のジョブは実行されていません。

ログで確認

cronのログを見ます。

Oct  2 13:01:01 localhost CROND[48360]: (root) CMD (run-parts /etc/cron.hourly)
Oct  2 13:01:01 localhost run-parts[48360]: (/etc/cron.hourly) starting 0anacron
Oct  2 13:01:02 localhost anacron[48369]: Anacron started on 2020-10-02
Oct  2 13:01:02 localhost run-parts[48360]: (/etc/cron.hourly) finished 0anacron
Oct  2 13:01:02 localhost anacron[48369]: Will run job cron.daily' in 24 min.
Oct  2 13:01:02 localhost anacron[48369]: Jobs will be executed sequentially
Oct  2 13:25:02 localhost anacron[48369]: Job cron.daily' started
Oct  2 13:25:02 localhost run-parts[49031]: (/etc/cron.daily) starting logrotate
Oct  2 13:25:02 localhost run-parts[49031]: (/etc/cron.daily) finished logrotate
Oct  2 13:25:02 localhost run-parts[49031]: (/etc/cron.daily) starting rhsmd

anacronが毎時1分になったら実行されていますね。

「履歴を見たら本日の分の仕事をしていないみたいだから、今から実行しまーす」という感じで実行されています。また、「Will run job cron.daily' in 24 min.」ということで、24分後に実行しますと言っています。このあたりもざっくりとした時間で実行されるようです。

もう一度実行履歴を見る

日付が更新されたか見てみます。

[root@localhost ~]# cat /var/spool/anacron/cron.daily
20201002
[root@localhost ~]# cat /var/spool/anacron/cron.weekly
20200930
[root@localhost ~]# cat /var/spool/anacron/cron.monthly
20200930

dailyの日付のみが更新されていることが分かります。

おまけ

dailyだけではなく、weeklyやmonthの動作見たくなったので、一気に日付をずらした際の結果を以下に記載しておきます。ご参考にどうぞ。

[root@localhost log]# date 11101259
2020年 11月 10日 火曜日 12:59:00 JST

ログを確認します。

[root@localhost ~]# cat /var/log/cron
Nov 10 13:01:01 localhost CROND[50078]: (root) CMD (run-parts /etc/cron.hourly)
Nov 10 13:01:01 localhost run-parts[50078]: (/etc/cron.hourly) starting 0anacron
Nov 10 13:01:01 localhost anacron[50087]: Anacron started on 2020-11-10
Nov 10 13:01:01 localhost anacron[50087]: Will run job cron.daily' in 23 min.
Nov 10 13:01:01 localhost anacron[50087]: Will run job cron.weekly' in 43 min.
Nov 10 13:01:01 localhost anacron[50087]: Will run job cron.monthly' in 63 min.
Nov 10 13:01:01 localhost anacron[50087]: Jobs will be executed sequentially
Nov 10 13:01:01 localhost run-parts[50078]: (/etc/cron.hourly) finished 0anacron
Nov 10 13:24:02 localhost run-parts[50746]: (/etc/cron.daily) finished logrotate
Nov 10 13:24:02 localhost run-parts[50746]: (/etc/cron.daily) starting rhsmd
Nov 10 13:29:02 localhost run-parts[50746]: (/etc/cron.daily) finished rhsmd
Nov 10 13:29:02 localhost anacron[50087]: Job cron.daily' terminated
Nov 10 13:29:02 localhost anacron[50087]: Job cron.daily' terminated
Nov 10 13:44:01 localhost anacron[50087]: Job cron.weekly' started
Nov 10 13:44:01 localhost anacron[50087]: Job cron.weekly' terminated
Nov 10 14:01:01 localhost CROND[51995]: (root) CMD (run-parts /etc/cron.hourly)
Nov 10 14:01:01 localhost run-parts[51995]: (/etc/cron.hourly) starting 0anacron
Nov 10 14:01:01 localhost run-parts[51995]: (/etc/cron.hourly) finished 0anacron
Nov 10 14:04:01 localhost anacron[50087]: Job cron.monthly' started
Nov 10 14:04:01 localhost anacron[50087]: Job cron.monthly' terminated
Nov 10 14:04:01 localhost anacron[50087]: Normal exit (3 jobs run)

無事に実行されました。日付も更新されたか確認します。

[root@localhost src]# cat /var/spool/anacron/cron.daily
20201110
[root@localhost src]# cat /var/spool/anacron/cron.weekly
20201110
[root@localhost src]# cat /var/spool/anacron/cron.monthly
20201110

週に1回、月に1回実行する記録も変更され更新されていますね。