system-wide rbenv + passenger (ver. 2015-11)

# 更新履歴

- 2020-05-08
    - もう5年近く前に書いた記録だが、未だにこの手法は現役で有効
    - sudoersの部分を加筆修正、secure_pathを使うやり方を追加した

# 概要

いっつも「あれどうやんだっけ」と若干悩むので、そのままコピペ出来るリファレンスとして作成。と言いつつもDebian/Ubuntuは若干不足。

# Installation steps

## Install dependent packages

**CentOS 7**

```bash:CentOS7
# 全てrootユーザーで実行
sudo su -

yum -y group install development

# ruby-buildに必要なパッケージをインストールする
# OS毎に必要なパッケージの一覧がruby-buildのWikiに記載されているので、最新版の一覧はWiki参照
# https://github.com/sstephenson/ruby-build/wiki
yum install -y gcc openssl-devel libyaml-devel libffi-devel readline-devel zlib-devel gdbm-devel ncurses-devel
```

**Debian 8 / Ubuntu 14.04**

```bash:Debian8/Ubuntu14.04
# 全てrootユーザーで実行
sudo su -

apt-get install build-essential -y
apt-get install git -y

# ruby-buildに必要なパッケージをインストールする
# OS毎に必要なパッケージの一覧がruby-buildのWikiに記載されているので、最新版の一覧はWiki参照
# https://github.com/sstephenson/ruby-build/wiki
apt-get install autoconf bison build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev libgdbm3 libgdbm-dev
```

## Install rbenv and ruby

```bash
# 本体
git clone https://github.com/sstephenson/rbenv.git /usr/local/opt/rbenv

# プラグイン
# - ruby-build (必須)
# - rbenv-gemsets
git clone https://github.com/sstephenson/ruby-build.git /usr/local/opt/rbenv/plugins/ruby-build
git clone https://github.com/jamis/rbenv-gemset.git     /usr/local/opt/rbenv/plugins/rbenv-gemset

cat << 'EOF' > /etc/profile.d/rbenv.sh
export RBENV_ROOT="/usr/local/opt/rbenv"
export PATH="$RBENV_ROOT/bin:$PATH"
eval "$(rbenv init -)"
EOF

source /etc/profile.d/rbenv.sh

rbenv install -l
# list of versions available

export TARGET_RUBY=2.2.4
rbenv install $TARGET_RUBY

# check
RBENV_VERSION=$TARGET_RUBY ruby -v

# bundlerだけはruby version globalなgemとしてインストールする
RBENV_VERSION=$TARGET_RUBY gem install bundler
```

## Configure sudo

2パターンある。

1. secure_pathを無効化する
2. secure_pathにrbenv関連のパスを追加する

以下、CentOS 7の例。

**CentOS 7**

```bash:1.secure_pathを無効化する
cat << 'EOF' > /etc/sudoers.d/rbenv
Defaults !secure_path
Defaults env_keep += "PATH"

Defaults env_keep += "RBENV_ROOT RBENV_VERSION RBENV_GEMSETS"
EOF
```

```bash:2.secure_pathにrbenv関連のパスを追加する
cat << 'EOF' > /etc/sudoers.d/rbenv
Defaults env_keep += "RBENV_ROOT RBENV_VERSION RBENV_GEMSETS"
EOF

visudo
# secure_pathに以下を追加する
# - /usr/local/opt/rbenv/shims
# - /usr/local/opt/rbenv/bin

# デフォルト
Defaults    secure_path = /sbin:/bin:/usr/sbin:/usr/bin

# 変更後
Defaults    secure_path = /usr/local/opt/rbenv/shims:/usr/local/opt/rbenv/bin:/sbin:/bin:/usr/sbin:/usr/bin
```

rbenvグループでrbenv関連のコマンドをNOPASSWDで実行できるようにする。

グループは既にあるグループを使っても良いし、ユーザー単位で指定出来るならグループは作らなくても良い。以下は `rbenv` グループを別途作成する例。

```bash
cat << 'EOF' > /etc/sudoers.d/rbenv_nopasswd
Cmnd_Alias RBENV = /usr/local/opt/rbenv/bin/
%rbenv ALL = (ALL) NOPASSWD: RBENV
EOF

visudo
# エラーが出ないかチェック
# これを実行することにより /etc/sudoers.d/* のファイルのパーミッションも是正してくれる

# グループ作成
# -rを付けてシステムグループにしておく
groupadd -r rbenv

# 作成したグループにユーザーを追加する
#gpasswd -a hoge rbenv
```


## Install passenger

```bash
rbenv gemset create $TARGET_RUBY passenger

RBENV_VERSION=$TARGET_RUBY RBENV_GEMSETS=passenger gem install passenger
rbenv rehash
RBENV_VERSION=$TARGET_RUBY RBENV_GEMSETS=passenger passenger-install-apache2-module
# パッケージのチェックで不足が表示されたら追加でインストールして再度実行
```

`passenger-install-apache2-module`でコンパイルが成功すると、最後にApacheのconfig記載例が表示されるので、コピーしておく。このパスはrubyやpassengerのバージョン、passengerをインストールしたgemsetの名前などによって変わるので注意。またconfigの記載場所はディストリによってポリシーが違い変わってくるので、よしなに。

以下、CentOS 7の例。

**CentOS 7**

```bash:CentOS7
vim /etc/httpd/conf.modules.d/02-passenger.conf
```

```apache:/etc/httpd/conf.modules.d/02-passenger.conf
LoadModule passenger_module /usr/local/opt/rbenv/versions/2.2.4/gemsets/passenger/gems/passenger-5.0.21/buildout/apache2/mod_passenger.so
```

```bash:CentOS7
vim /etc/httpd/conf.d/passenger.conf
```

```apache:/etc/httpd/conf.d/passenger.conf
<IfModule mod_passenger.c>
    PassengerRoot /usr/local/opt/rbenv/versions/2.2.4/gemsets/passenger/gems/passenger-5.0.21
    PassengerDefaultRuby /usr/local/opt/rbenv/shims/ruby
    PassengerInstanceRegistryDir /var/lib/passenger
</IfModule>
```

`PassengerDefaultRuby`は表示されたものではなく、rbenvのshimを指定しておく。こうしておくと、passengerを使うアプリのディレクトリにある`.ruby-version`のrubyでそのアプリは動かしてくれるようになる。1つのApache+Passengerで、複数のアプリを、アプリごとにrubyのバージョンを分けて実行できる。

以下、続き。

```bash:CentOS7
# PassengerInstanceRegistryDirで指定したディレクトリを作成 (詳細は下記参照)
# 予め作っておかないとhttpdの起動に失敗する
mkdir /var/lib/passenger

systemctl restart httpd
```

CentOS 7の場合、`PassengerInstanceRegistryDir`を指定しておかないと`passenger-config restart-app`等ができなくなる。

[Issue 1106: On CentOS / RHEL version 7 passenger-status does not work as Apache is started with PrivateTmp=true](https://code.google.com/p/phusion-passenger/issues/detail?id=1106)

上記リンク先はPassenger 4の場合についての記載だが、5でも同じ。ただ指定するパラメータが4の時は`PassengerTempDir`だったのが、5では`PassengerInstanceRegistryDir`に変わった。`passenger-config`等を実行するときは環境変数`PASSENGER_INSTANCE_REGISTRY_DIR`を指定して実行する。

```bash:CentOS7
PASSENGER_INSTANCE_REGISTRY_DIR=/var/lib/passenger RBENV_VERSION=2.2.4 RBENV_GEMSETS=passenger passenger-config restart-app /
```

`sudo`経由で呼び出すときは、この環境変数が引き継がれるようにenv_keepに項目を足すか、`sudo -E`で実行する必要がある。

# 補足

## cronから使う

rbenvで作ったアプリをcronから呼び出すときは、crontabの中で環境変数を設定してあげるか、呼び出すコマンドの頭の方に環境変数を設定するワンライナーを追加する。

```bash:crontab
RBENV_ROOT=/usr/local/opt/rbenv
PATH=/usr/local/opt/rbenv/shims:/usr/local/opt/rbenv/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

0 3 * * * cd /var/www/rails/hoge/current && bundle exec rake fuga
```

## その他もろもろ

- rbenv-binstubsはsystem-wide installでは使えない。残念。なのでsystem-wideなrbenvの時はいちいちbundle execする必要あり。
- そもそもsystem wideインストールするのってどうなのよ、という意見はご尤も。ただ内部向けのアプリとか小さいものなら、結構一台のサーバーに詰め込んでデプロイするなんて状況は普通にありそうなので、そういう時はなんだかんだでrbenv入ってると楽だよね、という話。