産業で
whenever 実行するユーザの
環境変数 PATH
に
/usr/local/bin
を追加しろ
2020/01/10 追記
以下のように whenever の設定ファイルに PATH を設定すると PATH を設定できる。 ログ出力と合わせて記述すると確認しやすくて良い、こっちのほうが楽ちんですね。 しかし、他の cronjob が設定されていると、ぶつかって crontab が壊れるので注意しないといけない。
config/schedule.rb
set :output, "#{Rails.root}/log/cron.log" env :PATH, "/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin"
なんでこうなる
whenever を設定すると、登録される cron はだいたいの場合において、以下のようなものになると思われる。
0 0 * * * /bin/bash -l -c 'cd ${RAILS_ROOT} && RAILS_ENV=${環境名} bundle exec rake ${タスク名} --silent >> log/crontab.log 2>&1'
ここで /bin/bash -l -c
を見てみましょう。 AmazonLinux2 の man bash
ではこう書かれています。
-l
-c string
- If the -c option is present, then commands are read from string. If there are arguments after the string, they are assigned to the positional parameters, starting with $0.
-c
オプションが指定されると、コマンドが string から読み込まれます。 string の後に引き数があれば、これらは 位置パラメータ (positional parameter: $0 から始まるパラメータ) に代入されます。
和訳はこちらから。 Man page of BASH
つまり、cron 起動時に、ユーザログインした状態(環境変数などを)を再現して、引数に指定されたコマンドを実行する、といったテイストですね。何もしないと cron のジョブ実行時の環境変数やらはお寒いので、( -l
は余計なものも読み込むからやめろ!と言われますが ) 解決するアプローチとしては良いと、個人的には思っております。
で、何が問題かというと、上記の状態で cron が動作すると、 log.crontab.log
にこのように記録されます。
/bin/bash: bundle: command not found
なして!
なぜコマンドがないのか
ここでコマンドの有無を、 cron を設定した&実行を期待されるユーザ ec2-user
で確認しましょう。
$ which bundle /usr/local/bin/bundle $ whereis bundle bundle: /usr/local/bin/bundle
ありますねえ……? ログインシェルのように起動して振る舞うなら、このコマンドを見失うのはおかしいですね。 では、実際に cron での動作を確認してみましょう。
0 0 * * * /bin/bash -lx -c 'which bundle' > /tmp/result.txt
ハイ、見事に空ですね。 コマンドは見当たりませんでした、ナンデ! という訳でこういう時は環境変数 PATH
ですね、上記と同じようにそれぞれ確認してみましょう。
PATH の中身を確認する
$ echo $PATH /usr/bin:/usr/local/sbin:/usr/sbin:/home/ec2-user/.local/bin:/home/ec2-user/bin:/usr/local/bin
0 0 * * * /bin/bash -lx -c 'env $PATH' →結果 + PATH=/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/home/ec2-user/.local/bin:/home/ec2-user/bin
/usr/local/bin おらんなった
おしりにいたはずの /usr/local/bin
おらんなった、なんじゃこれ。
どうしていないのか
どうやら bash のバグかなんからしいです。デフォルトの PATH として、 /usr/local/bin
もハードコートされているらしいのですが、どこかでポロっとおっことしているようで。 なので対策として、 sshd_config
が ssh でログインしてきたときに、 PATH に自動的に付与しているようです。
これずっと治ってないって外人兄貴達が言ってるんだけど実際どうなんじゃろ?
結局どうすればいいのか
- cron の先頭に
PATH
を記載して、実行時に環境変数を増やす bash -l
でログインシェル起動っぽく振る舞うので、そもそも.bash_profile
なりの環境変数 PATH に/usr/local/bin
を追加してしまう/usr/bin
はベンダの動作確認したコマンドで、それで足りなかったりオーバーライドしたい場合、/usr/local/bin
を使う、というふうに僕は聞いたんだけど、そうなら先頭に追加したほうがよさそうすねこれ
whenever を使う場合であれば、管理する箇所が増えるのだるいので .bash_profile
なりの環境変数 PATH に追加するのが良いと思います。 ただその場合、以下のように sshd_config
の親切心と合わせてちょっと PATH が汚くなるので、それはご愛嬌ってことで。
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/ec2-user/.local/bin:/home/ec2-user/bin:/usr/local/bin