CentOS 6.2のrbenvな環境でPassengerを動かしてみる

Railsアプリを本番環境にデプロイする上で,このPassengerは欠かせません.
前に行ったときは,なんだかんだ面倒くさかったような覚えがありますが,「passenger-install-apache2-module」コマンドなるものがあって,問題なくApacheモジュールを作成してくれます.

それでは,やってみましょう.

Curl,OpenSSL,Zlib,Apache2,Apache Portable Runtime (APR),APR Utility (APU)
それぞれの開発用ヘッダーファイル(ライブラリ)が必要なので,前もってインストールしておきます.

# yum install curl-devel httpd-devel apr-devel

現在のRuby環境の状態

# which ruby
/usr/local/rbenv/shims/ruby

rbenvを利用していますが,ユーザホーム上ではなく,前もってシステムワイドにインストールしております.

# rbenv version
1.9.3-p194 (set by /usr/local/rbenv/version)

実際のpathは以下のとおり.

# rbenv which ruby
/usr/local/rbenv/versions/1.9.3-p194/bin/ruby

現在のローカルgemsは以下のとおり.

# gem list

bigdecimal (1.1.0)
bundler (1.1.5)
io-console (0.3)
json (1.5.4)
minitest (2.5.1)
rake (0.9.2.2)
rdoc (3.9.4)

passengerのgemをインストール

# gem install passenger

Rehashを忘れずに...

# rbenv rehash

現在のローカルgemsはこうなりました.

# gem list

bigdecimal (1.1.0)
bundler (1.1.5)
daemon_controller (1.0.0)
fastthread (1.0.7)
io-console (0.3)
json (1.5.4)
minitest (2.5.1)
passenger (3.0.15)
rack (1.4.1)
rake (0.9.2.2)
rdoc (3.9.4)

passengerと共に,daemon_controller,fastthreadが増えています.

Apache2のモジュールインストーラを実行します.

# passenger-install-apache2-module

色々と開発ライブラリをチェックして,ビルド作業が始まります.

----------------------------------------------------
The Apache 2 module was successfully installed.

Please edit your Apache configuration file, and add these lines:

   LoadModule passenger_module /usr/local/rbenv/..(略)../mod_passenger.so
   PassengerRoot /usr/local/rbenv/..(略)../gems/passenger-3.0.15
   PassengerRuby /usr/local/rbenv/..(略)../bin/ruby

After you restart Apache, you are ready to deploy any number of Ruby on Rails
applications on Apache, without any further Ruby on Rails-specific
configuration!

上記3行をhttpd.confに追記して,httpdを再起動しろと言ってきます.

また,この後

--------------------------------------------
Deploying a Ruby on Rails application: an example

Suppose you have a Rails application in /somewhere. Add a virtual host to your
Apache configuration file and set its DocumentRoot to /somewhere/public:

   <VirtualHost *:80>
      ServerName www.yourhost.com
      # !!! Be sure to point DocumentRoot to 'public'!
      DocumentRoot /somewhere/public    
      <Directory /somewhere/public>
         # This relaxes Apache security settings.
         AllowOverride all
         # MultiViews must be turned off.
         Options -MultiViews
      </Directory>
   </VirtualHost>

という様に,Railsアプリをデプロイするならこんな風に書けよと言ってきます.

とりあえず,httpd.confに直接追記するのではなく,CentOS方式で,/etc/httpd/conf.dに,passenger.confというファイルを作成して,そこに上記3行を記述しました.

# vi /etc/httpd/conf.d/passenger.conf

しかし,httpd再起動時に失敗

# /etc/init.d/httpd restart
httpd を停止中:                                            [  OK  ]
httpd を起動中: httpd: Syntax error on line 221 of /etc/httpd/conf/httpd.conf: Syntax error on line 5
 of /etc/httpd/conf.d/passenger.conf: Cannot load /usr/local/rbenv/..(略)../passenger-3.0.15/ex
t/apache2/mod_passenger.so into server: /usr/local/rbenv/..(略)../passenger-3.0.15/ext/apach
e2/mod_passenger.so: failed to map segment from shared object: Permission denied

どうやらSELinuxが邪魔しているご様子.
そんな訳で,モジュールの実体「mod_passenger.so」を,/usr/lib64/httpd/modulesにコピーしておきます.
(シンボリックリンクを張るだけでは上手くいかない...)

# cp /usr/local/rbenv/..(略)../mod_passenger.so /usr/lib64/httpd/modules

SELinuxのポリシー付きで表示すると

# ls -Z /usr/lib64/httpd/modules

-rwxr-xr-x. root root system_u:object_r:httpd_modules_t:s0 libphp5.so
-rwxr-xr-x. root root system_u:object_r:httpd_modules_t:s0 mod_actions.so
  :
-rwxr-xr-x. root root system_u:object_r:httpd_modules_t:s0 mod_negotiation.so
-rwxr-xr-x. root root unconfined_u:object_r:httpd_modules_t:s0 mod_passenger.so
-rwxr-xr-x. root root system_u:object_r:httpd_modules_t:s0 mod_proxy.so

mod_passenger.soはunconfinedになっています.コピーするだけじゃダメなんですね.

restoreconコマンドを実行して

# restorecon -RFv /usr/lib64/httpd/modules/mod_passenger.so 
restorecon reset /usr/lib64/httpd/modules/mod_passenger.so context unconfined_u:object_r:httpd_module
s_t:s0->system_u:object_r:httpd_modules_t:s0

きちんとポリシーが適用されました.

再度,passenger.confを編集

# vi /etc/httpd/conf.d/passenger.conf

[/etc/httpd/conf.d/passenger.confの内容]

-----------------------
#
# Passenger.conf
#

LoadModule passenger_module modules/mod_passenger.so
PassengerRoot /usr/local/rbenv/..(略)../lib/ruby/gems/1.9.1/gems/passenger-3.0.15
PassengerRuby /usr/local/rbenv/..(略)../bin/ruby

RailsBaseURI /sample
RackBaseURI /sample   # > Rails 3

ここで,Apacheのバーチャルホスト機能を使用せず,「RailsBaseURI」キーワードを使って,Railsアプリの場所を指定しています.

Railsアプリのデプロイ先が,/home/hoge/rails/sampleとすると...

まず,/home/hogeに対し,全てのユーザに実行権限を与えておきます.

# chmod +x /home/hoge

ApacheのドキュメントルートにRailsアプリ先ディレクトリのシンボリックリンクを作成します.
(Railsルートではなく,publicディレクトリですのであしからず...)

# cd /var/www/html
# ln -s /home/hoge/rails/sample/public ./sample

今度は,Railsアプリ側の設定です.

# cd /home/hoge/rails/sample

Passengerを利用する場合は,productionモードで実行されますので,データベース設定関係を再度確認.

# vi config/database.yml

CSSとかJavaScriptとかコンパイルが必要なので,次のコマンドを実行
(Bandlerを利用していますので,bundle execを付加しています)

# bundle exec rake assets:precompile

でもって,デーモンを再起動

# /etc/init.d/httpd restart
httpd を停止中:                                            [  OK  ]
httpd を起動中:                                            [  OK  ]

さて,ブラウザからRailsアプリにアクセスして,上手くいくか!

と思ったらダメでした...

------------------------
Forbidden

You don't have permission to access /sample/ on this server.
------------------------

やはり,SELinuxが何かしているご様子.

そこで,次のサイトを参考に,SELinuxのポリシーを作成して適用してみます.

Deploy to RHEL5 and passenger with selinux running : bang_head_on_wall
 http://www.bangheadonwall.net/?p=343

まずは,SELinuxを無効にして動作可能にしてログを採取します.

# setenforce 0
# getenforce 
Permissive

予めログを削除し,デーモンを再起動します.

# rm /var/log/audit/audit.log
# /etc/init.d/auditd restart
# /etc/init.d/httpd restart

もちろん,このSELinuxが無効な状態なので,Railsアプリは問題なく動きます!

では,ログからポリシーファイルを作成します.

# cd
# grep httpd /var/log/audit/audit.log |audit2allow -M passenger

******************** IMPORTANT ***********************
To make this policy package active, execute:

semodule -i passenger.pp

カレントディレクトリに次のようなファイルが作成されます.

# ls
passenger.pp  passenger.te

passenger.teはテキストファイルなので中身が確認できます.passenger.ppはそれをコンパイルしたものと思われ...

ポリシーを適用します.

# semodule -i passenger.pp

それでは,再びSELinuxを有効にして,デーモンを再起動!

# setenforce 1
# /etc/init.d/auditd restart
# /etc/init.d/httpd restart

やっぱり,上手くいかず...

------------------------
Forbidden

You don't have permission to access /sample/ on this server.
------------------------

とりあえず,今適用したポリシーを削除しておきます.

# semodule -r passenger

ログを見る限りでは,SELinuxは,どうやらPassenger自体の起動を許していないみたい...

困ったときは,リファレンスを見てみようということで,Passengerのリファレンスを見るとちゃんと書いてあるではないですか!

# less /usr/local/rbenv/..(略)../passenger-3.0.15/doc/Users\ guide\ Apache.txt

[Users guide Apache.txtの内容(抜粋)]

----------------
If you are sure that the 'PassengerRoot' configuration option is set correctly,
then this problem is most likely caused by the fact that you're running Apache
with SELinux. On Fedora, CentOS and RedHat Enterprise Linux, Apache is locked
down by SELinux policies.

To solve this problem, you must set some permissions on the Phusion Passenger files
and folders, so that Apache can access them.

- If you've installed Phusion Passenger via a gem, then run this command to determine
  Phusion Passenger's root folder:
+
------------------------------------------------------------------
passenger-config --root
------------------------------------------------------------------
+
Next, run the following command:
+
------------------------------------------------------------------
chcon -R -h -t httpd_sys_content_t /path-to-passenger-root
------------------------------------------------------------------
+
where '/path-to-passenger-root' should be replaced with whatever
`passenger-config --root` printed.

- If you've installed Phusion Passenger via the source tarball, then run the following
  command:
+
------------------------------------------------------------------
chcon -R -h -t httpd_sys_content_t /path/to/passenger/folder
------------------------------------------------------------------

Once the permissions are fixed, restart Apache.

つまり,Passengerのルートに「httpd_sys_content_t」型のポリシーを適用しなさいということですな.

まずは,Passengerのルートディレクトリを確認して...

# passenger-config --root
/usr/local/rbenv/..(略)../gems/passenger-3.0.15

現在のポリシーを確認します.

# ls -Z /usr/local/rbenv/..(略)../gems/passenger-3.0.15
 :
drwxr-xr-x. root root unconfined_u:object_r:usr_t:s0   bin
drwxr-xr-x. root root unconfined_u:object_r:usr_t:s0   build
 :

それでは,ポリシーを変更(適用)させます.

# chcon -R -h -t httpd_sys_content_t /usr/local/rbenv/..(略)../gems/passenger-3.0.15

再度,ポリシーを確認

# ls -Z /usr/local/rbenv/..(略)../gems/passenger-3.0.15
 :
drwxr-xr-x. root root unconfined_u:object_r:httpd_sys_content_t:s0 bin
drwxr-xr-x. root root unconfined_u:object_r:httpd_sys_content_t:s0 build
 :

さあこれでバッチリ!ということで,デーモンを再起動(今日これで何回目?)

# /etc/init.d/httpd restart

やっぱり,上手くいかず...

------------------------
Forbidden

You don't have permission to access /sample/ on this server.
------------------------

いい加減嫌になりますが...それは我慢.
ここで,もう一回,ログからポリシーを作成して適用する方法を試してみます.

# setenforce 0
# rm /var/log/audit/audit.log 
# /etc/init.d/auditd restart
# /etc/init.d/httpd restart

Railsアプリを動作

# grep httpd /var/log/audit/audit.log | audit2allow -M passenger
# semodule -i passenger.pp
# setenforce 1
# /etc/init.d/httpd restart

おおっ!上手くいきました!

SELinuxめ,なかなか手強いです.
でも絶対無効にはしない! Enforceであり続けます!