Edited at

crontabのガイドライン

cron (クーロン) と言えば定期実行してくれるおなじみのシンプルな機能だがそこには奥行きがあり様々なノウハウがある。

そこで設定するときのガイドラインをまとめた。

深くはあまり説明出来ないので気になる用語機能を見つけたら別途検索していただきたい。


cronの場所


/etc/crontab

crontabの書式

意識してここに書く必要は現在あまり無い。

RedHat系は非推奨とか言われてるが根拠はよくわからない。

rootで実行される。

#crontabの書式

# (行頭の # マークはコメント行を示す)
# +------------ 分 (0 - 59)
# | +---------- 時 (0 - 23)
# | | +-------- 日 (1 - 31)
# | | | +------ 月 (1 - 12)
# | | | | +---- 曜日 (0 - 6) (日曜日=0)
# | | | | |
# * * * * * 実行されるコマンド


/var/spool/cron/

crontabの書式

crontab -e でスケジュールしたときに格納される。

実行したいユーザ名で保存され600にする必要がある。

crontab -e すれば勝手に作られるので自分で作ることはほぼ無く意識する必要は無い)

crontabをバックアップするならこのディレクトリ下をバックアップする。

ls -l /var/spool/cron/


/etc/cron.d/

crontabの書式

主にsystemが使う。意識してここに置く必要は現在あまり無い。

rootで実行される。

ls -l /etc/cron.d/


/etc/anacrontab

anacrontabの書式

cronのようでcrontabじゃ無いanacrontab。

遅延実行、再起動時の未実行タスクの消化などしてくれる。

特にjobのシリアライズ制御があるのでcronにありがちな02時台のバックアップジョブが1時間を超えたので後続処理が死んだ!!!!!11111みたいなことを回避することができる。

さらに言えばcron0時病(後述の実行時間は0分を避ける0時0分はもっと避ける)参照も気にしなくていい

rootで実行される。

less /etc/anacrontab


/etc/cron.hourly/ /etc/cron.daily/ /etc/cron.weekly/ /etc/cron.monthly/

crontabとは別な前述のanacrontabの管轄

それぞれ毎時毎日毎週毎月実行する。

タイミングずらしをしてくれる。(前述のanacrontabの機能)

スクリプトを直接配置する。

ls -l /etc/cron.hourly

ls -l /etc/cron.daily
ls -l /etc/cron.monthly


cronの書き方


実行パターン

5コある米印がそれぞれ分時日月曜を表す。

#分時日月曜

#* * * * * 実行されるコマンド

米印は任意の値であり

* * * * * なら毎分

数字を指定すればその時間に実行される。

0 * * * * なら毎時0分

0 0 * * * なら毎日0時0分

0 0 1 * * なら毎月1日0時0分

0 0 1 1 * なら毎年1月1日0時0分

0 0 1 1 0 なら毎年1月1日0時0分が日曜日だったとき(6年に1回しか実行されない)


米印以外にも書式があり、以下の制御文字が使える。

*   任意の値

, 値リストセパレータ
- 値の範囲
/ ステップ値

0 0,12 * * * なら0時0分と12時0分

0 10-18 * * * なら10時から18時までの0分

0 */6 * * * なら0時から6時間毎の0分

0 6-18/6 * * * なら6時から18時の間に6時間ごとの0分


組み合わせもできる

0 0,10-18 * * * なら0時と10時から18時までの0分

0 10-18 * * * なら10時から18時までの0分

0 21,6-18/6 * * * なら21時6時から18時の間に6時間ごとの0分

ステップ値に取扱

ステップ値の */数字 は0(または1)からのステップになる。

これは * 0-23/6 * * ** */6 * * * が同義である。

特にこのステップ地はスケージュール登録する上で重宝する。

例えば営業時間中(9時から18時で休憩は13時から14時)に15分間隔ただし0分から5分ずらすといった場合、

5-59/15 9-13,14-18 * * *とすることができる。

1時間以内で終わる6種類ある各処理を同時起動しないように6時間ごとに5分に実行したい場合は

5 0-23/6 * * * a.sh

5 1-23/6 * * * b.sh
5 2-23/6 * * * c.sh
5 3-23/6 * * * d.sh
5 4-23/6 * * * e.sh
5 5-23/6 * * * f.sh

とすることができる。

なお

https://crontab.guru/

で次回起動が確認できるので便利


パスを通す

crontabではパスが通ってないので、度々コマンドが無いと怒られる。

crontabの冒頭にPATHが指定すると読んでくれる。

PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin/

5 3 * * * apachectl graceful

またパスを通すまでもないときは which でフルパスを探して書くと失敗は少ない。

which apachectl -> /usr/sbin/apachectl

5 3 * * * /usr/sbin/apachectl graceful


crontabでのエスケープ

0 * * * * /bin/bash echo '実行した' >> /var/log/log_$(date +%Y%m%d).log 2>&1

上記のように毎時/var/logにlog_年月日.log というファイルに標準出力・エラー出力を書き出すスケジュールを追加した場合うまく行かない。

正しくは

0 * * * * /bin/bash echo '実行した' >> /var/log/log_$(date +\%Y\%m\%d).log 2>&1

とする必要がある。

これは % がcrontabの制御文字と衝突するためエスケープが必要になるため。

なのでcrontabで % 使う場合は必ず \% とエスケープが必要。


cronの実行ログは消さないこと

cronでは出力(標準出力/標準エラー出力)があった場合所有者にメールを出す。

大体は煩わしいので最終行に >/dev/null 2>&1 と書かれることがある。

しかしこれは非常に良くない

定期実行されるcronで処理が失敗した場合手がかりがなくなってしまうことになる。

いつから失敗したのかなど根本的なところから何も得られない。

>/dev/null 2>&1 を覚えるぐらいなら >> /var/log/log_$(date +\%Y\%m\%d).log 2>&1 を覚えたほうが良い。

2>&1 を毎度間違えると言うなら >> /var/log/log_$(date +\%Y\%m\%d).log 2>> /var/log/log_error_$(date +\%Y\%m\%d).log でも良い


crontabはバックアップしとくと安心

変更前はバックアップするのが鉄則

crontab -l > ~/.crontab-editlog.bk

ちら見する場合は -e は使わないず -l でみる。

大きいcrontabの場合は crontab -l|less とすると良い。

編集するとき -r と押し間違えるので -e を使うなと教えてるところが有るが逆に危険

これはcrontabが構文チェックしてるので直接/etc/crontabに書かれると構文チェックが働かず間違った構文で保存するとエラーになる。そもそもroot権限なので権限が無いと話にならない。

どうしてもと言うなら crontab [cronfile] でcrontabに上書きする方法。

これなら構文チェックも効くのでうっかりミスも防げる。

この方式でもバックアップは別に取るべきだろう。でなければviで編集してしまうと元が消えてしまう。

さらに言えばこのcrontabファイルをgit管理しとけば変更履歴も追えて一石二鳥かもしれない。

ただ他人の環境を覗くとき完備された環境ではないので毎回 crontab -l したあと crontab -e する癖をつけたほうが良い。

そうすれば仮に間違えて -r したとしてもconsoleに残ってるのでリカバリが効くのでこの癖を推しときたい。


バックアップを自動化する

バックアップは以下のcronを登録しておけば変更があるたびに勝手にバックアップを取るコマンド。

バックアップ頻度は起動時間で各自調整すると良いでしょう。

#CRON-BUCKUPv1

* * * * * crontab -l >~/.crontab-editlog$(date +\%Y\%m\%d\%H\%M).log && [ $(ls -t ~/.crontab-*.log 2>/dev/null |wc -l) -gt 1 ] && diff $(ls -t ~/.crontab-*.log | head -1) $(ls -t ~/.crontab-*.log |head -2 |tail -1) >/var/tmp/cron-diff.log 2>&1 && rm -v $(ls -t ~/.crontab-editlog*.log | head -1) >>/var/tmp/cron-diff.log 2>&1


実行時間は0分を避ける0時0分はもっと避ける

anacrontabを使えという話ではありますが、結局いつ実行されてんねんと言う突上げがあったりして

結局crontabで良いかと持ってきてしまう事があるわけで、そうした場合に気をつけたいのがこの0分を避けること。

サブシステムが0分起動で集まっていると0分にメインシステムのロードアベレージが上昇してしまう事があります。ですのでちょっとずらす5分とか10分とかずらすと影響を避ける事ができます。(みんな5分に起動すると同じことになるのでそこは調整が必要)


最後に

ガイドラインと言いつつ脱線気味になってしまいましたが、各々のマイナールールがあると思うので絶対厳守というわけでは無いです。ただいざ自分が設定するとき懸念点を知っていれば楯突くことができるので失敗したときも良いといったやんけと言質を取ってマウントを取れるでしょう。細かく覚えなくてもなんか書いてあったなぁ程度頭に入れてもらえれば幸いです。

またこの例はRHEL系向けのもので、FreeBSDやdebianではコマンドの細部が異なる可能性があるので注意が必要です。おきをつけください。

以下のサイトを参考にしました。

https://crontab.guru/

https://www.server-memo.net/tips/etc-crontab.html

https://qiita.com/ohtsuka1317/items/b89ea66f3d227c172477

https://qiita.com/Gin/items/3514a2107e536223fba5

https://www.express.nec.co.jp/linux/distributions/knowledge/system/crond.html

https://qiita.com/takahashi-kazuki/items/77d669d4414c6ec185f9

https://bacchi.me/linux/cron-tips-2/

http://dqn.sakusakutto.jp/2012/06/shell_dev_null_2_1_crontab.html

https://qiita.com/ritukiii/items/b3d91e97b71ecd41d4ea