2013年1月17日木曜日

CentOS6 の cron.daily 実行時刻を CentOS5 以前と同様に設定する

CentOS6 の cron.daily 実行時刻は、CentOS5 以前とは異なります。
CentOS5 以前であれば、デフォルトでは午前4:02にスケジュールされていましたが、CentOS6 のデフォルトでは、ある幅の範囲内でランダム化されています。
まずは、このランダム化の内容を確認してみます。

最初に /etc/crontab を見てみますと、次のように空っぽです。
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/

# For details see man 4 crontabs

# Example of job definition:
# .---------------- minute (0 - 59)
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# |  |  |  |  |
# *  *  *  *  * user-name command to be executed

CentOS5 以前であれば、次のようにエントリーされていました。
02 4 * * * root run-parts /etc/cron.daily

ランダム化は、anacron (cronie-anacron) により行われており、設定内容は /etc/anacrontab に記述されています。デフォルトの /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
いくつかのパラメータがあり、man anacrontab(5) に解説がありますが、概説しますと、赤字の 1 (左端) が実行周期 (日次実行=1) で、次の数字 5 は、実行開始タイミングになってから、5 分間の遅延を入れる指定です。実行開始する時間帯は START_HOURS_RANGE で制御され、3-22 とは、3時から22時までの範囲 (3:00 ≦ t < 22:00) を指定しています。ランダム化の幅は RANDOM_DELAY で制御され、45 とは 0~45 分の範囲 (乱数計算の実装上、正確には 44 分まで) でランダムに遅延を行うことを指定しています。
やや複雑ですが、このデフォルト設定で 24H 運用した場合、cron.daily は午前3:06から3:50の間でランダム実行されることになります。ここで 3:06 なのは、そもそも anacron が動作する周期が毎時01分であるためです。/etc/cron.d/0hourly および /etc/cron.hourly/0anacron 参照。
また、もし毎日シャットダウン電源OFF運用を行う場合、例えば、午前6:00にブートすると、その直後の午前6:06から6:50の間でランダムに cron.daily が実行されることになります。
もしも、1日以上シャットダウンしていて、午後23:00にブートした場合には、START_HOURS_RANGE の範囲外であるため、cron.daily の実行は、翌日の午前3:06から3:50の間ということになります。

このランダム化は、仮想化環境を考慮してのこと (ゲストOSの cron.daily が重ならないようにする意図) とは思いますが、ランダムに動作されるのは運用上よろしくない (混乱要因となる) と考える方も居るかと思います。CentOS6 では、CentOS5 以前のように固定の時刻に cron.daily を行わせるようにする方法も用意されており、デフォルトでインストールされる cronie-anacron パッケージの代わりに、cronie-noanacron をインストールすればよいです。

パッケージの依存関係のため、事前に cronie-noanacron をインストールし、その後に cronie-anacron を削除するという手順を踏みます。
# yum install cronie-noanacron
...途中省略...
Installed:
  cronie-noanacron.x86_64 0:1.4.4-7.el6

Complete!

# rpm -e cronie-anacron
この cronie-noanacron は、ちっぽけなパッケージで、/etc/cron.d/dailyjobs ファイルだけを含んでいます。
# rpm -qi cronie-noanacron                                                   
Name        : cronie-noanacron             Relocations: (not relocatable)
Version     : 1.4.4                             Vendor: CentOS
Release     : 7.el6                         Build Date: Tue 19 Jul 2011 01:55:56 PM JST
Install Date: Thu 17 Jan 2013 12:02:39 AM JST      Build Host: c6b5.bsys.dev.centos.org
Group       : System Environment/Base       Source RPM: cronie-1.4.4-7.el6.src.rpm
Size        : 326                              License: MIT and BSD and ISC and GPLv2
Signature   : RSA/SHA1, Mon 26 Sep 2011 01:17:22 PM JST, Key ID 0946fca2c105b9de
Packager    : CentOS BuildSystem 
URL         : https://fedorahosted.org/cronie
Summary     : Utility for running simple regular jobs in old cron style
Description :
Old style of {hourly,daily,weekly,monthly}.jobs without anacron. No features.

# rpm -ql cronie-noanacron
/etc/cron.d/dailyjobs    ※パッケージの中身はこれ1個
/etc/cron.d/dailyjobs の中身は次のようになっています。
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/

# run-parts
02 4 * * * root [ ! -f /etc/cron.hourly/0anacron ] && run-parts /etc/cron.daily
22 4 * * 0 root [ ! -f /etc/cron.hourly/0anacron ] && run-parts /etc/cron.weekly
42 4 1 * * root [ ! -f /etc/cron.hourly/0anacron ] && run-parts /etc/cron.monthly
CentOS5 以前での運用経験があれば、見慣れた実行時刻かと思います。ただし、0anacron の存在チェックが行われており、cronie-anacron パッケージを削除しないと有効化されないようにガードされています。ご注意を。

すでにお分かりかとは思いますが、cron.weekly および cron.monthly も同様です。ただ、cron.hourly はランダム化されておらず、次のように /etc/cron.d/0hourly に設定が書かれています。
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/
01 * * * * root run-parts /etc/cron.hourly

最後に、いわずもがなですが、RHEL6 でも同様です :-p

2013-01-18追記
境界値がはっきりしないので、いちおう cronie-1.4.4-7.el6 のソースを見てみました。

START_HOURS_RANGE に対応する処理は、次の通りです。こちらは簡単ですね。
    422             t = localtime(&jobtime);
    423             if (range_start != -1 && range_stop != -1 &&
    424                 (t->tm_hour < range_start || t->tm_hour >= range_stop))
    425             {
    426                 Debug(("The job `%s' falls out of the %02d:00-%02d:00 hours range, skipping.",
    427                         job_array[j]->ident, range_start, range_stop));
    428                 job_array[j]->drop_job = 1;
    429             }
    430             else
"anacron/main.c"

RANDOM_DELAY に対応する処理は、次の通りです。
    279         if (strncmp(env_var, "RANDOM_DELAY", 12) == 0) {
    280             r = match_rx("^([[:digit:]]+)$", value, 0);
    281             if (r != -1) {
    282                 int i = random();
    283                 double x = 0;
    284                 x = (double) i / (double) RAND_MAX * (double) (atoi(value));
    285                 random_number = (int)x;    ※ここで小数点以下切捨てだからvalueは含まない?
    286                 Debug(("Randomized delay set: %d", random_number));
    287             }
    288         else goto reg_invalid;
    289         }
"anacron/readtab.c"
これを見ても、生成される random_number の範囲に value が含まれるのか否か、頭の中だけでは判然とせず。手間ですが、実験プログラムを一筆書いてみました。
/*
        test_random.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/time.h>

get_random_number(char *value)
{
        int random_number;

        // next 4 lines are copied from anacron/readtab.c:parse_tab_line()
        int i = random();
        double x = 0;
        x = (double) i / (double) RAND_MAX * (double) (atoi(value));
        random_number = (int)x;

        return random_number;
}

main(int argc, char* argv[])
{
        int i ;
        int random_number ;
        struct timeval tv ;
        struct timezone tz ;

        if (argc <= 1) exit(1) ;

        gettimeofday(&tv, &tz) ;
        // cronie uses this method
        srandom(getpid()+tv.tv_usec) ;

        for (i = 0; i < 10000; i++) {
                random_number = get_random_number(argv[1]) ;
                printf("%d\n", random_number) ;
        }
}
# gcc -o test_random test_random.c 
# ./test_random 10 | sort -n | uniq -c
    993 0
   1028 1
   1006 2
   1011 3
    998 4
   1030 5
    999 6
    966 7
    985 8
    984 9
# ./test_random 45 | sort -n | uniq -c | tail 
    210 35
    207 36
    218 37
    250 38
    197 39
    221 40
    217 41
    217 42
    209 43
    233 44
というわけで、0 ≦ random_number < value ですね。

2014-05-05追記
RHEL7.0 RC を試しましたが、RHEL6/CentOS6 と同様で、もし cron.daily 実行時刻を固定化したい場合は、cronie-noanacron と入れ替えれば良いようです。デフォルトの /etc/anacrontab の設定にも変更は無いようです。

2014-07-21追記
いちいち書くまでもないと思いつつ、RHEL7/CentOS7 でも同様です。
人気ブログランキングへ にほんブログ村 IT技術ブログへ