Article Image

VagrantでRVMを使うときによくはまること

開発 hironemuhironemu

弊社サービス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等色々便利なのでどうやって動いているのかほとんど意識することはないのですが、時々分かっていないと大ハマリするので基礎的な動作原理もある程度知っておく必要がありますね。