Immutable Infrastructureでない環境のPassengerとRubyのアップグレード

Immutable Infrastructure でないホスト OS への環境直接インストールによる稼働中システムに対して、Passenger 4.x と ソースビルドによる Ruby 2.0.0 の環境を、Passenger 5.x と rbenv の Ruby 2.3.0 にするように進めた話を記しておきます。インフラについてはプロジェクトやサービスによりルールが違うと思いますので、あくまで一例としてのご参考までに。前提条件によってもっとこなれた良い手法があると思っています。

これは Immutable Infrastructure でバーンとできない世界線のお話です。

## ざっくりいうと

* 2016年2月24日(木) で EOL を迎えた Ruby 2.0.0 (と Passenger 4.x) を Ruby 2.3.0 (と Passenger 5.x) にアップグレードするはなし
* Immutable Infrastructure の対極にある、ホスト OS への直接インストールである Unrecoverable Infrastructure へのアップグレードは手に汗を握るので、一足飛びに最新バージョンまでアップできないなら、ピボットできるところまで上げる段階戦略をとるといいと思います
* Passenger のインストールは passenger.conf に軽く手を加えれば 4.x から 5.x に移行可能。 (もちろん設定量によりますが) 難しくないよ

## 流れ

Passenger 4 系のある時点の旧いバージョンだと Ruby 2.1 系までしか動かないようなので、上手くアップグレードできなかったときに切り戻せるよういったん Ruby をバージョン切り替え可能な状態にし、然る後に Ruby 2.2 以上も動く Passenger 5 系にアップグレードします。流れとして以下の 2 ステップに分けることができます。

* ステップ1. ソースビルドされた本番環境の Ruby のバージョンを rbenv に置き換えて、Passenger 4.x と Ruby 2.1.8 の組み合わせで動かす
* ステップ2. Ruby 2.3.0 にした上で Passenger 5.x をインストールを行い、Passenger 5.x と Ruby 2.3.0 の組み合わせで動かす

なお、各ステップを本番環境に適用する前に、ステージング環境や CI 環境といった前段の開発環境をアップグレードして動作確認するという移行期間の設計も重要です。
インクリメンタルな開発を前提とした大雑把な作戦として、どのリリースまで旧バージョンで動かし、どのリリースから新バージョンで動かすかを決め、その2点のリリース間に移行を進めるといったことが考えられます。

## ステップ1. Passenger を 4 系のまま、Ruby のバージョンだけ上げる :walking: :dash: 

本節では EOL の Ruby 2.0.0 から脱却するまでを目的としたステップを取りあげます。

### rbenv のインストール

だいたい[こちらの記事](http://qiita.com/inouet/items/478f4228dbbcd442bfe8)などのとおりに行うことでインストールできると思います。

注意点として、インストールしたユーザーのホームディレクトリに rbenv をインストールした場合は、.bashrc など (お使いの環境に合わせて読み替えてください) 起動スクリプトへの rbenv の設定が以下にあるいつもの記述ではその環境の実行ユーザーによってエラーになりうる点です。

```sh
export PATH="$HOME/.rbenv/bin:$PATH"
```

エラーが起きうるケースは、rbenv のインストールユーザーと Passenger の実行ユーザーが異なる場合です。上記のように $HOME 固定だと Passenger の実行ユーザーに変わった際に .rbenv がインストールされていないユーザーのホームディレクトリを参照しようとしてエラーになりますので、以下のように絶対パスに置き換えるなど対処すると良いです。

```sh
export PATH="/home/yourname/.rbenv/bin:$PATH" # username はユーザー名に置き換えて下さい
eval "$(rbenv init -)"
```

Dirty Hack 感があるため、管理者権限がない場合の奥の手としてご参考まで。

### いつもの rbenv によるバージョン指定、gem install bundler と、 bundle install の実行をする

rbenv をインストールしたら既存バージョンの 2.0.0 と、アップグレード先の 2.1.8 を rbenv install しておきましょう。またこの時に、この後の流れで用いる 2.2.4 と 2.3.0 もあわせてインストールしておくと良いです。

* rbenv install で Ruby のバージョンをインストールしておく
* gem install bundler する
* アップグレードする Ruby のバージョンに合わせて bundle install しておく

ひととおりのインストールが終わったら、まずソースビルドの Ruby から rbenv の Ruby に切り替わって動くことを確認するため、ソースコードビルドによる Ruby と同一バージョンの rbenv の Ruby (2.0.0) にしておきましょう。

### PassengerRuby の指定を変える

passenger.conf に記された `PassengerRuby` のパスを、ソースビルドした Ruby のパスから rbenv でインストールした Ruby のパスに書き換えます。

例えばソースビルドされた Ruby が `/usr/local/ruby/bin/ruby` にインストールされているようであれば以下のような設定になっている箇所があると思います。

```
PassengerRuby /usr/local/ruby/bin/ruby
```

1つのやり方としては、その設定箇所を rbenv でインストールされている Ruby の絶対パスに変更するといったことが考えられます。

```
PassengerRuby /home/yourname/.rbenv/shims/ruby
```

別のアプローチとしては、`/usr/local/ruby/bin/ruby` を `/home/yourname/.rbenv/shims/ruby` への symlink にするといったことも考えられます。

### 移行対象のバージョンの Ruby (2.1.8) にする

ソースコードビルドによる Ruby と同一バージョンの rbenv の Ruby (2.0.0) で、動作に問題がないようであれば、Passenger 4.x で動く最新の Ruby バージョンまで足を進めてみましょう　(Passenger 4 系でも Ruby 2.2 系が動くバージョンがあるようですが、試したことがないためここでは割愛します)。

### Good by EOL :rocket: 

ここで一度 EOL から脱却した形でリリースできます。

2.1.8 では、来る Rails 5 が動かないことや Ruby のメンテナンスサイクルから1年以内に EOL になることが分かっているため、動作確認からリリースまでひと段落したら次のステップ2に進みましょう。

## ステップ2. Passenger を 5 系にして、最新の Ruby バージョンで動かす :walking: :dash: 

前節で rbenv が入ったことにより、Ruby のバージョン切り替えができる準備が整っていると思います。ここでは最新の Ruby 2.3.0 までアップグレードすることを目的としたステップを取りあげます。

### Passenger のインストール

passenger のインストールを行います。

```sh
$ rbenv global 2.3.0
$ gem install passenger
$ (sudo) passenger-install-apache2-module
```

なお環境によっては passenger-install-apache2-module を実行する際に、apxs2 のパスを環境変数 APXS2 で指定する必要があります。

```sh
$ export APXS2=apxs2のインストールパス
$ (sudo) passenger-install-apache2-module
```

### passenger.conf を変更する

passenger.conf に記された `PassengerRoot` のパスを、新たにインストール、ビルドした Passenger の so ファイルのパスに書き換えます。

例えばソースビルドされた Ruby が `/usr/local/ruby/bin/ruby` にインストールされているようであれば以下のような設定になります。

```
PassengerRoot /home/yourname/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/passenger-5.0.26
```

なお、[Passenger の公式ドキュメント](https://www.phusionpassenger.com/library/install/apache/migrating/migrating_from_p4_to_p5.html#feature-and-configuration-changes)に以下のようにあります。

> The PassengerTempDir option has been replaced by two config options: PassengerInstanceRegistryDir and PassengerDataBufferDir.

Passenger 4.x での `PassengerTempDir` は、Passenger 5.x で `PassengerInstanceRegistryDir` と `PassengerDataBufferDir` に変わっているためこちらもあわせて変更しておきましょう。

### 移行対象のバージョンの Ruby (2.3.0) にする

無事に Passenger のインストールと、passenger.conf の設定が終わったら Ruby 2.3.0 でサーバーを再起動して、無事に動くことを確認しましょう。

## The Upgrade Must Go On :rocket: 

息の長いサービスのエンハンスを行っていくうえで、Ruby のアップグレードの重要性は色々あります。

* セキュリティパッチに追随する
* 来る Rails 5 では Ruby 2.2.2 以上でないと動かない
* Ruby のバージョンを上げることでパフォーマンスの向上が見込める

またサービス開発のメンバーとして新しいメンバーを誘うとき、誘われるときに EOL の Ruby のバージョンでの開発か最新の Ruby のバージョンでの開発どちらがモチベーションが上がるでしょうか？

以上、開発/運用にもちいる Ruby のバージョンはなるべく上げておくと良いですよというお話でした。

## おまけ

rbenv を使った Ruby のアップグレードということで、bundle install した Gem は Ruby のバージョンごとに管理されることになります。
旧バージョンの Ruby で bundle install していたインストールディレクトリは、無駄にディスク領域を占有することになるため、新バージョンでの安定稼働を見届けたら削除しましょう。