前人未踏の領域へ WEB・インフラ・プログラミング全般編

フロントエンド、バックエンド、インフラ、言語など、アプリ開発、IOT以外の記録

Rails: Cronジョブ実行時にExecJSでエラー

課題

RailsのJobを実行する処理をcrontabに記述したがExecJSでJavaScriptの実行環境が見つからないとエラーになる。

bundler: failed to load command: bin/rails (bin/rails)
ExecJS::RuntimeUnavailable: Could not find a JavaScript runtime. See https://github.com/rails/execjs for a list of available runtimes.
  /var/rails/milestone/shared/bundle/ruby/2.6.0/gems/execjs-2.7.0/lib/execjs/runtimes.rb:58:in `autodetect'
  /var/rails/milestone/shared/bundle/ruby/2.6.0/gems/execjs-2.7.0/lib/execjs.rb:5:in `<module:ExecJS>'
  /var/rails/milestone/shared/bundle/ruby/2.6.0/gems/execjs-2.7.0/lib/execjs.rb:4:in `<main>'

Nodeはインストール済みだし、ExecJSからも見えている。直接consoleからJobを実行することはできる。 なんなら別な環境では普通に動く。

原因

cronのpathに /usr/local/bin が通っていなかった。cronの初期状態のPATHは /usr/bin:/bin となっている(Ubuntuの場合)が、 Nodeを直接インストールすると /usr/local/bin/node が実行パスになる。

ちなみに、 apt経由で8.10系をインストールした場合はパッケージ名が nodejs でコマンドも node ではなくnodejsになり、 こちらの実行パスは /usr/bin/nodejs となるため、cronで発見される。 別な環境で動いたのはたまたまこちらがインストールされていたため。 ExecJSはこのどちらも探しにいくが、node が優先される。

対応

/usr/local/bin をPATHに連結させて解決。

# ~/.bash_profile
export PATH="$PATH:/usr/local/bin"

crontabに /bin/bash -lc くっつけてcronの実行者の.bash_profile を読み込ませる。

# crontab -e
0 9 0 0 0 /bin/bash -lc {command} > /var/log/cron.log 2>&1

この方法より個別にシェル書いた方が安全という記事も見つけたがそれはまた別の話ということで。

参考

/* Responsive: yes */