BogoToBogo
  • Home
  • About
  • Big Data
  • Machine Learning
  • AngularJS
  • Python
  • C++
  • go
  • DevOps
  • Kubernetes
  • Algorithms
  • More...
    • Qt 5
    • Linux
    • FFmpeg
    • Matlab
    • Django 1.8
    • Ruby On Rails
    • HTML5 & CSS

Deploying a Rails 4 app on CentOS 7 production server with Apache and Passenger - part I

RubyOnRails_logo




Bookmark and Share





bogotobogo.com site search:





Note

We will demonstrate how to install Phusion Passenger as our Rails-friendly web server, which is easy to install, configure, and maintain. We will integrate it into Apache with Phusion Passenger(mod_rails and mod_rack) on CentOS 7.

By the end of this tutorial, we will have a Rails application (App : Facebook and Twitter Authentication using Omniauth oauth2) deployed on our production server.

PyGoogleSignUpPage.png

The reference I used was Deploying a Ruby app with Passenger to production.

In this tutorial, we do not use rvmsudo and just stayed with sudo.

Please visit the repo : Rails4-PyGoogle.

The site sample I used here was pygoogle.com, and it's been changed because it started to evolve into real product. But we can still find the sample for this tutorial in demo.pygoogle.com which hasn't been changed.





Pull code from Git

We need to create a directory for our app. In this case, it is pygoogle.com:

$ sudo mkdir -p /var/www/pygoogle.com
$ sudo chown app-user: /var/www/pygoogle.com

So, after pulling our code from Git, we have the following file structure under /var/www/pygoogle.com:

FileTree-1.png
FilesTree-2.png




Ruby and Rails Setup

We'll use Ruby Version Manager (RVM) to maintain multiple Ruby environments on our server.

To install rvm, let's use curl to download a script at https://get.rvm.io, and let bash execute it locally with an option of stable:

$ curl -L https://get.rvm.io | bash -s stable

To start using RVM, we need to run source ~/.rvm/scripts/rvm:

$ source ~/.rvm/scripts/rvm

Also, we put that into our shell:

echo "source ~/.rvm/scripts/rvm" >> ~/.bashrc

We may want to install RVM's requirements using the following command which will tinstall various development packages and dependencies:

$ rvm requirements
Checking requirements for centos.
Installing requirements for centos.
Installing required packages: patch, libyaml-devel, autoconf, gcc-c++, patch, readline-devel, libffi-devel, automake, libtool, bison, sqlite-devel...............
Requirements installation successful.

To check rvm version:

$ rvm -v
rvm 1.26.11 (latest) by Wayne E. Seguin ..

What we've done so far made RVM fully initialized and ready to use.





Ruby 2.3.0 install - (skip)

Install Ruby 2.3.0:

$ rvm install 2.3.0

Verify if Ruby was installed properly with this command:

$ ruby -v
ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-linux]

Set default Ruby:

$ rvm --default use ruby-2.3.0
Using ~/.rvm/gems/ruby-2.3.0

We want remove the rdoc and ri when installing new gems:

$ echo "gem: --no-ri --no-rdoc" > ~/.gemrc

To manage our application dependencies, we need to install the bundler gem:

$ gem install bundler
Fetching: bundler-1.11.2.gem (100%)
Successfully installed bundler-1.11.2
1 gem installed




Rails 4.2.5 install

Install Rails 4.2.5:

$ gem install rails -v 4.2.5
...
Successfully installed rails-4.2.5
30 gems installed

$ rails -v
Rails 4.2.5




Bundle install 1

Actually, our current Gemfile is using Ruby 2.1.3 not 2.3.0. So, we may want to install 2.1.3:

$ rvm install 2.1.3

Now we have two versions of Ruby, so we need to check the status of each version:

$ rvm list

rvm rubies

=> ruby-2.1.3 [ x86_64 ]
 * ruby-2.3.0 [ x86_64 ]

# => - current
# =* - current && default
#  * - default

It might be better set default Ruby for rvm to ruby-2.1.3:

$ rvm --default use 2.1.3

Then, check the status again:

$ rvm list

rvm rubies

=> ruby-2.1.3 [ x86_64 ]
 * ruby-2.3.0 [ x86_64 ]

# => - current
# =* - current && default
#  * - default

Install packages using bundle command:

$ rvm list

rvm rubies

=* ruby-2.1.3 [ x86_64 ]
   ruby-2.3.0 [ x86_64 ]

# => - current
# =* - current && default
#  * - default

Now, ruby-2.1.3 is current && default!

Install bundler:

$ gem install bundler

Install packages using bundle command:

$ bundle install

But I got an error during the packages install. It's related to building 'capybara-webkit 1.3.0'. Looks like capybara-webkit depends on a WebKit implementation from Qt, and 'qmake' is missing.

$ bundle install
...
Installing capybara-webkit 1.3.0 with native extensions

Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

    /home/sfvue/.rvm/rubies/ruby-2.1.3/bin/ruby -r ./siteconf20160120-24887-3jbgep.rb extconf.rb
Command 'qmake -spec linux-g++ ' not available

Makefile not found
...

That's because I did not set the environment, and the default was "development".

To switch our rails environment to production, we need to add the following to ~/.bashrc and source it:

$ echo "export RAILS_ENV=production" >> ~/.bashrc
$ source ~/.bashrc

Or put the following line into config/environment.rb:

Rails.env="production"

Let's install packages again using bundle command:

$ bundle install

This time it worked.

To check the current rails status, we can use rake about command:

$ rake about
(in /var/www/pygoogle.com/Rails4-PyGoogle)
About your application's environment
Ruby version             2.1.3-p242 (x86_64-linux)
RubyGems version         2.4.8
Rack version             1.5
Rails version            4.1.6
JavaScript Runtime       therubyracer (V8)
Active Record version    4.1.6
Action Pack version      4.1.6
Action View version      4.1.6
Action Mailer version    4.1.6
Active Support version   4.1.6
Middleware               Airbrake::UserInformer, Rack::Timeout, ActionDispatch::SSL, Rack::Sendfile, ActionDispatch::Static, #, Rack::Runtime, Rack::MethodOverride, ActionDispatch::RequestId, Rails::Rack::Logger, ActionDispatch::ShowExceptions, ActionDispatch::DebugExceptions, Airbrake::Rails::Middleware, ActionDispatch::RemoteIp, ActionDispatch::Callbacks, ActiveRecord::ConnectionAdapters::ConnectionManagement, ActiveRecord::QueryCache, ActionDispatch::Cookies, ActionDispatch::Session::CacheStore, ActionDispatch::Flash, ActionDispatch::ParamsParser, Remotipart::Middleware, Rack::Head, Rack::ConditionalGet, Rack::ETag, Warden::Manager, OmniAuth::Builder, Rack::Pjax
Application root         /var/www/pygoogle.com/Rails4-PyGoogle
Environment              production
Database adapter         postgresql




Phusion Passenger Setup

(Note) The reference used for this section is Installing Passenger + Apache.

Phusion Passenger (Passenger or referred to as mod_passenger) is an application server.

Phusion Passenger it is often used to power Ruby sites. Its code is distributed in form of a Ruby gem, which is then compiled on the target machine and installed into Apache as a module.

We will install Passenger + Apache module through Phusion's yum repository.

First, let's install EPEL and other other prerequisites:

$ sudo yum install -y epel-release pygpgme curl

Then, add it to our el7 YUM repository:

$ sudo curl --fail -sSLo /etc/yum.repos.d/passenger.repo https://oss-binaries.phusionpassenger.com/yum/definitions/el-passenger.repo

Finally, install Passenger + Apache module:

$ sudo yum install -y mod_passenger

Now that the Passenger Apache module is installed, restart Apache to ensure that Passenger is activated:

$ sudo systemctl restart httpd

After installation, please validate the install by running:

$ sudo passenger-config validate-install
validate-install.png

All checks should pass. If any of the checks do not pass, we should follow the suggestions on screen.

Finally, check whether Apache has started the Passenger core processes using sudo passenger-memory-stats. We should see Apache processes as well as Passenger processes:

Version: 5.0.23
Date   : 2016-01-24 11:21:28 -0800

--------- Apache processes ---------
PID   PPID  VMSize    Private  Name
------------------------------------
8629  1     410.3 MB  0.4 MB   /usr/sbin/httpd -DFOREGROUND
8682  8629  803.7 MB  35.2 MB  /usr/sbin/httpd -DFOREGROUND
8683  8629  803.7 MB  34.2 MB  /usr/sbin/httpd -DFOREGROUND
8684  8629  412.4 MB  2.5 MB   /usr/sbin/httpd -DFOREGROUND
8685  8629  412.4 MB  2.5 MB   /usr/sbin/httpd -DFOREGROUND
8686  8629  412.4 MB  2.5 MB   /usr/sbin/httpd -DFOREGROUND
8721  8629  412.4 MB  2.5 MB   /usr/sbin/httpd -DFOREGROUND
8722  8629  412.4 MB  2.5 MB   /usr/sbin/httpd -DFOREGROUND
8909  8629  412.4 MB  2.6 MB   /usr/sbin/httpd -DFOREGROUND
9545  8629  412.4 MB  2.5 MB   /usr/sbin/httpd -DFOREGROUND
9546  8629  412.4 MB  2.5 MB   /usr/sbin/httpd -DFOREGROUND
9547  8629  412.4 MB  2.5 MB   /usr/sbin/httpd -DFOREGROUND
9548  8629  412.4 MB  2.5 MB   /usr/sbin/httpd -DFOREGROUND
### Processes: 13
### Total private dirty RSS: 95.18 MB


-------- Nginx processes --------

### Processes: 0
### Total private dirty RSS: 0.00 MB


----- Passenger processes -----
PID   VMSize    Private   Name
-------------------------------
8658  418.8 MB  0.9 MB    Passenger watchdog
8661  628.9 MB  1.8 MB    Passenger core
8666  427.1 MB  1.0 MB    Passenger ust-router
9514  628.3 MB  134.5 MB  Passenger RubyApp: /var/www/pygoogle.com/Rails4-PyGoogle (production)
### Processes: 4
### Total private dirty RSS: 138.16 MB

If we do not see any Apache processes or Passenger processes, then we probably have some kind of installation problem or configuration problem.





Determine the Ruby command that Passenger should use

We need to tell Passenger which Ruby command it should use to run our app, since we have multiple Ruby interpreters on our system. We can run passenger-config about ruby-command to find out which Ruby interpreter we are using:

$ passenger-config about ruby-command
passenger-config was invoked through the following Ruby interpreter:
  Command: /home/sfvue/.rvm/gems/ruby-2.1.3/wrappers/ruby
  Version: ruby 2.1.3p242 (2014-09-19 revision 47630) [x86_64-linux]
  To use in Apache: PassengerRuby /home/sfvue/.rvm/gems/ruby-2.1.3/wrappers/ruby
  To use in Nginx : passenger_ruby /home/sfvue/.rvm/gems/ruby-2.1.3/wrappers/ruby
  To use with Standalone: /home/sfvue/.rvm/gems/ruby-2.1.3/wrappers/ruby /home/sfvue/.rvm/gems/ruby-2.1.3/gems/passenger-5.0.23/bin/passenger start

The most important information is the "Command" line:

 Command: /home/sfvue/.rvm/gems/ruby-2.1.3/wrappers/ruby

We'll use the path to set PassengerRuby in our VirtualHost setup in next chapter (part II).





memcached install

Install memcached:

$ sudo yum -y install memcached

To start:

$ sudo systemctl restart memcached

To make memcached starts at boot:

$ systemctl enable memcached

To check the status of memcached:

$ systemctl status memcached
● memcached.service - Memcached
   Loaded: loaded (/usr/lib/systemd/system/memcached.service; disabled; vendor preset: disabled)
   Active: active (running) since Sat 2016-01-23 13:38:23 PST; 3min 35s ago
 Main PID: 29658 (memcached)
   CGroup: /system.slice/memcached.service
           └─29658 /usr/bin/memcached -u memcached -p 11211 -m 64 -c 1024

Jan 23 13:38:23 sf systemd[1]: Started Memcached.
Jan 23 13:38:23 sf systemd[1]: Starting Memcached...




redis install

Install redis:

$ wget http://download.redis.io/redis-stable.tar.gz
$ tar xvzf redis-stable.tar.gz
$ cd redis-stable
$ make
  1. redis-server is the Redis Server itself.
  2. redis-sentinel is the Redis Sentinel executable (monitoring and failover).
  3. redis-cli is the command line interface utility to talk with Redis.
  4. redis-benchmark is used to check Redis performances.
  5. redis-check-aof and redis-check-dump are useful in the rare event of corrupted data files.

Run redis:

[sfvue@sf redis-stable]$ ./src/redis-server redis.conf
30797:M 20 Jan 16:35:39.486 # You requested maxclients of 10000 requiring at least 10032 max file descriptors.
30797:M 20 Jan 16:35:39.486 # Redis can't set maximum open files to 10032 because of OS error: Operation not permitted.
30797:M 20 Jan 16:35:39.486 # Current maximum open files is 4096. maxclients has been reduced to 4064 to compensate for low ulimit. If you need higher maxclients increase 'ulimit -n'.
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 3.0.6 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
 |    `-._   `._    /     _.-'    |     PID: 30797
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                      

Actually, we may want the redis to be daemonized:

$ pwd
/var/www/pygoogle.com/redis-stable

$ ls
00-RELEASENOTES  COPYING   INSTALL    nohup.out   runtest           sentinel.conf  utils
BUGS             deps      Makefile   README      runtest-cluster   src
CONTRIBUTING     dump.rdb  MANIFESTO  redis.conf  runtest-sentinel  tests

So, we need to edit a line in redis.conf:

daemonize yes

Then, run it:

$ ./src/redis-server redis.conf

$ ps -ef|grep redis
sfvue     5994     1  0 20:25 ?        00:00:00 ./src/redis-server *:6379

To start the redis server, we can use:

$ sudo systemctl start redis.service

To check the running status of redis server:

$ sudo systemctl status redis.service

To enable redis server at system's booting time.

$ sudo systemctl enable redis.service




postgres install

Install postgres:

$ sudo yum install postgresql postgresql-contrib

Then, work on firewall:

$ sudo iptables -I INPUT -p tcp -m tcp --dport 5432 -j ACCEPT

Create a new PostgreSQL database cluster:

$ sudo postgresql-setup initdb

PostgreSQL, however, in its default set, does not allow password authentication. We will change that by editing its host-based authentication (HBA) configuration (/var/lib/pgsql/data/pg_hba.conf):

# "local" is for Unix domain socket connections only
local   all             all                                     md5
# IPv4 local connections:
host    all             all             127.0.0.1/32            md5
# IPv6 local connections:
host    all             all             ::1/128                 md5

We want to switch "md5" to "trust" for 'local' in the configuration file, then go into PostgreSQL, set a password:

$ psql postgres
    
postgres=# ALTER USER postgres WITH PASSWORD 'password';
ALTER ROLE
postgres=# \q

Looks like we need quote(') for the password.

Now, our PostgreSQL is configured to allow password authentication.

Let's start it and enable boot-time run:

$ sudo systemctl start postgresql

$ sudo systemctl enable postgresql
Created symlink from /etc/systemd/system/multi-user.target.wants/postgresql.service to /usr/lib/systemd/system/postgresql.service.

Our installation procedure created a user account called postgres that is associated with the default Postgres role. In order to use Postgres, we'll need to log into that account. Let's go into the postgres shell with a user name postgres:

$ sudo -i -u postgres psql
postgres=# \l
                                  List of databases
   Name    |  Owner   | Encoding |   Collate   |    Ctype    |   Access privileges   
-----------+----------+----------+-------------+-------------+-----------------------
 postgres  | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | 
 template0 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
           |          |          |             |             | postgres=CTc/postgres
 template1 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
           |          |          |             |             | postgres=CTc/postgres
(3 rows)

postgres=# \q
-bash-4.2$ logout
$ 




rake db:setup

Let's setup the db:

$ bundle exec rake db:setup

We can see it created a new db for us:

$ sudo -i -u postgres
-bash-4.2$ psql
psql (9.2.14)
Type "help" for help.

postgres=# \l
                                       List of databases
        Name         |  Owner   | Encoding |   Collate   |    Ctype    |   Access privileges   
---------------------+----------+----------+-------------+-------------+-----------------------
 postgres            | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | 
 pygoogle_production | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | 
 template0           | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
                     |          |          |             |             | postgres=CTc/postgres
 template1           | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
                     |          |          |             |             | postgres=CTc/postgres
(4 rows)

postgres=# \c pygoogle_production 
You are now connected to database "pygoogle_production" as user "postgres".

pygoogle_production=# \dt
                 List of relations
 Schema |         Name          | Type  |  Owner   
--------+-----------------------+-------+----------
 public | authentications       | table | postgres
 public | oauth_caches          | table | postgres
 public | rails_admin_histories | table | postgres
 public | schema_migrations     | table | postgres
 public | users                 | table | postgres
(5 rows)

pygoogle_production=# select * from users;
 id | first_name | last_name | image_url | email | encrypted_password | reset_password_token | reset_password_sent_at |
 remember_created_at | sign_in_count | current_sign_in_at | last_sign_in_at | current_sign_in_ip | last_sign_in_ip | co
nfirmation_token | confirmed_at | confirmation_sent_at | unconfirmed_email | failed_attempts | unlock_token | locked_at
 | created_at | updated_at | is_admin 
----+------------+-----------+-----------+-------+--------------------+----------------------+------------------------+
---------------------+---------------+--------------------+-----------------+--------------------+-----------------+---
-----------------+--------------+----------------------+-------------------+-----------------+--------------+----------
-+------------+------------+----------
(0 rows)

pygoogle_production=# \q
$

We can drop the newly create DB: pygoogle_production.

$ bundle exec rake db:drop

Since we drop them, we may want to create them again to make our app to work:

$ bundle exec rake db:setup

Actually, the DBs are created by referencing to config/database.yml:

...
production:
  adapter: postgresql
  encoding: unicode
  database: <%= Rails.application.config.settings.app_name %>_production
  pool: 5
  username: postgres
  password:


We'll do Apache configurations including SSL and other setups such as Facebook login etc. in next chapter Deploying a Rails 4 app on CentOS 7 production server with Apache and Passenger II.















Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization

YouTubeMy YouTube channel

Sponsor Open Source development activities and free contents for everyone.

Thank you.

- K Hong






Sponsor Open Source development activities and free contents for everyone.

Thank you.

- K Hong







Ruby on Rails



Ruby On Rails Home

Ruby - Input/Output, Objects, Load

Ruby - Condition (if), Operators (comparison/logical) & case statement

Ruby - loop, while, until, for, each, (..)

Ruby - Functions

Ruby - Exceptions (raise/rescue)

Ruby - Strings (single quote vs double quote, multiline string - EOM, concatenation, substring, include, index, strip, justification, chop, chomp, split)

Ruby - Class and Instance Variables

Ruby - Class and Instance Variables II

Ruby - Modules

Ruby - Iterator : each

Ruby - Symbols (:)

Ruby - Hashes (aka associative arrays, maps, or dictionaries)

Ruby - Arrays

Ruby - Enumerables

Ruby - Filess

Ruby - code blocks and yield

Rails - Embedded Ruby (ERb) and Rails html

Rails - Partial template

Rails - HTML Helpers (link_to, imag_tag, and form_for)

Layouts and Rendering I - yield, content_for, content_for?

Layouts and Rendering II - asset tag helpers, stylesheet_link_tag, javascript_include_tag

Rails Project

Rails - Hello World

Rails - MVC and ActionController

Rails - Parameters (hash, array, JSON, routing, and strong parameter)

Filters and controller actions - before_action, skip_before_action

The simplest app - Rails default page on a Shared Host

Redmine Install on a Shared Host

Git and BitBucket

Deploying Rails 4 to Heroku

Scaffold: A quickest way of building a blog with posts and comments

Databases and migration

Active Record

Microblog 1

Microblog 2

Microblog 3 (Users resource)

Microblog 4 (Microposts resource I)

Microblog 5 (Microposts resource II)

Simple_app I - rails html pages

Simple_app II - TDD (Home/Help page)

Simple_app III - TDD (About page)

Simple_app IV - TDD (Dynamic Pages)

Simple_app V - TDD (Dynamic Pages - Embedded Ruby)

Simple_app VI - TDD (Dynamic Pages - Embedded Ruby, Layouts)

App : Facebook and Twitter Authentication using Omniauth oauth2

Authentication and sending confirmation email using Devise

Adding custom fields to Devise User model and Customization

Devise Customization 2. views/users

Rails Heroku Deploy - Authentication and sending confirmation email using Devise

Deploying a Rails 4 app on CentOS 7 production server with Apache and Passenger I

Deploying a Rails 4 app on CentOS 7 production server with Apache and Passenger II

OOPS! Deploying a Rails 4 app on CentOS 7 production server with Apache and Passenger (Trouble shooting)











Contact

BogoToBogo
contactus@bogotobogo.com

Follow Bogotobogo

About Us

contactus@bogotobogo.com

YouTubeMy YouTube channel
Pacific Ave, San Francisco, CA 94115

Pacific Ave, San Francisco, CA 94115

Copyright © 2024, bogotobogo
Design: Web Master