VagrantでRVMを使うときによくはまること
弊社サービスHachikinではfnichol/chef-rvmを使ってRuby環境を構築しているのですが、Vagrantを使っている場合に直ぐにハマってしまうことがあるのでメモがてらまとめておきます。
まず、はじめにVagrantから起動されるサーバで使われるChefをバージョン指定してインストールするための、vagrant-omnibusというプラグインをインストールします。(すでにChefがインストールされている場合は、一旦アンインストールしたほうがいいかと思います。)
$ vagrant plugin install vagrant-omnibus
Vagrantfileに次の行を追加します。
config.omnibus.chef_version = "11.8.0"
※ 参考: vagrant-omnibusで簡単Chef Client/Chef Soloインストール
この設定をいれて、vagrant provisionすると以下のディレクトリにChefやRubyがインストールされます。
/opt/chef/bin/chef-solo
/opt/chef/embedded/bin/ruby
また、chef-rvmのレシピを実行したあとサーバ(Vagrantを使って出来る環境のことをここではサーバと言います)上に/usr/local/bin/chef-solo
, /usr/local/bin/chef-client
というファイルができます。これは、RVMを使用している時chef-solo(またはchef-client)コマンドを実行しても、システムにインストールされたRubyの環境でchef-soloを実行してくれるようにするラッパーのようです。
次に、chef-rvmがvagrant-omnibusでインストールしたChefを参照するように以下の設定を追加します。ここで指定したパスが上記のラッパーファイルに記載されます。
chef.json = {
"rvm" => {
"vagrant" => {
'system_chef_solo' => '/opt/chef/bin/chef-solo',
'system_chef_client' => '/opt/chef/bin/chef-client'
}
}
}
因みに、RVMはシステムワイドでインストールしているのでVagrantfileでは以下のように記述しています。(システムワイドでなければ、今回とはまた違った対応になるのかも知れません)
chef.add_recipe "rvm::vagrant"
chef.add_recipe "rvm::system"
ここまでの設定をまとめると次のような感じです。(以下は今回関係する部分だけを抜き出したものです)
Vagrant.configure("2") do |config|
config.omnibus.chef_version = "11.8.0"
config.vm.define :master do |c|
c.vm.provision :chef_solo do |chef|
chef.add_recipe "rvm::vagrant"
chef.add_recipe "rvm::system"
chef.json = {
"rvm" => {
"vagrant" => {
'system_chef_solo' => '/opt/chef/bin/chef-solo',
'system_chef_client' => '/opt/chef/bin/chef-client'
}
}
}
end
end
end
よくでるエラー
2回目以降のvagrant provision
で出るかもしれないエラー
/usr/local/bin/chef-solo: line 23: /opt/ruby/bin/chef-solo: No such file or directory
Chef never successfully completed! Any errors should be visible in the
output above. Please fix your recipes so that they properly complete.
これは、chef-rvmを実行した後に出来るラッパーファイル(/usr/local/bin/chef-solo
)に記述されている「システムのchef-solo」の場所が間違っている可能性があります。Vagrantfileに記述する「system_chef_solo」の設定があっているか確認しましょう。そもそも、システムのChefのインストール場所が分からないという場合、そっちから先に解決する必要があります。(vagrant-omnibusの場合、上にも書いているように/opt/chef/bin/chef-solo
にあるかもしれません)
ただし、Vagrantfileを直しただけだとvagrant provision
した時に必ず上記のエラーになり先に進めませんので、一旦はサーバ上のラッパーファイル(/usr/local/bin/chef-solo
)の内容を直接書き換えておく必要があります。書き換える内容は、ファイルの最後の行のexecのあとのパスです。ここを修正して、もう一度vagrant provision
を試してみましょう。(ここでは/opt/chef/bin/chef-solo
に修正しています)
#!/usr/bin/env bash
# RVM-aware chef-solo wrapper
#
# Generated by Chef for master.hachik.in
# Local modifications will be overridden
if [[ -d "/usr/local/rvm" ]] ; then
export PATH="/bin:/usr/local/rvm:$PATH"
rvm_path='/usr/local/rvm'
export rvm_path
unset RUBY_VERSION
unset GEM_HOME
unset GEM_PATH
unset MY_RUBY_HOME
unset IRBRC
rvm_ruby_string='system'
export rvm_ruby_string
unset rvm_gemset_name
unset MAGLEV_HOME
fi
exec /opt/chef/bin/chef-solo "$@"
サーバ上のChefを手動で入れようとして色々やったあとに出るかもしれないエラー。
/opt/chef/embedded/lib/ruby/site_ruby/1.9.1/rubygems/dependency.rb:247:in `to_specs': Could not find chef (>= 0) amongst [bundler-1.5.0, bundler-unload-1.0.2, executable-hooks-1.2.6, gem-wrappers-1.2.4, gem-wrappers-1.2.1, rubygems-bundler-1.4.2, rvm-1.11.3.8] (Gem::LoadError)
from /opt/chef/embedded/lib/ruby/site_ruby/1.9.1/rubygems/dependency.rb:256:in `to_spec'
from /opt/chef/embedded/lib/ruby/site_ruby/1.9.1/rubygems.rb:1231:in `gem'
from /usr/bin/chef-solo:22:in `<main>'
Chef never successfully completed! Any errors should be visible in the
output above. Please fix your recipes so that they properly complete.
vagrant-omnibusを使う前に、Chefを手動(gem install chef
)でバージョンアップしたところ/usr/local/bin/chef-solo
にインストールされてしまいました。そのため、rvm-chefのラッパーファイルを上書きしてしまったため上記のエラーが発生しました。この時は、一旦下記のようにRVMデフォルトの設定をシステムに変更して、provisionしなおして対応しました。
rvm use system --default
こうすることで、一旦はrvmを使わずに常にシステムの方を参照する形になるので、vagrant provision
が正しく動く可能性があります。
Vagrantを使っている場合は、手動でChefをインストールしないほうがいいかもしれません(手動でインストールされた結果/usr/local/bin
のファイルを上書きしてしまう可能性があるので)。vagrant-omnibusを使っている場合は、/opt/chef
以下にインストールされるので競合することは今のところありません。
というわけで、色々ハマりどころがあります。久しぶりにChefを使おうとするといつもここでつまづきます。Vagrant、Chef等色々便利なのでどうやって動いているのかほとんど意識することはないのですが、時々分かっていないと大ハマリするので基礎的な動作原理もある程度知っておく必要がありますね。