crontabでplenvとcartonを使ってperlファイルを定期実行

めちゃめちゃハマったので備忘録も兼ねてメモ

環境

OS:CentOS6.4

Perl:5.16.3(plenv使用。システムperlは5.10.1)

cartonを使用している。一般ユーザー名はhomepage

 

今回はAmon2で作っているアプリのscript/apiディレクトリ内のapi.plファイルをcronで実行しました。

ちなみにアプリのトップディレクトリはこんな感じ。

 

➜  App git:(dev-branch) ✗ ls
Build.PL  README.md  api_script.sh  app.psgi  carton.lock  config  cpanfile  lib  local  script  setup.sh  sql  static  t  tmpl  xt

 

/etc/crontabのもともとの中身は下記の通り

SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/

# For details see man 4 crontabs

# 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

 

さて実行させていくわけですが、最初の僕のアプローチ

$ sudo mkdir /var/log/cron.log

でログディレクトリを作っておき(/var/log/cronにはcronを実行したことしかログででないので)アプリのルートディレクトリにシェルファイルを作成

$ vim api.sh
export PATH="$HOME/.plenv/bin:$PATH"
eval "$(plenv init -)"

exec perl -Ilib /アプリルートディレクトリへの絶対パス/script/api/api.pl    

 

そしてcrontabには下記を記述

*/01 * * * * homepage /bin/sh api.sh perl -Ilib /アプリルートディレクトリへの絶対パス/script/api/api.pl > /var/log/cron.log/app.log 2> /var/log/cron.log/app_error.log

 

で、まずログが出ない(笑)

ownerとgroupがrootで書き込み権限がない状態だったのでcron.logディレクトリを一般ユーザーであるhomepageに変更

 

Can't locate App.pm in @INC ~~~~

 

というようなエラー。

ここでplenvで実行するコマンドじゃないしcarton書いてないと気づいたので・・・

シェルスクリプトの中身を変更

$ vim api.sh

export PATH="$HOME/.plenv/bin:$PATH"
eval "$(plenv init -)"

exec plenv exec carton exec perl -Ilib /アプリルートディレクトリへの絶対パス/script/api/api.pl

 

plenvが無いといわれる。。仕方がないので

export PATH="/home/homepage/.plenv/bin:$PATH"

に変更。

 

cronが実行されるのを待つと次は

(ちょっとエラーログを紛失してしまったのですが・・・)

Ther is no command carton in plenv

的な、plenvはあるけどplenvのコマンドとしてcartonなんて無いよというエラーが。

記されているエラーログの .plenv/versions/5.16.3/binに行ってlsしてみると

cartonは完全に存在している。。なぜ。。。(T_T)

 

ちなみにexec plenv exec -- carton exec perl ~~

などとやっても"--"なんてコマンドplenvに無いよと言われる。

 

ここは超ハマったのですが、何が原因だったのかというとまず

crontab内の

SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/

ここの部分でHOME=/としているせいでcron実行の時に/.plenv~を見に行ってしまっていました。なのでそこにはcartonも無ければ--もないと。

 

ちなみにエラーログに

No such file or directory at /home/homepage/.plenv/bin/plenv line 75. 

とあるので中身をみてみると

 
 54 sub CMD_exec {
 55     home_init();
 56
 57     my $bin = shift @ARGV or show_help('exec');
 58
 59     my ($version, $file) = detect_version();
 60
 61     if ($version eq 'system') { 
 62         # remove shims path from ENV[PATH].
 63         $ENV{PATH} = join( 
 64             ':',
 65             grep { File::Spec->canonpath($_) ne File::Spec->canonpath("$PLENV_HOME/shims/") } File::Spec->path() 
 66         ); 
 67     } else { 
 68         my $bindir = "$PLENV_HOME/versions/$version/bin"; 
 69         unless (-x File::Spec->catfile($bindir, $bin)) { 
 70             die "[plenv] There is no $bin in $bindir.(determined by @{[ $file || '-' ]})\n";
 71         } 
 72         $ENV{PATH}="$bindir:$ENV{PATH}";
 73     } 
 74     exec $bin, @ARGV;
 75     die $!; 
 76 }
 

ここでelseにはいって勝手に/.plenv/~~があるような形になっているっぽいです。

だからThere is no 'carton' in /.plenv~~となっていたわけですね。

このせいでplenvは読み取ってくれているように感じてしまっていたわけです。

 

と、一旦こんな感じなのですがもうちょっとちゃんと書きに行くと

シェルスクリプトの中を

  
  1 cd /home/homepage/アプリルートまでのパス
  2 export PATH="$HOME/.plenv/bin:$PATH"
  3 eval "$(plenv init -)"
  4
  5 exec $*

 

として、crontabの中身の方にコマンドを書いておくようにできます。

(1行目でcdコマンドを書いているのはアプリルートディレクトリで実行しなければIlibが生きないためです)

SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/home/homepage

0 0 * * * homepage /bin/sh /アプリルートディレクトリの絶対パス/api.sh plenv exec carton exec -Ilib -- perl /アプリルートディレクトリの絶対パス/script/api/api.pl > /var/log/cron.log/api.log 2> /var/log/cron.log/api_error.log

 といった感じです。

 

plenvはなかなかというか超ややこしい・・・。ただ普段パスとかをあまり意識できていなかったので勉強になりました。

 

=== 補足 ===

perlがシステムのバージョンの方を使われているんじゃないかというのに気づいて調べるために使ったクーロンの中身

 */01 * * * * homepage /bin/sh /home/homepage/アプリまでのルート/api.sh perl -V > /home/homepage/test.log 2> /home/homepage/test_error.log

これでシステムperlの方を使っていることがログで確認できました。