読者です 読者をやめる 読者になる 読者になる

めもめも

このブログに記載の内容は個人の見解であり、必ずしも所属組織の立場、戦略、意見を代表するものではありません。

anacronふたたび。(「RHEL6で悩ましい諸々」シリーズ)

何の話かというと

微妙な違いではあるのですが、RHEL5とRHEL6では、cronとanacronの関係が若干変わっています。

RHEL5を運用していた方は、cronとanacronの関係を

  • cronが実行しそこねた定期ジョブをanacronが代わりに再実行する

と捉えているのではないでしょうか?

これは間違いではないのですが、実は、もともとcronとanacronにはこのような関係はありません。それぞれ独立したジョブ実行ツールであり、目的に応じて使い分けることになります。なのですが、RHEL5では、あえてcronとanacronを連携させるような設定を行なって、上記のような使い方をしていました。おそらくこれは、「cronしか無かった時代の設定を引き継ぎつつ、anacronのジョブ再実行機能も使いたくなった」という歴史的な理由によるものです。

一方、RHEL6では、このような連携はなくなっており、

  • 日次、週次、月次のジョブはanacronで実行する
  • その他のジョブはcronで実行する

という設定に変わっています。

ここでは、このようなRHEL5とRHEL6のcron/anacronの設定の違いをまとめておきます。

cronとanacronの特徴

まずは、cronとanacronの本来の特徴を整理すると次のようになります。

[cronの特徴]

  • 日付・曜日・時・分の粒度で指定時刻にジョブを実行する。
  • 指定時刻にサーバが停止していたなどで、実行しなかったジョブを再実行する機能はない。次回の指定時刻まで待つ必要がある。
  • /etc/crontab, /etc/cron.d/以下のファイル, 各ユーザのcrontabファイル(crontabコマンドで設定)など、複数のジョブ設定場所がある。
  • デーモンプロセス(crond)が常駐して指定時刻にジョブを起動する。

[anacronの特徴]

  • ジョブの実行間隔を1日単位で指定する。したがって、ジョブの実行頻度は、1日1回以上には設定できない。(ジョブの実行時刻を明示的に指定する機能はない。この点は後で説明)
  • ジョブの設定場所は、/etc/anacrontabのみで、rootユーザ以外はジョブの設定はできない。
  • ジョブの実行タイミングにランダムな遅延を挿入することが可能で、複数サーバで同じ時刻に一斉にジョブが起動する問題(仮想マシン環境で、時々、はまりますよね。。。)を回避できる。
  • 指定時刻にサーバが停止していたなどで、実行しなかったジョブを再実行する機能がある(というか、そのような使い方ができる。)
  • デーモンプロセスは存在しないので、定期的にanacronコマンドを実行する仕組みを別途用意する必要がある。(anacronコマンドを実行したタイミングで、実行するべきジョブがあれば、それを実行する。)

・・・という感じで、cronとanacronの違いが分かってきたのではないでしょうか?

cronの設定方法はみなさんよくご存知だと思うので、anacronの設定方法を少し詳しく説明しておきます。

acacronの設定方法とRHEL6におけるcronとの関係

anacron本来の使い方が分かるということで、RHEL6における設定ファイル(/etc/anacrontab)のデフォルト状態を下記に示します。

/etc/anacrontab (RHEL6)

# /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

anacronは、ジョブを実行すると、job-identifierで指定されたファイル(/var/spool/anacron/)に日付を書きこむことで、前回のジョブ実行日を記録します(日付までで、時刻は記録しません)。何らかの方法で、anacronコマンドが、再度、実行されると、前回のジョブ実行日から"period in days"以上の日数が経過していると、このジョブを再度実行します。上記の設定の場合、日次、週次、月次で、3つのrun-partsコマンドがそれぞれ実行されることになります。

ここでは、毎時1分にanacronコマンドを実行するように、cronジョブが仕込まれているものと仮定します。(というか、RHEL6では実際にそのように設定されています。)この場合、一時的にサーバが停止していたとすると、再度、サーバが起動して、最初の1分のタイミングで、cronからanacronコマンドが実行されて、(停止中にanacronが実行しそこねた)必要なジョブが実行されることになります。

この方式の場合、各ジョブの(最小の)実行間隔は決まりますが、ジョブの実行時刻は特定できない点に気づいたでしょうか? サーバを長期間にわたって停止しておいて、ある日サーバを起動すると、最初にanacronを実行したタイミング(最初の1分のタイミング)で、全てのジョブが実行されて、翌日以降は、その日の最初にanacronコマンドが実行されたタイミングで、必要なジョブの実行が行われます。(ジョブの実行タイミングの記録は、日付までで、時刻は記録されない点に注意してください。)これがanacronの考え方なので、仕方ないといえば仕方ないのですが、さすがに昼間に重いジョブが走るのは困るということで、ジョブの実行を許可する時間帯を"START_HOURS_RANGE"で指定するようになっています。

上の例であれば、深夜3時〜翌日の夜10時において、ジョブの実行が許可されます。通常の稼動状態であれば、毎晩、03:01にanacronコマンドが実行されたタイミングで、必要なジョブが実行されます。一方、長期間、停止していたサーバを昼間に起動すると、起動直後の1分のタイミングでジョブが走ります。ただし、次の実行は、翌日以降の最初の03:01に戻ります。

さらに、anacronがあるジョブを実行しようと決断したタイミングから、「"delay in minutes" +(0〜RANDOM_DELAYの乱数)」分の遅延の後に、実際にジョブが実行されるようになっています。これによって、サーバ間でジョブの実行タイミングをばらけさせる事ができます。

以上が、RHEL6のanacronの役割になります。cronのように、厳密に決まった時刻にジョブを起動するのではなく、日次、週次、月次のジョブをほどよいタイミングで実行するためのツールであることが分かります。

なお、RHEL6では、毎時実行するべきジョブは、/etc/cron.d/0hourlyの設定ファイルに仕込まれています。

/etc/cron.d/0hourly (RHEL6)

SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/
01 * * * * root run-parts /etc/cron.hourly

そして、その実体は、/etc/cron.hourly/以下になり、ここにanacronコマンドの定期実行が仕込まれています。

/etc/cron.hourly/0anacron (RHEL6)

#!/bin/bash
#in case file doesn't exist 
if test -r /var/spool/anacron/cron.daily; then
    day=`cat /var/spool/anacron/cron.daily`
fi
if [ `date +%Y%m%d` = "$day" ]; then
    exit 0;
fi

# in case anacron is already running,
# there will be log (daemon won't be running twice).
if test -x /usr/bin/on_ac_power; then
    /usr/bin/on_ac_power &> /dev/null
    if test $? -eq 1; then
    exit 0
    fi
fi
/usr/sbin/anacron -s

若干、コメントが混乱しているようなので、コメントは無視してください。前半の分岐処理は、前回のcron.daily実行時から日付が変わっていない場合は、anacronコマンドの実行そのものをやめる処理です。anacronコマンドを実行しても何も起こらないことがわかっているので、無駄な実行を避けています。後半の分岐処理は、ノートPC環境で、バッテリー動作中は、anacronコマンドの実行をやめる処理です。

RHEL5におけるcronとanacronの関係

RHEL6の状況がわかった所で、再度、RHEL5の設定を振り返っておきましょう。

RHEL5では、毎時のジョブも、日次も週次も月次もすべて/etc/crontabに仕込まれていました。

/etc/crontab (RHEL5)

SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/

# run-parts
01 * * * * root run-parts /etc/cron.hourly
02 4 * * * root run-parts /etc/cron.daily
22 4 * * 0 root run-parts /etc/cron.weekly
42 4 1 * * root run-parts /etc/cron.monthly

深夜4時に一斉に仮想マシンがジョブの実行を開始してひどい目に会う、あのパターンですね。

そして、日次、週次、月次のジョブの中には、それぞれ「anacronに関係した何か」が含まれています。

/etc/cron.daily/0anacron (RHEL5)

#!/bin/sh
#
# anacron's cron script
#
# This script updates anacron time stamps. It is called through run-parts
# either by anacron itself or by cron.
#
# The script is called "0anacron" to assure that it will be executed
# _before_ all other scripts.

# Don't run anacron if this script is called by anacron.
if [ ! -e /var/run/anacron.pid ]; then
    anacron -u cron.daily
fi

これは、先に説明したanacronの実行日の記録ファイル(/var/spool/anacron/)に日付を書きこむだけの処理をしています。anacronから実行されたわけではないのですが、anacronから実行されたことにして、anacronをごまかしているわけですね。

何をごまかしているかって?

実は、RHEL5のanacronの設定を見ると、RHEL6とほとんど同じ設定がなされています。

/etc/anacrontab (RHEL5)

# /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

1	65	cron.daily		run-parts /etc/cron.daily
7	70	cron.weekly		run-parts /etc/cron.weekly
30	75	cron.monthly		run-parts /etc/cron.monthly

本来であれば、定期実行も再実行も両方をanacronでやるべきところですが、RHEL5では、定期実行はcronの方でやっているので、再実行だけをanacronにやらせるためのトリックですね。cronの実行が行われないと、実行日の記録ファイルの更新が行われないので、この場合は、anacronが再実行処理を行なってくれます。

ただし、anacronが実行されるタイミングは、RHEL6とはまったく異なります。RHEL5では、anacronを実行する必要があるのは、サーバを停止していた後に、再度、サーバを起動した時だけなので、rcサービス(chkconfigで設定する、サーバ起動時に自動実行するサービス)から実行されるようになっています。

RHEL5

# chkconfig --list anacron
anacron        	0:off	1:off	2:on	3:on	4:on	5:on	6:off

RHEL6

# chkconfig --list anacron
サービス anacron に関する情報の読み込み中にエラーが発生しました: そのようなファイルやディレクトリはありません

いやぁ。cronなんていまさら、という感じですが、いろいろ学ぶことはありますね。

cronieについて

RHEL5では、cron/anacronは、vixie-cronとanacronのRPMパッケージで提供されていました。

RHEL5

# rpm -qa | grep cron
crontabs-1.10-8
vixie-cron-4.1-77.el5_4.1
anacron-2.3-45.el5

RHEL6では、cronie系のパッケージに変わっています。

RHEL6

# rpm -qa | grep cron
cronie-1.4.4-7.el6.x86_64
crontabs-1.10-32.1.el6.noarch
cronie-anacron-1.4.4-7.el6.x86_64

cronieは、vixie-cronの品質に問題を感じたRed Hatの中の人が、vixie-cronをフォークして立ち上げたプロジェクトです。

参考)https://fedorahosted.org/cronie/