Difference between revisions of "Ruby on Rails"

From ArchWiki
Jump to: navigation, search
m (Option C: The Perfect Rails Setup)
(Updated latest ruby version number.)
(39 intermediate revisions by 19 users not shown)
Line 1: Line 1:
[[Category:Web Server (English)]]
+
[[Category:Web Server]]
{{i18n|RubyOnRails}}
+
[[zh-CN:Ruby on Rails]]
 
+
 
[http://rubyonrails.org/ Ruby on Rails], often shortened to Rails or RoR, is an open source web application framework for the Ruby programming language. It is intended to be used with an Agile development methodology that is used by web developers for rapid development.
 
[http://rubyonrails.org/ Ruby on Rails], often shortened to Rails or RoR, is an open source web application framework for the Ruby programming language. It is intended to be used with an Agile development methodology that is used by web developers for rapid development.
  
Line 24: Line 23:
 
== Option B: Installing via the AUR ==
 
== Option B: Installing via the AUR ==
  
{{Warning|This is not recommended, as this might not include the latest Rails version, and additional dependencies may be introduced that may require you to run {{Codeline|gem install}} anyway.}}
+
{{Warning|This is not recommended, as this might not include the latest Rails version, and additional dependencies may be introduced that may require you to run {{Ic|gem install}} anyway.}}
There is a {{Package AUR|rails}} package available in the [[AUR]]. Note that this is not in an [[Official Repositories|official repository]], so you will need to [[AUR#Build_the_package|build it manually]].
+
There is a {{AUR|rails}} package available in the [[AUR]]. Note that this is not in an [[Official Repositories|official repository]], so you will need to [[AUR#Build_the_package|build it manually]].
  
 
== Configuration ==
 
== Configuration ==
Line 33: Line 32:
 
  $ rails new testapp_name
 
  $ rails new testapp_name
  
This makes a new folder in your current working directory. Next start the web server. It listens on port 3000 by default:
+
{{Note|If you get an Error message like this:
 +
{{ic|... FetchError: SSL_connect returned=1 errno= 0 state=SSLv2/v3 read server hello A: sslv3 alert handshake
 +
failure (https://s3.amazonaws.com/ production.s3.rubygems.org/gems/rake-10.0.3.gem) }}
 +
try
 +
{{ic|$ pacman -Suy}}
 +
{{ic|$ pacman -S nodejs}}
 +
then try again.}}
 +
 
 +
This creates a new folder inside your current working directory.  
  
 
  $ cd testapp_name
 
  $ cd testapp_name
 +
 +
Next start the web server. It listens on port 3000 by default:
 +
 
  $ rails server
 
  $ rails server
  
Finally open your server address on port 3000 in your web browser. For example, if you are working on your local machine, visit http://localhost:3000.
+
Now visit the testapp_name website on your local machine by opening http://localhost:3000 in your browser
 +
{{Note| If ruby complains about not being able to find a JavaScript runtime, do {{ic|# pacman -S nodejs}}.}}
  
 
A test-page should shown greeting you "Welcome aboard".
 
A test-page should shown greeting you "Welcome aboard".
  
== Web servers ==
+
== Application servers ==
  
While the default Ruby On Rails HTTP server (WeBrick) is convenient for basic development it is not recommended for production use. Here are some suitable alternatives:
+
While the default Ruby On Rails HTTP server (WeBrick) is convenient for basic development it is not recommended for production use. Generally, you should choose between installing the Phusion Passenger module for your webserver ([[Apache]] or [[Nginx]]), or use a dedicated application-server (such as Mongrel or Unicorn) combined with a separate web-server acting as a reverse proxy.
  
 
=== Mongrel ===
 
=== Mongrel ===
 
 
Mongrel is a drop-in replacement for WeBrick, that can be run in precisely the same way, but offers better performance.
 
Mongrel is a drop-in replacement for WeBrick, that can be run in precisely the same way, but offers better performance.
  
Line 58: Line 68:
 
Alternatively, you can just run "ruby script/server" again, as it replaces WeBrick by default.
 
Alternatively, you can just run "ruby script/server" again, as it replaces WeBrick by default.
  
Generally, Mongrel is used in a production environment by running multiple instances of mongrel_rails, which are load-balanced behind an [[Nginx]] or [[Apache]] reverse proxy. However, you might find Phusion Passenger (see below) a much simpler solution for running a production environment.
+
=== Unicorn ===
 +
[http://unicorn.bogomips.org/ Unicorn] is loosely based on Mongrel, and is used by Github. It uses a different architecture that tries harder to find the best child for handling a request. [https://github.com/blog/517-unicorn Explanation of differences between Unicorn and Mongrel].
 +
 
 +
Install the Unicorn gem:
 +
# gem install unicorn
 +
 
 +
Then create a configuration file for your application in /etc/unicorn/. For example; here is a configuration example (Based on [http://www.warden.pl/2011/01/07/running-redmine-under-unicorn-in-debian/]) for Redmine:
 +
 
 +
<pre>working_directory "/srv/http/redmine"
 +
pid "/tmp/redmine.pid"
 +
 
 +
preload_app true
 +
timeout 60
 +
worker_processes 4
 +
listen 4000
 +
stderr_path('/var/log/unicorn.log')
 +
 
 +
GC.respond_to?(:copy_on_write_friendly=) and GC.copy_on_write_friendly = true
 +
 
 +
after_fork do |server, worker|
 +
#start the worker on port 4000, 4001, 4002 etc...
 +
addr = "0.0.0.0:#{4000 + worker.nr}"
 +
# infinite tries to start the worker
 +
server.listen(addr, :tries => -1, :delay => -1, :backlog => 128)
 +
 
 +
#Drop privileges if running as root
 +
worker.user('nobody', 'nobody') if Process.euid == 0
 +
end</pre>
 +
 
 +
Start it using:
 +
# usr/bin/unicorn -D -E production -c /etc/unicorn/redmine.ru
  
 
=== Apache/Nginx (using Phusion Passenger) ===
 
=== Apache/Nginx (using Phusion Passenger) ===
  
[http://www.modrails.com/ Passenger] also known as {{Filename|mod_rails}} is a module available for [[Nginx]] and [[Apache]], that greatly simplifies setting up a Rails server environment.
+
[http://www.modrails.com/ Passenger] also known as {{ic|mod_rails}} is a module available for [[Nginx]] and [[Apache]], that greatly simplifies setting up a Rails server environment. Nginx does not support modules as Apache and has to be compiled with {{ic|mod_rails}} in order to support Passenger; let Passenger compile it for you. As for Apache, let Passenger set up the module for you.
 +
 
 +
{{note|The current Nginx package in the official repositories actually is compiled with the Passenger module, so you can install it via pacman. The configuration files are stored in {{ic|/etc/nginx/conf/}}.}}
  
Start by installing the Passenger gem:
+
Start by installing the 'passenger' gem:
 
  # gem install passenger
 
  # gem install passenger
  
If you are aiming to use [[Apache]] with Passenger, run:
+
If you are aiming to use [[Apache]], run:
 
  # passenger-install-apache2-module
 
  # passenger-install-apache2-module
  
Line 75: Line 117:
 
The installer will provide you with any additional information regarding the installation (such as installing additional libraries).
 
The installer will provide you with any additional information regarding the installation (such as installing additional libraries).
  
{{Note|See [[NginX#Ruby_Integration_(Ruby_on_Rails_and_Rack-based)]] for more information on configuring a Rails Nginx-Passenger web stack.}}
+
To serve an application with Nginx, configure it as follows:
 +
<pre>
 +
server {
 +
    server_name app.example.org;
 +
    root path_to_app/public; # Be sure to point to 'public' folder!
 +
    passenger_enabled on;
 +
    rails_env development; # Rails environment.
 +
}
 +
</pre>
  
 
== Databases ==
 
== Databases ==
Line 83: Line 133:
 
=== SQLite ===
 
=== SQLite ===
  
SQLite is the default lightweight database for Ruby on Rails. To enable SQLite, simply install {{Package Official|sqlite3}}.
+
SQLite is the default lightweight database for Ruby on Rails. To enable SQLite, simply install {{Pkg|sqlite3}}.
  
 
=== PostgreSQL ===
 
=== PostgreSQL ===
Line 89: Line 139:
 
(Stub.)
 
(Stub.)
  
Install {{Package Official|postgresql}}.
+
Install {{Pkg|postgresql}}.
  
 
=== MySQL ===
 
=== MySQL ===
  
{{Note|You must first install [[MySQL]] with the appropriate headers in {{Filename|/usr/include}} (just installing {{Package Official|mysql}} is fine) before attempting to install the Ruby MySQL extensions.}}
+
{{Note|You must first install [[MySQL]] with the appropriate headers in {{ic|/usr/include}} (just installing {{Pkg|mysql}} is fine) before attempting to install the Ruby MySQL extensions.}}
  
 
Please refer to [[MySQL]] on how to install MySQL Server.
 
Please refer to [[MySQL]] on how to install MySQL Server.
Line 100: Line 150:
 
  # sudo gem install mysql
 
  # sudo gem install mysql
  
You can generate a rails application configured for MySQL by using the {{Codeline|-d}} parameter:
+
You can generate a rails application configured for MySQL by using the {{Ic|-d}} parameter:
 
  $ rails new testapp_name -d mysql
 
  $ rails new testapp_name -d mysql
  
Line 120: Line 170:
 
''Phusion Passenger running multiple Ruby versions.''
 
''Phusion Passenger running multiple Ruby versions.''
  
* [http://www.archlinux.org/ Archlinux]: A simple, lightweight distribution. ;)
+
* [https://www.archlinux.org/ Archlinux]: A simple, lightweight distribution. ;)
 
* [http://www.nginx.org/ Nginx]: A fast and lightweight '''web server''' with a strong focus on high concurrency, performance and low memory usage.
 
* [http://www.nginx.org/ Nginx]: A fast and lightweight '''web server''' with a strong focus on high concurrency, performance and low memory usage.
 
* [http://www.modrails.com/ Passenger] (a.k.a. mod_rails or mod_rack): Supports both Apache and Nginx web servers. It makes deployment of Ruby web applications, such as those built on Ruby on Rails web framework, a breeze.
 
* [http://www.modrails.com/ Passenger] (a.k.a. mod_rails or mod_rack): Supports both Apache and Nginx web servers. It makes deployment of Ruby web applications, such as those built on Ruby on Rails web framework, a breeze.
Line 130: Line 180:
  
 
Easy as:
 
Easy as:
  $ sudo pacman -S sqlite3
+
  $ sudo pacman -S sqlite
  
 
{{note|Of course SQLite is not critical in this setup, you can use MySQL and PostgreSQL as well.}}
 
{{note|Of course SQLite is not critical in this setup, you can use MySQL and PostgreSQL as well.}}
Line 143: Line 193:
 
. http and nobody are the users related to Nginx and Passenger, respectively.
 
. http and nobody are the users related to Nginx and Passenger, respectively.
  
{{note|Maybe adding the 'nodody' user to the 'rvm' group is not necessary.}}  
+
{{note|Maybe adding the 'nobody' user to the 'rvm' group is not necessary.}}
  
 
=== Step 2: Rubies ===
 
=== Step 2: Rubies ===
  
 
Once you have a working RVM installation in your hands, it is time to install the Ruby Enterprise Edition interpreter
 
Once you have a working RVM installation in your hands, it is time to install the Ruby Enterprise Edition interpreter
 +
{{Note|During the installation of Ruby Enterprise Edition patches will be applied. Consider installing '''base-devel''' beforehand.}}
 +
 
  $ rvm install ree
 
  $ rvm install ree
 
. Also take the chance to include other interpreters you want to use, like the last Ruby version
 
. Also take the chance to include other interpreters you want to use, like the last Ruby version
  $ rvm install 1.9.3
+
  $ rvm install 2.0.0
  
 
==== Advice ====
 
==== Advice ====
 +
 +
There is a documented [https://bugs.ruby-lang.org/issues/6383#note-1 bug] with older versions of Ruby (ie. the 1.8.7 version that REE uses) and the GCC versions 4.6 and up. If you get segmentation fault errors when trying to install gems such as Passenger, remove your install of REE and reinstall with the following:
 +
 +
$ rvm remove ree
 +
$ CFLAGS="-O2 -fno-tree-dce -fno-optimize-sibling-calls" rvm install ree
 +
 +
It is also possible to make it work with older versions of GCC, but that requires considerably more time.
  
 
I have found useful to delete the 'global' gemsets of the environments that have web applications. Their gems were somehow interfering with Passenger. Do not do
 
I have found useful to delete the 'global' gemsets of the environments that have web applications. Their gems were somehow interfering with Passenger. Do not do
 
  $ rvm ree do gemset delete global
 
  $ rvm ree do gemset delete global
  $ rvm 1.9.3 do gemset delete global
+
  $ rvm 2.0.0 do gemset delete global
 
now, but consider this later if you encounter complications.
 
now, but consider this later if you encounter complications.
  
Line 165: Line 224:
 
  $ gem install passenger
 
  $ gem install passenger
 
. The gem will be put into the 'default' gemset. Now execute the following script:
 
. The gem will be put into the 'default' gemset. Now execute the following script:
 +
 +
{{note|The current nginx package in the official repos actually was compiled with the passenger module. So you can install it via pacman and skip this step. The config files are stored in /etc/nginx/conf/. }}
 +
 
  $ rvmsudo passenger-install-nginx-module
 
  $ rvmsudo passenger-install-nginx-module
 
. It will download the sources of Nginx, compile and install it for you. It will guide you through all the process. (The default location for Nginx is /opt/nginx.)
 
. It will download the sources of Nginx, compile and install it for you. It will guide you through all the process. (The default location for Nginx is /opt/nginx.)
  
After completion, the script will require you to add two lines into the 'http block' at /opt/nginx/conf/nginx.conf that look like:
+
{{note|If you encounter a compilation error regarding Boost threads, see [https://bbs.archlinux.org/viewtopic.php?id&#61;139164 this] article.}}
 +
 
 +
After completion, the script will require you to add two lines into the 'http block' at {{ic|/opt/nginx/conf/nginx.conf}} that look like:
 
  http {  
 
  http {  
 
   ...
 
   ...
Line 175: Line 239:
 
   ...
 
   ...
 
  }
 
  }
 +
 +
If you installed [[Nginx]] from pacman the {{ic|passenger_root}} needs to be changed to:
 +
passenger_root /usr/lib/passenger/;
 +
{{warning|Do not set it to {{ic|/usr/lib/passenger/bin/passenger}} since this will result in [[Nginx]] segfaulting when checking the config}}
  
 
For everything that is not Ruby, use [[Nginx]] as usual to serve static pages, PHP and Python. Check the wiki page for more information.
 
For everything that is not Ruby, use [[Nginx]] as usual to serve static pages, PHP and Python. Check the wiki page for more information.
  
To enable the Nnginx service by default at start-up just add {{Codeline|nginx}} to the {{Codeline|DAEMONS}} array in {{Filename|/etc/rc.conf}}:
+
To enable the Nnginx service by default at start-up just add {{Ic|nginx}} to the {{Ic|DAEMONS}} array in {{ic|/etc/rc.conf}}:
 
  DAEMONS=(ntpd syslog-ng ... nginx)
 
  DAEMONS=(ntpd syslog-ng ... nginx)
  
{{note|It is possible that your Nginx installation has not come with an init script; check your /etc/rc.d/ directory for a file called ''nginx'', if that is your case manually create it. Help yourself with [[Nginx/Init_script]].}}
+
If you are using {{Ic|systemd}} instead of {{Ic|initscripts}}, you must run the following command to have your system run {{Ic|nginx}} on startup
 +
 
 +
# systemctl enable nginx.service
 +
 
 +
{{note|It is possible that your Nginx installation has not come with an init script; check your /etc/rc.d/ directory for a file called ''nginx'', if that is your case manually create it. Help yourself with [[Nginx/Init_script]]. If you installed nginx to another location, such as /opt/nginx, you will need to edit the init script accordingly.}}
  
 
=== Step 4: Gemsets and Apps ===
 
=== Step 4: Gemsets and Apps ===
Line 194: Line 266:
 
  $ rvmsudo refinerycms refineria
 
  $ rvmsudo refinerycms refineria
 
Again:
 
Again:
  $ rvm use 1.9.3@browser --create
+
  $ rvm use 2.0.0@browser --create
 
  $ gem install passenger
 
  $ gem install passenger
 
  $ gem install browsercms sqlite3
 
  $ gem install browsercms sqlite3
Line 208: Line 280:
 
* ''ree'' => for Nginx,
 
* ''ree'' => for Nginx,
 
* ''ree@refinery'' => Standalone, and
 
* ''ree@refinery'' => Standalone, and
* ''1.9.3@browser'' => Standalone.
+
* ''2.0.0@browser'' => Standalone.
  
The strategy is to combine Passenger for Nginx with Passenger Standalone. One must first identify the Ruby environment (interpreter plus gemset) that one uses the most; in this setup the REE interpreter and the default gemset were selected. One then proceeds with setting up Passenger for Nginx to use that environment. All applications that are to use a different Ruby version and/or gemset can be served separately through Passenger Standalone and hook into the main web server via a reverse proxy configuration.
+
The strategy is to combine Passenger for Nginx with Passenger Standalone. One must first identify the Ruby environment (interpreter plus gemset) that one uses the most; in this setup the REE interpreter and the default gemset were selected. One then proceeds with setting up Passenger for Nginx to use that environment (step 3).
 +
* Applications within the chosen environment can be served as in [[Ruby_on_Rails#Apache.2FNginx_.28using_Phusion_Passenger.29|Apache/Nginx (using Phusion Passenger)]], page up in this article.
 +
* All applications that are to use a different Ruby version and/or gemset can be served separately through Passenger Standalone and hook into the main web server via a reverse proxy configuration (step 6).
  
 
=== Step 5: .rvmrc files and ownerships ===
 
=== Step 5: .rvmrc files and ownerships ===
Line 218: Line 292:
 
so the specified environment is set at the entrance of applications' root folder.
 
so the specified environment is set at the entrance of applications' root folder.
  
Create /srv/http/refineria/.rvmrc and put
+
Create /srv/http/refineria/.rvmrc doing
  rvm ree@refineria
+
  sudo sh -c 'echo "rvm ree@refinery" > /srv/http/refineria/.rvmrc'
, while in /srv/http/navegador/.rvmrc,
+
, and /srv/http/navegador/.rvmrc with
  rvm 1.9.3@navegador  
+
  sudo sh -c 'echo "rvm 2.0.0@browser" > /srv/http/navegador/.rvmrc'
. You have to enter to both application root folders now, because every first time that RVM finds a .rvmrc it asks you if you trust the given file, consequently you must validate the two files you have just created.
+
You have to enter to both application root folders now, because every first time that RVM finds a .rvmrc it asks you if you trust the given file, consequently you must validate the two files you have just created.
  
 
These files aid the programs involved to find the correct gems.
 
These files aid the programs involved to find the correct gems.
Line 233: Line 307:
 
You have to start the Passenger Standalone web servers for your applications. So, do
 
You have to start the Passenger Standalone web servers for your applications. So, do
 
  $ cd /srv/http/refineria
 
  $ cd /srv/http/refineria
  $ rvmsudo passenger start --socket refineria.socket -d
+
  $ rvmsudo passenger start --socket tmp/sockets/passenger.socket -d
 
and
 
and
 
  $ cd /srv/http/navegador
 
  $ cd /srv/http/navegador
  $ rvmsudo passenger start --socket navegador.socket -d
+
  $ rvmsudo passenger start --socket tmp/sockets/passenger.socket -d
 
. The first time that you run a Passenger Standalone it will perform a minor installation.
 
. The first time that you run a Passenger Standalone it will perform a minor installation.
  
 
Note that you are using ''unix domain'' sockets instead of the commonly-used ''TCP'' sockets; it turns out that unix domain are significantly faster than TCP sockets.
 
Note that you are using ''unix domain'' sockets instead of the commonly-used ''TCP'' sockets; it turns out that unix domain are significantly faster than TCP sockets.
 +
 +
{{note|If you are experimenting trouble with unix sockets, changing to TCP should work:
 +
rvmsudo passenger start -a 127.0.0.1 -p 3000 -d
 +
}}
  
 
==== Launch Passenger Standalone daemons at system start-up ====
 
==== Launch Passenger Standalone daemons at system start-up ====
  
''Coming soon!''
+
''Do you have a script? Please post it here.''
 +
 
 +
The systemd script below was made for a Typo blog I host at /srv/http/typo. It's located at /etc/systemd/system/passenger_typo.service. I set the Environment= tags (see "man systemd.exec") from the output of "rvm env". The only exception was PATH=, which I had to combine from my regular PATH and the output of rvm env.
 +
 
 +
Note: If you don't set the "WorkingDirectory=" variable to your application folder, passenger will fail to find your app and will subsequently shut itself down.
 +
 
 +
<pre>
 +
[Unit]
 +
Description=Passenger Standalone Script for Typo
 +
After=network.target
 +
 
 +
[Service]
 +
Type=forking
 +
WorkingDirectory=/srv/http/typo
 +
PIDFile=/srv/http/typo/tmp/pids/passenger.pid
 +
 
 +
Environment=PATH=/usr/local/rvm/gems/ruby-2.0.0-p0@typo/bin:/usr/local/rvm/gems/ruby-2.0.0-p0@global/bin:/usr/local/rvm/rubies/ruby-2.0.0-p0/bin:/usr/local/rvm/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin:/usr/bin/core_perl
 +
Environment=rvm_env_string=ruby-2.0.0-p0@typo
 +
Environment=rvm_path=/usr/local/rvm
 +
Environment=rvm_ruby_string=ruby-2.0.0-p0
 +
Environment=rvm_gemset_name=typo
 +
Environment=RUBY_VERSION=ruby-2.0.0-p0
 +
Environment=GEM_HOME=/usr/local/rvm/gems/ruby-2.0.0-p0@typo
 +
Environment=GEM_PATH=/usr/local/rvm/gems/ruby-2.0.0-p0@typo:/usr/local/rvm/gems/ruby-2.0.0-p0@global
 +
Environment=MY_RUBY_HOME=/usr/local/rvm/rubies/ruby-2.0.0-p0
 +
Environment=IRBRC=/usr/local/rvm/rubies/ruby-2.0.0-p0/.irbrc
 +
 
 +
ExecStart=/bin/bash -c "rvmsudo passenger start --socket /srv/http/typo/tmp/sockets/passenger.socket -d"
 +
 
 +
[Install]
 +
WantedBy=multi-user.target
 +
</pre>
  
 
=== Step 7: Deployment ===
 
=== Step 7: Deployment ===
 +
 +
== With subdomains ==
  
 
Once again edit /opt/nginx/conf/nginx.conf to include some vital instructions:
 
Once again edit /opt/nginx/conf/nginx.conf to include some vital instructions:
Line 251: Line 362:
 
<pre>
 
<pre>
 
## RefineryCMS ##
 
## RefineryCMS ##
 
upstream refineria_upstream {
 
    server unix:/srv/http/refineria/refineria.socket;
 
}
 
  
 
server {
 
server {
Line 260: Line 367:
 
     root /srv/http/refineria/public;
 
     root /srv/http/refineria/public;
 
     location / {
 
     location / {
         proxy_pass http://refineria_upstream;
+
         proxy_pass http://unix:/srv/http/refineria/tmp/sockets/passenger.socket;
 
         proxy_set_header Host $host;
 
         proxy_set_header Host $host;
 
     }
 
     }
Line 266: Line 373:
  
 
## BrowserCMS ##
 
## BrowserCMS ##
 
upstream navegador_upstream {
 
    server unix:/srv/http/navegador/navegador.socket;
 
}
 
  
 
server {
 
server {
Line 275: Line 378:
 
     root /srv/http/navegador/public;
 
     root /srv/http/navegador/public;
 
     location / {
 
     location / {
         proxy_pass http://navegador_upstream;
+
         proxy_pass http://unix:/srv/http/navegador/tmp/sockets/passenger.socket;
 +
        proxy_set_header Host $host;
 +
    }
 +
}
 +
</pre>
 +
 
 +
{{note|Or if using TCP sockets, configure the ''proxy_pass'' directive like
 +
<pre>proxy_pass http://127.0.0.1:3000;</pre>
 +
}}
 +
 
 +
== Without subdomains ==
 +
 
 +
If you for some reason don't want to host each application on it's own subdomain but rather in a url like: {{ic|site.com/railsapp}} then you could do something like this in your config:
 +
 
 +
<pre>
 +
server {
 +
    server_name site.com;
 +
    #Base for the html files etc
 +
    root /srv/http/;
 +
 
 +
    #First application you want hosted under domain site.com/railsapp
 +
    location /railsapp {
 +
        root /srv/http/railsapp/public;
 +
        #you may need to change passenger_base_uri to be the uri you want to point at, ie:
 +
        #passenger_base_uri /railsapp;
 +
        #but probably only if you're using the other solution with passenger phusion
 +
        proxy_pass http://unix:/srv/http/railsapp/tmp/sockets/passenger.socket;
 +
        proxy_set_header Host $host;
 +
    }
 +
 
 +
    #Second applicatino you want hosted under domain site.com/anotherapp
 +
    location /anotherapp {
 +
        root /srv/http/anotherapp/public;
 +
        #same thing about the passenger_base_uri here, but with value /anotherapp instead
 +
        proxy_pass http://unix:/srv/http/anotherapp/tmp/sockets/passenger.socket;
 
         proxy_set_header Host $host;
 
         proxy_set_header Host $host;
 
     }
 
     }

Revision as of 08:07, 28 March 2013

Ruby on Rails, often shortened to Rails or RoR, is an open source web application framework for the Ruby programming language. It is intended to be used with an Agile development methodology that is used by web developers for rapid development.

This document describes how to set up the Ruby on Rails Framework on an Arch Linux system.

Ruby on Rails requires Ruby to be installed, so read that article first for installation instructions.

Option A: Installation via RubyGems (Recommended)

Template:Box Note

# gem install rails

Building the documentation takes a while. If you want to skip it, append the parameters --no-ri --no-rdoc to the install command.

# gem install rails --no-ri --no-rdoc

Updating Gems

gem is a package manager for Ruby modules, somewhat like pacman is to Arch Linux. To update your gems, simply run:

# gem update

Option B: Installing via the AUR

Warning: This is not recommended, as this might not include the latest Rails version, and additional dependencies may be introduced that may require you to run gem install anyway.

There is a railsAUR package available in the AUR. Note that this is not in an official repository, so you will need to build it manually.

Configuration

Rails is bundled with a basic HTTP server called WeBrick. You can create a test application to test it. First, create an application with the rails command:

$ rails new testapp_name
Note: If you get an Error message like this:
... FetchError: SSL_connect returned=1 errno= 0 state=SSLv2/v3 read server hello A: sslv3 alert handshake
failure (https://s3.amazonaws.com/ production.s3.rubygems.org/gems/rake-10.0.3.gem) 

try

$ pacman -Suy
$ pacman -S nodejs
then try again.

This creates a new folder inside your current working directory.

$ cd testapp_name

Next start the web server. It listens on port 3000 by default:

$ rails server

Now visit the testapp_name website on your local machine by opening http://localhost:3000 in your browser

Note: If ruby complains about not being able to find a JavaScript runtime, do # pacman -S nodejs.

A test-page should shown greeting you "Welcome aboard".

Application servers

While the default Ruby On Rails HTTP server (WeBrick) is convenient for basic development it is not recommended for production use. Generally, you should choose between installing the Phusion Passenger module for your webserver (Apache or Nginx), or use a dedicated application-server (such as Mongrel or Unicorn) combined with a separate web-server acting as a reverse proxy.

Mongrel

Mongrel is a drop-in replacement for WeBrick, that can be run in precisely the same way, but offers better performance.

First install the Mongrel gem:

# gem install mongrel

Then start it using:

# mongrel_rails start

Alternatively, you can just run "ruby script/server" again, as it replaces WeBrick by default.

Unicorn

Unicorn is loosely based on Mongrel, and is used by Github. It uses a different architecture that tries harder to find the best child for handling a request. Explanation of differences between Unicorn and Mongrel.

Install the Unicorn gem:

# gem install unicorn

Then create a configuration file for your application in /etc/unicorn/. For example; here is a configuration example (Based on [1]) for Redmine:

working_directory "/srv/http/redmine"
pid "/tmp/redmine.pid"

preload_app true
timeout 60
worker_processes 4
listen 4000
stderr_path('/var/log/unicorn.log')

GC.respond_to?(:copy_on_write_friendly=) and GC.copy_on_write_friendly = true

after_fork do |server, worker|
	#start the worker on port 4000, 4001, 4002 etc...
	addr = "0.0.0.0:#{4000 + worker.nr}"
	# infinite tries to start the worker
	server.listen(addr, :tries => -1, :delay => -1, :backlog => 128)

	#Drop privileges if running as root
	worker.user('nobody', 'nobody') if Process.euid == 0
end

Start it using:

# usr/bin/unicorn -D -E production -c /etc/unicorn/redmine.ru

Apache/Nginx (using Phusion Passenger)

Passenger also known as mod_rails is a module available for Nginx and Apache, that greatly simplifies setting up a Rails server environment. Nginx does not support modules as Apache and has to be compiled with mod_rails in order to support Passenger; let Passenger compile it for you. As for Apache, let Passenger set up the module for you.

Note: The current Nginx package in the official repositories actually is compiled with the Passenger module, so you can install it via pacman. The configuration files are stored in /etc/nginx/conf/.

Start by installing the 'passenger' gem:

# gem install passenger

If you are aiming to use Apache, run:

# passenger-install-apache2-module

For Nginx:

# passenger-install-nginx-module

The installer will provide you with any additional information regarding the installation (such as installing additional libraries).

To serve an application with Nginx, configure it as follows:

server {
    server_name app.example.org;
    root path_to_app/public; # Be sure to point to 'public' folder!
    passenger_enabled on;
    rails_env development; # Rails environment.
}

Databases

Most web applications will need to interact with some sort of database. ActiveRecord (the ORM used by Rails to provide database abstraction) supports several database vendors, the most popular of which are MySQL, SQLite, and PostgreSQL.

SQLite

SQLite is the default lightweight database for Ruby on Rails. To enable SQLite, simply install sqlite3.

PostgreSQL

(Stub.)

Install postgresql.

MySQL

Note: You must first install MySQL with the appropriate headers in /usr/include (just installing mysql is fine) before attempting to install the Ruby MySQL extensions.

Please refer to MySQL on how to install MySQL Server.

A gem with some native extensions is required, probably best installed as root:

# sudo gem install mysql

You can generate a rails application configured for MySQL by using the -d parameter:

$ rails new testapp_name -d mysql

You then need to edit config/database.yml. Rails uses different databases for development, testing, production and other environments. Here is an example development configuration for MySQL running on localhost:

 development:
   adapter: mysql
   database: my_application_database
   username: development
   password: my_secret_password

Note that you do not have to actually create the database using MySQL, as this can be done via Rails with:

# rake db:create

If no errors are shown, then your database has been created and Rails can talk to your MySQL database.

Option C: The Perfect Rails Setup

Phusion Passenger running multiple Ruby versions.

  • Archlinux: A simple, lightweight distribution. ;)
  • Nginx: A fast and lightweight web server with a strong focus on high concurrency, performance and low memory usage.
  • Passenger (a.k.a. mod_rails or mod_rack): Supports both Apache and Nginx web servers. It makes deployment of Ruby web applications, such as those built on Ruby on Rails web framework, a breeze.
  • Ruby Enterprise Edition (REE): Passenger allows Ruby on Rails applications to use about 33% less memory, when used in combination with REE.
  • Ruby Version Manager (RVM): A command-line tool which allows you to easily install, manage, and work with multiple Ruby environments from interpreters to sets of gems. RVM lets you deploy each project with its own completely self-contained and dedicated environment —from the specific version of ruby, all the way down to the precise set of required gems to run your application—.
  • SQLite: The default lightweight database for Ruby on Rails.

Step 0: SQLite

Easy as:

$ sudo pacman -S sqlite
Note: Of course SQLite is not critical in this setup, you can use MySQL and PostgreSQL as well.

Step 1: RVM

Make a multi-user RVM installation as specified here.

In the 'adding users to the rvm group' step, do

$ sudo usermod -a -G rvm http
$ sudo usermod -a -G rvm nobody

. http and nobody are the users related to Nginx and Passenger, respectively.

Note: Maybe adding the 'nobody' user to the 'rvm' group is not necessary.

Step 2: Rubies

Once you have a working RVM installation in your hands, it is time to install the Ruby Enterprise Edition interpreter

Note: During the installation of Ruby Enterprise Edition patches will be applied. Consider installing base-devel beforehand.
$ rvm install ree

. Also take the chance to include other interpreters you want to use, like the last Ruby version

$ rvm install 2.0.0

Advice

There is a documented bug with older versions of Ruby (ie. the 1.8.7 version that REE uses) and the GCC versions 4.6 and up. If you get segmentation fault errors when trying to install gems such as Passenger, remove your install of REE and reinstall with the following:

$ rvm remove ree
$ CFLAGS="-O2 -fno-tree-dce -fno-optimize-sibling-calls" rvm install ree

It is also possible to make it work with older versions of GCC, but that requires considerably more time.

I have found useful to delete the 'global' gemsets of the environments that have web applications. Their gems were somehow interfering with Passenger. Do not do

$ rvm ree do gemset delete global
$ rvm 2.0.0 do gemset delete global

now, but consider this later if you encounter complications.

Step 3: Nginx with Passenger support

Do not install Nginx via pacman. This web server does not support modules as Apache, so it must be compiled from source with the functionality of mod_rails (Passenger). Fortunately this is straightforward thanks to the passenger gem. Get it:

$ rvm use ree
$ gem install passenger

. The gem will be put into the 'default' gemset. Now execute the following script:

Note: The current nginx package in the official repos actually was compiled with the passenger module. So you can install it via pacman and skip this step. The config files are stored in /etc/nginx/conf/.
$ rvmsudo passenger-install-nginx-module

. It will download the sources of Nginx, compile and install it for you. It will guide you through all the process. (The default location for Nginx is /opt/nginx.)

Note: If you encounter a compilation error regarding Boost threads, see this article.

After completion, the script will require you to add two lines into the 'http block' at /opt/nginx/conf/nginx.conf that look like:

http { 
  ...
  passenger_root /usr/local/rvm/gems/ree-1.8.7-2011.03/gems/passenger-3.0.9;
  passenger_ruby /usr/local/rvm/wrappers/ree-1.8.7-2011.03/ruby;
  ...
}

If you installed Nginx from pacman the passenger_root needs to be changed to:

passenger_root /usr/lib/passenger/;
Warning: Do not set it to /usr/lib/passenger/bin/passenger since this will result in Nginx segfaulting when checking the config

For everything that is not Ruby, use Nginx as usual to serve static pages, PHP and Python. Check the wiki page for more information.

To enable the Nnginx service by default at start-up just add nginx to the DAEMONS array in /etc/rc.conf:

DAEMONS=(ntpd syslog-ng ... nginx)

If you are using systemd instead of initscripts, you must run the following command to have your system run nginx on startup

# systemctl enable nginx.service
Note: It is possible that your Nginx installation has not come with an init script; check your /etc/rc.d/ directory for a file called nginx, if that is your case manually create it. Help yourself with Nginx/Init_script. If you installed nginx to another location, such as /opt/nginx, you will need to edit the init script accordingly.

Step 4: Gemsets and Apps

For each Rails application you should have a gemset. Suppose that you want to try RefineryCMS against BrowserCMS, two open-source Content Management Systems based on Rails. Then you should do:

$ rvm use ree@refinery --create
$ gem install rails -v 3.0.11
$ gem install passenger
$ gem install refinerycms refinerycms-i18n sqlite3

Deploy a RefineryCMS instance called refineria:

$ cd /srv/http/
$ rvmsudo refinerycms refineria

Again:

$ rvm use 2.0.0@browser --create
$ gem install passenger
$ gem install browsercms sqlite3

Deploy a BrowserCMS instance called navegador:

$ cd /srv/http/
$ rvmsudo browsercms demo navegador
$ cd /srv/http/navegador
$ rvmsudo rake db:install

Passenger for Nginx and Passenger Standalone

Observe that the passenger gem was installed three times and with different intentions; in the environments

  • ree => for Nginx,
  • ree@refinery => Standalone, and
  • 2.0.0@browser => Standalone.

The strategy is to combine Passenger for Nginx with Passenger Standalone. One must first identify the Ruby environment (interpreter plus gemset) that one uses the most; in this setup the REE interpreter and the default gemset were selected. One then proceeds with setting up Passenger for Nginx to use that environment (step 3).

  • Applications within the chosen environment can be served as in Apache/Nginx (using Phusion Passenger), page up in this article.
  • All applications that are to use a different Ruby version and/or gemset can be served separately through Passenger Standalone and hook into the main web server via a reverse proxy configuration (step 6).

Step 5: .rvmrc files and ownerships

This step is crucial for the correct behaviour of the setup. RVM seeks for .rvmrc files when changing folders; if it finds one, it reads it. In these files normally one stores a line like

rvm <ruby_version>@<gemset_name>

so the specified environment is set at the entrance of applications' root folder.

Create /srv/http/refineria/.rvmrc doing

sudo sh -c 'echo "rvm ree@refinery" > /srv/http/refineria/.rvmrc'

, and /srv/http/navegador/.rvmrc with

sudo sh -c 'echo "rvm 2.0.0@browser" > /srv/http/navegador/.rvmrc'

You have to enter to both application root folders now, because every first time that RVM finds a .rvmrc it asks you if you trust the given file, consequently you must validate the two files you have just created.

These files aid the programs involved to find the correct gems.

Apart, if applications' files and folders are not owned by the right user you will face database write-access problems. The use of rvmsudo produces root-owned archives when generated by Rails; in the other hand, nobody is the user for Passenger —if you have not changed it—: who will use and should posses them. Fix this doing

$ sudo chown -R nobody.nobody /srv/http/refineria /srv/http/navegador

Step 6: Reverse proxies

You have to start the Passenger Standalone web servers for your applications. So, do

$ cd /srv/http/refineria
$ rvmsudo passenger start --socket tmp/sockets/passenger.socket -d

and

$ cd /srv/http/navegador
$ rvmsudo passenger start --socket tmp/sockets/passenger.socket -d

. The first time that you run a Passenger Standalone it will perform a minor installation.

Note that you are using unix domain sockets instead of the commonly-used TCP sockets; it turns out that unix domain are significantly faster than TCP sockets.

Note: If you are experimenting trouble with unix sockets, changing to TCP should work:
rvmsudo passenger start -a 127.0.0.1 -p 3000 -d

Launch Passenger Standalone daemons at system start-up

Do you have a script? Please post it here.

The systemd script below was made for a Typo blog I host at /srv/http/typo. It's located at /etc/systemd/system/passenger_typo.service. I set the Environment= tags (see "man systemd.exec") from the output of "rvm env". The only exception was PATH=, which I had to combine from my regular PATH and the output of rvm env.

Note: If you don't set the "WorkingDirectory=" variable to your application folder, passenger will fail to find your app and will subsequently shut itself down.

[Unit]
Description=Passenger Standalone Script for Typo
After=network.target

[Service]
Type=forking
WorkingDirectory=/srv/http/typo
PIDFile=/srv/http/typo/tmp/pids/passenger.pid

Environment=PATH=/usr/local/rvm/gems/ruby-2.0.0-p0@typo/bin:/usr/local/rvm/gems/ruby-2.0.0-p0@global/bin:/usr/local/rvm/rubies/ruby-2.0.0-p0/bin:/usr/local/rvm/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin:/usr/bin/core_perl
Environment=rvm_env_string=ruby-2.0.0-p0@typo
Environment=rvm_path=/usr/local/rvm
Environment=rvm_ruby_string=ruby-2.0.0-p0
Environment=rvm_gemset_name=typo
Environment=RUBY_VERSION=ruby-2.0.0-p0
Environment=GEM_HOME=/usr/local/rvm/gems/ruby-2.0.0-p0@typo
Environment=GEM_PATH=/usr/local/rvm/gems/ruby-2.0.0-p0@typo:/usr/local/rvm/gems/ruby-2.0.0-p0@global
Environment=MY_RUBY_HOME=/usr/local/rvm/rubies/ruby-2.0.0-p0
Environment=IRBRC=/usr/local/rvm/rubies/ruby-2.0.0-p0/.irbrc

ExecStart=/bin/bash -c "rvmsudo passenger start --socket /srv/http/typo/tmp/sockets/passenger.socket -d"

[Install]
WantedBy=multi-user.target

Step 7: Deployment

With subdomains

Once again edit /opt/nginx/conf/nginx.conf to include some vital instructions:

## RefineryCMS ##

server {
    server_name refinery.domain.com;
    root /srv/http/refineria/public;
    location / {
        proxy_pass http://unix:/srv/http/refineria/tmp/sockets/passenger.socket;
        proxy_set_header Host $host;
    }
}

## BrowserCMS ##

server {
    server_name browser.domain.com;
    root /srv/http/navegador/public;
    location / {
        proxy_pass http://unix:/srv/http/navegador/tmp/sockets/passenger.socket;
        proxy_set_header Host $host;
    }
}
Note: Or if using TCP sockets, configure the proxy_pass directive like
proxy_pass http://127.0.0.1:3000;

Without subdomains

If you for some reason don't want to host each application on it's own subdomain but rather in a url like: site.com/railsapp then you could do something like this in your config:

server {
    server_name site.com;
    #Base for the html files etc
    root /srv/http/;

    #First application you want hosted under domain site.com/railsapp
    location /railsapp {
        root /srv/http/railsapp/public;
        #you may need to change passenger_base_uri to be the uri you want to point at, ie:
        #passenger_base_uri /railsapp;
        #but probably only if you're using the other solution with passenger phusion
        proxy_pass http://unix:/srv/http/railsapp/tmp/sockets/passenger.socket;
        proxy_set_header Host $host;
    }

    #Second applicatino you want hosted under domain site.com/anotherapp
    location /anotherapp {
        root /srv/http/anotherapp/public;
        #same thing about the passenger_base_uri here, but with value /anotherapp instead
        proxy_pass http://unix:/srv/http/anotherapp/tmp/sockets/passenger.socket;
        proxy_set_header Host $host;
    }
}

At this point you are in conditions to run Nginx with:

$ sudo rc.d start nginx

and to access both CMSs through refinery.domain.com and browser.domain.com.

References

See also

References