ruby 実行環境の構築 - rbenvの使い方とその仕組み

rbenvとruby-buildのインストール方法

$ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
$ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
$ source ~/.bash_profile

.bash_profileは自分の使っているシェルに応じて.zshrcなどに読み替えてください。

インストール可能なrubyバージョンの一覧表示

$ rbenv install -l
Available versions:
  1.8.6-p383
  1.8.6-p420
  1.8.7-p249
  1.8.7-p302
  1.8.7-p334
  (省略)

指定したバージョンのrubyをインストール

下記はバージョン2.1.2をインストールする例です。

$ rbenv install 2.1.2

使用するrubyのバージョンを指定する

rbenvでは、ディレクトリ階層ごとに使用するrubyのバージョンを指定できます。 下記はカレントディレクトリ以下で2.1.2を使用する例です。

$ rbenv local 2.1.2

rbenvによる指定が意図したように行われているかどうかは以下のように確認できます。

$ ruby --version
ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-darwin13.0]

インストール済みrubyバージョンの一覧表示

$ rbenv versions
  system
  2.0.0-p353
  2.1.0
* 2.1.2 (set by ~/projects/xxx/.ruby-version)

systemは、/usr/bin/rubyのようにrbenv以外の方法でシステムへインストールされたrubyです。 どのパスのコマンドが実行されるかはPATH環境変数によって決まります。

アスタリスクの付いているバージョンが選択されたバージョンです。 カッコの内容はrbenvがそのバージョンを選択した理由です(後述)。

rbenvとは

rbenvは複数のバージョンのruby実行環境を切り替えるための仕組みです。

https://github.com/sstephenson/rbenv

rbenvには、同一の作者による ruby-build というプラグインがあります。 これは指定したrubyのソースをダウンロードして、ローカルでビルドするための仕組みです。

https://github.com/sstephenson/ruby-build

rbnevの類似のものとして rvm があります。

https://rvm.io/

rbenvの仕組み

rbenvではPATH環境変数とシンボリックリンクを用いて、 rubyやirbのようなコマンドの呼出経路に割り込むことでバージョンの切り替えを行っています。

ruby、irb、gem、およびrspecやrakeなどgemによってインストールされるコマンド群は、 以下のパスにシンボリックリンクが作られます。

~/.rbenv/shims/

これらのシンボリックリンクは、rbenv rehashが実行されるたびに更新されます。

また、インストールの際にシェルのrcスクリプトに追加したeval "$(rbenv init -)"というコマンドでは、下記が行われています。

  • PATH 環境変数の更新(export PATH="~/.rbenv/shims:${PATH}")
  • rbenv rehashの実行

これにより、ruby、irbなどのruby関連コマンドはrbenvの影響下にある~/.rbenv/shims/以下のシンボリックリンクに置き換えられています。

$ which irb
~/.rbenv/shims/irb

バージョン決定の仕組み

shims以下のコマンドでは以下のように起動するrubyインタプリタのバージョンを決定します。

  1. RBENV_VERSION環境変数の中身。
  2. スクリプトの置いてあるディレクトリから上位にさかのぼって最初に見つかった.ruby-versionファイルの中身。
  3. カレントディレクトリから上位にさかのぼって最初に見つかった.ruby-versionファイルの中身。 このファイルはrbenv localコマンドで作成することができます。
  4. ~/.rbenv/version ファイルの中身。 このファイルはrbenv globalコマンドで作成することができます。

つまり、シェルのカレントディレクトリとスクリプト自体が置いてあるディレクトリが異なり、 それぞれ.ruby-versionファイルが存在するような場合、後者が優先されます。 また、RBENV_VERSION環境変数は常に優先されます。

例えば製品環境でgodとrbenvを使ってデーモンプロセスのrubyバージョンを指定するような場合にはこの点に注意する必要があります。

各バージョンの実体

rbenv installでインストールしたruby実行環境は~/.rbenv/versionsディレクトリの下に保存されます。

~/.rbenv/versions/
├── 2.0.0-p353
│   ├── bin
│   ├── include
│   ├── lib
│   └── share
├── 2.1.0
│   ├── bin
│   ├── include
│   ├── lib
│   └── share
└── 2.1.2
    ├── bin
    ├── include
    ├── lib
    └── share

gemもこのディレクトリ以下に保存され、例えば2.1.2にインストールしたnokogiri1.6.1の中身を見たければ以下のディレクトリにあります。

~/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/nokogiri-1.6.1/

rbenv install(ruby-build)の動作

rbenv installは概ね以下の様な動作をします。

  1. CDNで配布されているrubyのソースコードをダウンロード
  2. ダウンロードしたソースコードを展開してビルド

このCDNは本稿執筆時点ではAmazon CloudFrontが使用されており、そのスポンサーはBasecampです。(参考)

BasecampはRailsを開発したDHHがCTOをやっている会社です。ありがとうBasecampのみなさん。

なおBasecampは去年まで37signalsという名前でした。こちらの方が馴染みが深い人が多いかもしれません。

ソースコードの配布URLはruby-buildに記述されています。

具体的には以下のパスに1ファイル1バージョンで書かれています。

$ ls -1 ~/.rbenv/plugins/ruby-build/share/ruby-build/
1.8.6-p383
1.8.6-p420
1.8.7-p249
1.8.7-p302
1.8.7-p334
1.8.7-p352
1.8.7-p357
1.8.7-p358
1.8.7-p370
1.8.7-p371
(省略)

opensslなど依存パッケージが同時に配布されている場合もあり、ビルド環境に応じてそれらもダウンロードされます。 またコードのURLにはSHA2のチェックサムが含まれており、shasumなどのツールが利用できる環境であれば自動的に改ざん検知が行われます。

$ cat ~/.rbenv/plugins/ruby-build/share/ruby-build/2.1.2
install_package "openssl-1.0.1h" "https://www.openssl.org/source/openssl-1.0.1h.tar.gz#9d1c8a9836aa63e2c6adb684186cbd4371c9e9dcc01d6e3bb447abf2d4d3d093" mac_openssl --if has_broken_mac_openssl
install_package "ruby-2.1.2" "http://cache.ruby-lang.org/pub/ruby/2.1/ruby-2.1.2.tar.gz#f22a6447811a81f3c808d1c2a5ce3b5f5f0955c68c9a749182feb425589e6635" ldflags_dirs standard verify_openssl

新しいバージョンのrubyがリリースされたら下記のように更新すれば反映されます。

$ cd ~/.rbenv/plugins/ruby-build
$ git pull

環境変数RUBY_BUILD_CACHE_PATHでキャッシュディレクトリが指定されている場合、 rbenv installはコードをダウンロードする代わりにそのパスに置いてあるファイルを使用します。 キャッシュディレクトリに該当するファイルがない場合にはCDNからダウンロードしたファイルをキャッシュディレクトリに保存します。

あらかじめ取得しておいたソースを使用することで、 インターネットへの接続が制限されている環境でもrbenvを使用することができます。

また、製品環境でrbenvを利用する場合にもキャッシュを用いることで外的要因に依存せずプロビジョニングを行うことができるようになります。