Rails
cron
whenever

【Rails】wheneverでcronが実行されない問題【No MTA installed, discarding output】

概要

新しく導入したwheneverが、ステージング及び本番環境で実行されていなかったので原因・調査結果をまとめました。

状況

syslogを見ると、cron自体は実行されようとしているがNo MTA installed, discarding outputの文字が・・・。

$ grep CRON /var/log/syslog
Nov  1 XX:XX:XX XXXXXXXXXXXXXXXXXXXXX CRON[XXXXX]: (ubuntu) CMD (/bin/bash -l -c 'cd /srv/api/releases/XXXXXXXXXX && RAILS_ENV=staging bundle exec rake XXXX:XXXX --silent')
Nov  1 XX:XX:XX XXXXXXXXXXXXXXXXXXXXX CRON[XXXXX]: (CRON) info (No MTA installed, discarding output)

しかしshell上で同様のコマンドを打つと正常に動作します。

$ /bin/bash -l -c 'cd /srv/api/releases/XXXXXXXXXX && RAILS_ENV=staging bundle exec rake XXXX:XXXX --silent'

outputも出力されていないので、詳細がこれ以上わからないという状況でした。

config/schedule.rb
set :output, "/path/to/my/cron_log.log"

原因

結論から書くと、cron実行時にPATH上にbundleが存在しないことが原因でした。

なぜこうなるの?

.profileではなく.bashrcで設定されたパス上にbundleが存在するのが根本要因になります。

$ which bundle
$HOME/.rbenv/shims/bundle

$ grep PATH= ~/.profile
PATH="$HOME/bin:$HOME/.local/bin:$PATH"

$ grep PATH= ~/.bashrc
export PATH="$HOME/.rbenv/bin:$PATH"

まずcronは最初/usr/bin/binしか参照できない状態です。
そのためwheneverで設定されたcronは/bin/bash -l -c上で実行されます。
-lはログインした状態で実行されるため、.bash_profile|.bash_login|.profileが呼び出されます。
しかし.bashrcは呼び出されないため、環境変数が無いわけですね。

$ man bash
-l        Make bash act as if it had been invoked as a login shell (see INVOCATION below).

つまり、何かしらの方法でcron実行時にbundleにPATHが通るようにする必要があるわけです。

対応方法

1. .bash_profile|.bash_login|.profile上で環境変数の定義をする

一番ダイレクトな対応方針です。
一般的にも環境変数の定義はこちらですることが推奨されているように見えます。

2. cron実行時に環境変数を定義する

crontabが実行されてるときにPATHをセットしてしまおうってことですね。

$ grep PATH /etc/crontab
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

wheneverではshedule.rbに下記コードを足すことでPATHを通すことができます。

config/schedule.rb
env :PATH, ENV['PATH']

こちらについては下記スレッドに出てきている内容を参考にしました。

ref) Whenever gem (cron job) not executing rake task, Rails 4

3. jobを対話モードで実行する。

bash-iで対話モードとして実行することで.bashrcが読み込まれるようになります。
そのため.bashrcに定義されたPATHが通るというわけですね。

config/schedule.rb
set :job_template, "/bin/bash -l -i -c ':job'"

こちらについては下記スレッドに出てきている内容を参考にしました。

ref) Cron commands doesn't work in Ubuntu with 0.6.2 with rails3

おわりに

調べると英語圏で困ってる人がたくさんいるようだったものの、日本語記事があまりなさそうだったのでまとめてみました。
こういう問題は、わかるとシンプルでも、わかるまでは本当に意味不明なんですよね・・・。