📜 ⬆️ ⬇️

Bundle rvm + Rails + Nginx + Unicorn or deploy rails correctly

The purpose of this article is to describe in detail the organization of the server for Rails applications in the currently most popular bundle: rvm + Rails + Nginx + Unicorn. The writing of the article was prompted by the absence of complete step-by-step documentation on this bundle, which is understandable not only to the vigorous professionals in this field. Next, I will try in detail, step by step, to describe the ideologically correct process of organizing a server for servicing several Rails applications (using one example) - if you have absolute confidence that more than one application will never work on a test machine - the setting may Much shorter and simpler. I want to warn you that the subtleties concerning the work of the application under high load are not described in the article, since the goal was different - to make the application work in a bundle and reduce the number of conflicts with other applications to a minimum.

Ssh key

Before using the tools listed in the header, you need to prepare the server on which we are going to organize everything. Suppose you just installed a fresh Ubuntu 10.04 LTS on the server (+ started up in the process of the first user), and raised OpenSSH daemon there. Everything! From now on, the server should not be touched for arms, legs and other limbs - we will only work with it remotely, and for this you should run on your working machine:
ssh-copy-id vasya@rails-production.example.com 
where vasya is the name of the user on the server, on behalf of which the rights will be implemented by the deploy, and rails-production.example.com is the address or name of the server you just raised. After entering, it will be necessary to agree to add a host to the list of known hosts on your machine - that's okay - this is normal. And enter Vasin password. This will be the last time you will enter Vasily's password. Now access to the server is possible using the ssh key and nothing needs to be entered.

It would seem that these are the basics of which it is not worth mentioning - but there is always a certain percentage of people who have an alternative point of view on access to the machine via ssh keys. For them, I can advise ointment from arthritis of the joints of the hand, the rest I suggest just to believe that ssh keys are good.

Database

Here I’ll just give you tips on installing the right packages for the two most popular DBMSs:
 sudo apt-get install mysql-server mysql-client libmysqld-dev # MySQL sudo apt-get install postgresql postgresql-client postgresql-server-dev #Postgresql 
Setting up a database on a server is the topic of a separate article, so suppose that you can handle it yourself. Therefore - tadaaam! DBMS is up and running.
')
Rvm

Rvm is a Ruby version control tool in the system that allows you to create separate “environments” from gems, which is not important in our case. If we consider the concepts of bundler and gemsets Rvm, then there may be a feeling that they are designed for the same purpose - to isolate the environment for the work of a particular application. Bundler is a great tool for resolving gem dependencies, and Rails 3 works with it by default. And in general, since we are talking about this - I recommend using the bundler for Rails 2.3.x, as described here .

We need Rvm only to easily switch between different versions of Ruby, and such a need will most likely arise on the server, where applications written on different versions of Ruby on Rails will run simultaneously. Rvm has its opponents. No, and in fact - if you are absolutely sure that more than one version of ruby ​​will never work on this piece of hardware, then it will be more correct to install some ree as a system interpreter of ruby ​​and burn in hell little by little to enjoy life. But the reality is harsh, so I just recommend using rvm - this will keep everything in order.

If you have already read the documentation, you probably noticed that there are two ways to install Rvm - from root (the so-called system wide install) and normal - for a simple user. So again, I want to test your faith - do not install as root. No wonder we created a user responsible for deploy. Therefore, going to the server under our Vasily executes the following sequence of commands:

 sudo apt-get install git-core curl #  ,    Rvm. curl -L https://get.rvm.io | bash -s stable --ruby type rvm | head -1 
The last command should issue "rvm is a function" or equivalent in Russian. If this does not happen, it is worthwhile to start studying from here until the moment - “until it works”.

Ruby

Choosing the version of Ruby depends on which version of the rail is used in the application: so for 2.3.x it is preferable to use Ruby Enterprise Edition , for 3.x it is recommended to use 1.9.3. In my case, this is an application on 2.3.x and ree respectively. And if to install ruby ​​1.9.3 nothing extraordinary from the system is required, then to install ree you need several system packages:
 sudo apt-get install build-essential bison openssl libreadline6 libreadline6-dev curl git-core zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-0 libsqlite3-dev sqlite3 libxml2-dev libxslt-dev autoconf libc6-dev ncurses-dev rvm install ree-1.8.7-2011.03 #  ree rvm ree # ,   Ruby .      -     : rvm ree@myapp --create.   Bundler    . gem install bundler #  gem,    . sudo mkdir -p /srv/myapp #  ,      . sudo chown -R vasya:vasya /srv/myapp #      (   ,  -R  ). 


Nginx

Since we will use Unicorn, we cannot do without Nginx - there is such a feature of Unicorn - it cannot work with slow clients. There is, however, its counterpart, which can - Rainbows, but Nginx in itself is extremely useful, good and easy to use. Installation instructions can be found on the website of the author of this wonderful server - Igor Sysoev. I just give here a simple init script to run nginx and nginx.conf:
 #! /bin/sh EXEC_PATH="/usr/local/nginx/sbin/nginx" case "$1" in start) echo "Starting NginX" start-stop-daemon --start --exec $EXEC_PATH ;; stop) echo "Stopping NginX" start-stop-daemon --stop --exec $EXEC_PATH ;; restart) echo "Stopping NginX" start-stop-daemon --stop --exec $EXEC_PATH sleep 1 echo "Starting NginX" start-stop-daemon --start --exec $EXEC_PATH ;; *) echo "Usage: {start|stop|restart}" exit 1 ;; esac exit 0 

 worker_processes 1; #       . user vasya vasya; #      worker -   ,    . pid /tmp/nginx.pid; #       - Nginx. error_log /tmp/nginx.error.log; events { worker_connections 1024; #        . accept_mutex off; #         - . } http { #     -       - -  : include mime.types; default_type application/octet-stream; access_log /tmp/nginx.access.log combined; sendfile on; tcp_nopush on; tcp_nodelay off; gzip on; #    .    upstream .        ,     . upstream myapp_server { server unix:/srv/myapp/shared/unicorn.sock fail_timeout=0; #        config/unicorn.rb    . } server { listen 80 default deferred; #  ,       ip   ,      -  myapp.mydomain.ru:80 client_max_body_size 1G; #     (   -       ). server_name myapp.mydomain.ru; #   keepalive_timeout 5; root /srv/myapp/current/public; #        public Rails .  current       Capistrano try_files $uri/index.html $uri.html $uri @myapp; #     - ,    location    location @myapp { proxy_pass http://myapp_server; #   http://       upstream . proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect off; } error_page 500 502 503 504 /500.html; location = /500.html { root /srv/myapp/current/public; } } } 

It should immediately be warned that when Nginx simultaneously serves several applications, server { ... } blocks should be placed in separate files in the /usr/local/nginx/conf/vhosts and in nginx.conf write include /usr/local/nginx/conf/vhosts/* - in the example of this is not done for clarity.

Unicorn

Unicorn can be installed for the entire system, but this should not be done - it is much more correct to include the appropriate gem in the Gemfile:
 gem 'unicorn' 
and run Unicorn with the bundle exec command. By the way, the recommendation applies not only to Unicorn, but also to any executable files that come with gems. Installing Unicorn within a specific application will allow you to have as many applications as you need on one machine.
Next, I will give an example of server configuration, which provides the so-called zero downtime deploy. So, config / unicorn.rb:
 deploy_to = "/srv/myapp" rails_root = "#{deploy_to}/current" pid_file = "#{deploy_to}/shared/pids/unicorn.pid" socket_file= "#{deploy_to}/shared/unicorn.sock" log_file = "#{rails_root}/log/unicorn.log" err_log = "#{rails_root}/log/unicorn_error.log" old_pid = pid_file + '.oldbin' timeout 30 worker_processes 4 #      ,       listen socket_file, :backlog => 1024 pid pid_file stderr_path err_log stdout_path log_file preload_app true #    ,  ,    . GC.copy_on_write_friendly = true if GC.respond_to?(:copy_on_write_friendly=) #   ,    ,     . before_exec do |server| ENV["BUNDLE_GEMFILE"] = "#{rails_root}/Gemfile" end before_fork do |server, worker| #  ,     ,    . defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect! #   ,   0 downtime deploy. if File.exists?(old_pid) && server.pid != old_pid begin Process.kill("QUIT", File.read(old_pid).to_i) rescue Errno::ENOENT, Errno::ESRCH # someone else did our job for us end end end after_fork do |server, worker| #      ,     . defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection end 


Capistrano

Well, we have already set up everything that is possible basic things, so you should deal with Capistrano. First of all, you should place the capistrano gem into the group: development of our Gemfile, and also put the rvm-capistrano for integration with rvm (for rvm versions> = 1.1.0):
 group :development do gem "capistrano" gem "rvm-capistrano" end 
After installing the gem, execute the command:
 bundle exec capify . 
and we get an almost empty config / deploy.rb file. I will give an example of a file for our needs:
 require 'rvm/capistrano' #   rvm require 'bundler/capistrano' #   bundler.    bundler      ,       . set :application, "myapp" set :rails_env, "production" set :domain, "vasya@rails-production.example.com" #      ssh.             ,    . set :deploy_to, "/srv/#{application}" set :use_sudo, false set :unicorn_conf, "#{deploy_to}/current/config/unicorn.rb" set :unicorn_pid, "#{deploy_to}/shared/pids/unicorn.pid" set :rvm_ruby_string, 'ree' #    ,  Ruby    . set :scm, :git #  git. , ,  -  - svn, ,         git -  git. set :repository, "git@github.com:myprojects/myapp.git" #    . ,         ,   ,     rsa        deployment keys   . set :branch, "master" #        . set :deploy_via, :remote_cache #   ,              .       . role :web, domain role :app, domain role :db, domain, :primary => true before 'deploy:setup', 'rvm:install_rvm', 'rvm:install_ruby' #  rvm  capistrano  ,    cap deploy:setup      rvm_ruby_string . after 'deploy:update_code', :roles => :app do #           - database.yml.       /srv/myapp/shared/config    .           . run "rm -f #{current_release}/config/database.yml" run "ln -s #{deploy_to}/shared/config/database.yml #{current_release}/config/database.yml" end #      unicorn.       -  . #    Rails 3    bundle exec unicorn_rails  bundle exec unicorn namespace :deploy do task :restart do run "if [ -f #{unicorn_pid} ] && [ -e /proc/$(cat #{unicorn_pid}) ]; then kill -USR2 `cat #{unicorn_pid}`; else cd #{deploy_to}/current && bundle exec unicorn_rails -c #{unicorn_conf} -E #{rails_env} -D; fi" end task :start do run "bundle exec unicorn_rails -c #{unicorn_conf} -E #{rails_env} -D" end task :stop do run "if [ -f #{unicorn_pid} ] && [ -e /proc/$(cat #{unicorn_pid}) ]; then kill -QUIT `cat #{unicorn_pid}`; fi" end end 


PS If anyone has any suggestions for improving the material presented, or constructive comments I will be glad to read and correct.

Source: https://habr.com/ru/post/120368/


All Articles