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の方を使っていることがログで確認できました。