cronを使うとコマンドやスクリプトを定期的に自動実行できるので便利です。
自動実行ですから、少なくともエラーが発生した場合には通知を受け取りたいです(通知がないとエラーが発生していても気づかず放置してしまいがちです)。
cronで実行するスクリプト内に通知を行うコードを書くことが多いですが、そもそもそのスクリプトが実行されなければ通知もされません。
cronにはデフォルトでエラーを含む出力をメール通知する機能が備わっています。
しかし、外部にメールを送信できるようにサーバーを構築するのは面倒なので、slackに通知しようというのがここでの問題意識です。
その方法を調べると、いろいろなやり方が出てきて混乱したので、なるべくシンプルな方法をここにまとめます。
1.事前準備(slackに通知をするシェルスクリプトを用意する)
事前準備として、標準入力から受け取った内容をslackに通知するシェルスクリプトを用意します。
標準入力からの受け取りやjsonの組み立て、特にダブルクォートのエスケープに苦労しつつも、最終的には次のシンプルなシェルスクリプトに落ち着きました。
webhookurlはご自身のものを指定してください。
#!/bin/sh
webhookurl='https://hooks.slack.com/services/XXXXXXXX/YYYYYYYY/ZZZZZZZZ'
stdin=$(cat)
message=$(jq --arg text "$stdin" -n '{text: $text}')
curl -X POST -H 'Content-Type: application/json' --data "$message" "$webhookurl"
ここでは、このシェルスクリプトを、/usr/bin/local/slack_notification.shという名前で保存し、パーミッションを755に設定します。
2.ジョブごとに通知を設定する
ジョブの出力をパイプでslack通知スクリプトに渡せば、ジョブごとに通知を設定できます。
この記事では、実験のため、エラーが発生しないecho "cron slack test"
とエラーが発生するnotexistingcommand
を例に取ります。
標準出力と標準エラー出力の両方を通知するcrontabの例
* * * * * echo "cron slack test" 2>&1 | /usr/local/bin/slack_notification.sh
* * * * * notexistingcommand 2>&1 | /usr/local/bin/slack_notification.sh
標準エラー出力だけを通知するcrontabの例
* * * * * echo "cron slack test" 2>&1 1>/dev/null | /usr/local/bin/slack_notification.sh
* * * * * notexistingcommand 2>&1 1>/dev/null | /usr/local/bin/slack_notification.sh
3.一括で通知を設定する
cronで実行するジョブの数が少なければジョブごとに通知を設定すればよいのですが、たくさんのジョブをまとめて通知の設定をしたい場合は、cronにデフォルトで備わっているメール通知を活用すると楽です。
この方法では、メールの件名に実行コマンドが書かれているなど、詳細情報を取得できるという利点もあります。
postfixをインストールして、/etc/aliasesにcronslack: |"/usr/local/bin/slack_notification.sh"
を追記し、newaliasesコマンドを実行します。
詳しくは「postfix スクリプト」などと検索してください。
標準出力と標準エラー出力の両方を通知するcrontabの例
MAILTO=cronslack
* * * * * echo "cron slack test"
* * * * * notexistingcommand
標準エラー出力だけを通知するcrontabの例
MAILTO=cronslack
* * * * * echo "cron slack test" 2>&1 1>/dev/null
* * * * * notexistingcommand 2>&1 1>/dev/null
4.細かい話
crontabの実行結果をSlackで受け取る方法 | 北進総業と問題意識を共有していますが、私は既存のcronをなるべく変更せずにcronの記載自体から何をしているかわかるようにしたかったので、上に書いたような方法にしました。
【AlpineLinux】Cron(busybox)でMAILTOを指定してもメールが届かない件 – TokunagaKazuya.tkとか、sendmailコマンドを差し替えて、sendmailコマンドにくるメールをSlackに投稿する #Docker – Qiitaとか、cronの出力をメールでなくSlackに送る方法 #Slack – Qiitaとか、みなさんが書かれているいろいろな方法を参考にしました。
スクリプト言語に頼らずUNIX哲学に則った解決法を見出だせたのではないかなと思っています。