cron

Linux上でプログラムの実行をスケジュールするためのプログラム。

独自の書式をもつcrontabファイルでスケジュールを設定する。

想定環境

Ubuntu 20.04

/etc/crontab

SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=
# 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
17 * * * * root cd / && run-parts --report /etc/cron.hourly
25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6 * * 7 root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6 1 * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )

crontabの時刻形式は独特で、複雑な設定を新しく書き起こすのは面倒くさい。

デフォルトの/etc/crontabでは、/etc/cron.hourly/etc/cron.dailyのようなディレクトリの内部の実行ファイルを 定期実行するように設定されているので、ここに雑にシェルスクリプトを入れると簡単にcronを体験できる。

run-partsは、指定したディレクトリ内の実行ファイルを、ファイル名でソートした順番で実行する。 以下のように、ファイル名にソートを考慮した番号付けをすることで、ジョブの実行順を制御できる(10-my-job20-your-job33-gomaの順で実行される)。

10-my-job
20-your-job
33-goma

cronが正しく動作しているか確認するには、journalctlを使うとよい。

journalctl -u cron.service -f -n 20

cronにより実行されたプログラムの標準出力や、標準エラー出力を取得するのは手間がかかる。 cronは、プログラムの出力をメールで送るという動作をするが、メールを送信できるように設定しておかなければならない。 上のcrontabファイルでは、MAILTOを空にすることでメールの送信を試みないようにしている(送信できなかったメールが/var/spool/mailに蓄積されてストレージを圧迫するため)。

ログを確認するには、プログラム中でファイルに書き出すようにする方法をとることになる。

シェルスクリプトからSlackに通知を送るには、以下のようにするとよい(sudo apt install curl jq)。

#!/bin/bash
WORKDIR=/work
WEBHOOK_URL=https://hooks.slack.com/services/***
cd "$WORKDIR"
ERROR=$(mycommand 2>&1 | tee /dev/stderr)
if [[ $? -ne 0 ]]; then
SHORT_ERROR=${ERROR:0:1000}
DATA=$(jq --arg key0 "text" --arg value0 "ERROR: $SHORT_ERROR" '. | .[$key0]=$value0' <<< '{}')
curl -X POST -H 'Content-type: application/json' --data "$DATA" "$WEBHOOK_URL"
fi

/etc/cron.d

crontab形式の設定を複数のファイルに分けて配置するためのディレクトリ。 例えば、/etc/crontabを直接編集せずに、アプリケーションごとにcrontabを配置するようなことができる。 また、実体を別の場所においておいて、シンボリックリンクを/etc/cron.d以下に配置することもできる(後述の権限設定に注意)ので、設定のgit管理にも便利(リポジトリ内のMakefileで、/etc/cron.d/以下からシンボリックリンクを張るようにしておくなど)。

crontabファイルを一から書くのは慣れが必要なので、上の/etc/crontabをコピーし、 もともとの設定を#でコメントアウトしておいて、改変しながら使うのが便利だと思う。

なお、crontabファイルの所有者はrootで、所有者以外に書き込み権限があってはならない(chmod 644 crontab または chmod 600 crontab)という制約があるので注意。

また、SHELLPATHのような設定は、/etc/crontabとは独立しているので、改めて記述しなければならない。

例えば、デフォルトでは、SHELL/bin/shであり、PATH/usr/local/binが含まれていない。 PATHをそれぞれのcrontabファイルで設定しないと、ターミナルで動かしたときと違って特定のコマンドが動かない、というような問題が起こることがある。 例えば、docker-compose/usr/local/binによくインストールされるので、docker-composeコマンドを含むジョブがうまく動かない、ということがある。