kumak1’s blog

kumak1のイラストや技術ログ

capistranoでデプロイ先にユーザ指定してwheneverを適用させたい

この記事は Pepabo Advent Calendar 2015 - Qiita の19日目の記事です。

capistranoRuby で簡潔によしなにしてデプロイしてくれるので大変便利ですね。拡張ライブラリも豊富ですし Ruby の経験がほとんどなくてもそれとなく記述できて素晴らしいです。(ぺちぱーなもので・・) whenever も cron を Ruby で書けるっていいですね。バージョン管理できるし、レビューも受けやすいし、なにより crontab -r がもう怖くないw 基本的にすんなりと設定できるのですが、掲題の「capistranoでデプロイ先にユーザ指定してwheneverを適用」の時にちょっと詰まったので、備忘録がてらに記事にします。

なんでユーザ指定したいの

ユーザを分ける事自体に疑問を持つ方は、こちらの記事を読むと良さそう。 用は、デプロイする人はデプロイするだけ、バッチ実行する人はバッチ実行するだけと分けたいのです。 セキュリティ的にも、あとで調査等々する場合にも役に立ちます。 www.atmarkit.co.jp

前提

デプロイ先に以下が導入済みだとします。

  • rbenv 導入済み
  • .ruby-version ファイルで Ruby のバージョンを記録
  • バッチ実行用ユーザ(batch)がいる

やりたいこと

  1. capistrano でデプロイし、以下を実行
  2. デプロイ先で、bundle install
  3. デプロイ先で、whenever 実行

具体的には

whenever を素で実行する際の -u オプションを、capistrano でデプロイ時に適用したいわけです。

% bundle exec whenever --help
Usage: whenever [options]
    -i [identifier],                 Default: full path to schedule.rb file
        --update-crontab
    -w, --write-crontab [identifier] Default: full path to schedule.rb file
    -c, --clear-crontab [identifier]
    -s, --set [variables]            Example: --set 'environment=staging&path=/my/sweet/path'
    -f, --load-file [schedule file]  Default: config/schedule.rb
    -u, --user [user]                Default: current user
    -k, --cut [lines]                Cut lines from the top of the cronfile
    -r, --roles [role1,role2]        Comma-separated list of server roles to generate cron jobs for
    -v, --version

では capistrano の設定結果から。

Capfile.rb

# Include tasks from other gems included in your Gemfile
require 'capistrano/rbenv'
require 'capistrano/bundler'
require 'whenever/capistrano'

rbenv, bundler, whenever の gem を追加

deploy.rb

# rbenv の設定
set :rbenv_ruby, File.read('.ruby-version').strip # rbenvが.ruby-versonのRubyを使うようになる
set :rbenv_path, '/usr/local/rbenv'               # デプロイ先のrbenvのpathを指定

# bundler の設定
set :bundle_path, -> { release_path.join('vendor/bundle') } # sharedよりもreleaseのパス指定の方が、切り戻しが必要な際に楽

# whenever の設定
set :whenever_update_flags, ->{ "--update-crontab #{fetch :whenever_identifier} --set #{fetch :whenever_variables} --user batch" }

わけわからないのは多分 whenever の設定だけかと思います。 ライブラリのソースコードを見てみると、whenever の実行オプションを指定している箇所があります。 whenever/whenever.rake at 334cfa01f373006cc032e23907b1777a8ea3f3b0 · javan/whenever · GitHub

これに --user batch を付け加えているだけです、簡単だー。 バッチユーザがデプロイユーザと同じグループに所属しているならばこれでOKですね。 もしダメな場合、以下のような荒技を使えば大丈夫・・ デプロイユーザにsudoer権限が必要になりますが、これでなんとかなってしまいます。

set :whenever_command, ->{ [:sudo, "#{fetch(:rbenv_path)}/bin/rbenv", :exec, :bundle, :exec, :whenever] }

ぷらすあるふぁ

実はこれをいじっていた当初は whenever をユーザ指定して実行がなかなかできず、task を半ば自前で作ってしまっていました。 (調度 set :whenever_update_flags のところで。tsushikazu 先輩の優しいアドバイスで解決)

「やりたいことができそうなのにできない」時は、コードリーディングをすると大体のことは解決できるので、怖がらず・面倒くさがらずにGitHubを覗くのが近道みたいです。 綺麗なコードを読むのはエンジニアの成長には欠かせませんしね。 まだまだ若手感を出して頑張っていこう。