Skip to content

RoR : Déployer votre application avec Capistrano, Nginx et Unicorn

Soyez sociable ! Partagez :
  • Facebook
  • Twitter
  • Delicious
  • LinkedIn
  • StumbleUpon
  • Add to favorites
  • Email
  • RSS

Vous êtes au début du développement de votre nouvelle application Ruby on Rails et vous vous posez la question de la mise en production de votre site. Ou tout simplement, vous souhaitez tester la version staging de votre application sur autre serveur.

Je vais vous expliquer dans ce tutoriel comment déployer une application Ruby On Rails avec capistrano, Unicorn et Nginx.*

0# – Conseil pour démarrer

Je ne saurai trop vous conseiller d’ouvrir un fichier de note pour écrire tout ce que vous faites sur votre serveur et pour éventuellement y reporter vos nouveaux mots de passe.

/!\ N’oubliez pas également qu’il faut modifier ses mots de passe très régulièrement

 #1 – Je reçois les identifiants de mon nouveau serveur !

Comme souvent quand on reçoit un serveur dédié ou un VPS, on a des accès root avec un mot de passe. Notre première étape va consister en la modification des accès root :

ssh root@<ip>
>> Renseigner le mot de passe

Une fois connecté en SSH à votre serveur, faites :

passwd root

Puis suivez les instructions pour modifier le mot de passe ! Utilisez http://www.generateurdemotdepasse.com/ pour générer vos mots de passe, surtout celui du root que vous n’utiliserez plus jamais, donc n’hésitez surtout pas à en mettre un très difficile, genre 22 caractères, chiffres, lettres et caractères spéciaux.

On va créer ensuite le propriétaire de l’application. En général, il porte le nom de l’application.

sudo adduser myapp

De même renseignez un mot de passe, en essayant de respecter les règles de sécurité d’un mot de passe. Puis on ajoute l’utilisateur au groupe sudo

sudo adduser myapp sudo
exit

Ensuite on quitte notre serveur et on se reconnecte en utilisant l’utilisateur “myapp”

ssh myapp@<ip du serveur>

Voilà, notre première étape se termine, nous avons:

  • modifié les accès root
  • créé un utilisateur propriétaire de l’application
  • ajouté cet utilisateur au groupe sudo

 #2 Préparation de la Base de données

Avant de se lancer dans un apt-get install, on va commencer par mettre à jour le serveur :

sudo apt-get update && sudo apt-get upgrade

Une fois les mises à jours faites et installées, on peut installer MySQL

sudo apt-get install mysql-server

/!\ On vous demandera un mot de passe pour l’utilisateur root, comme toujours, on va générer un mot de passe bien compliqué à retenir car on ne l’utilisera plus.

Ensuite on se connecter au serveur Mysql

mysql -u root -p

Entrez le mot de passe précédemment généré.

On va maintenant créer notre base de données. Je suis très sensible au convention de nommage même pour les bases de données. On va donc préférer un structure du genre : <nom de l’application>_<version de l’application> :

CREATE USER 'myapp'@'localhost' IDENTIFIED BY '<mot de passe>';
CREATE DATABASE myapp_<version>;
GRANT ALL privileges ON myapp_<version>.* TO 'myapp'@'localhost';
FLUSH PRIVILEGES;

Voilà, notre base de données est prêtes à accueillir nos données !

Pour la suite du tutoriel, je vais considérer que l’on utilise la version “production” de l’application.

#3 Installation de GIT

Aujourd’hui, tous les bons projets utilisent un outils de versionning. Le plus connu est GIT. Nous allons avoir besoin de git pour déployer notre application.

sudo apt-get install git-core

Nous allons utiliser GIT pour tout ce qui va suivre…

#4 Installation de RUBY avec Rbenv

C’est la parti la moins évidente de la configuration. Si vous ne voulez pas utiliser RBENV pour le déploiement de votre application, il existe d’autres solutions (RVM…), mais je ne pourrais pas vous en dire plus.

Rbenv permet notamment de gérer plusieurs version de RUBY à des endroits différents de votre serveur.

D’abord, nous allons installer quelques dépendances :

sudo apt-get install build-essential zlib1g-dev libmysql++-dev libxml2-dev libxslt1-dev libreadline-dev libssl-dev

Une fois ces dépendances installées, on passe à l’installation de RBENV :

git clone git://github.com/sstephenson/rbenv.git .rbenv

Puis nous allons ajouter quelques lignes au “.bashrc” du profil myapp pour que la commande rbenv soit reconnue :

echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
exec $SHELL

/!\ Il faut ajouter ces lignes au bashrc de myapp !

Vous pouvez éventuellement vérifier que les lignes suivantes :

export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"

soient bien intégrées à votre fichier .bashrc :

cat ~/.bashrc

Ensuite, nous allons installer Ruby Build :

mkdir -p ~/.rbenv/plugins
cd ~/.rbenv/plugins
git clone git://github.com/sstephenson/ruby-build.git

Puis, on installe la version de Ruby qui nous intéresse :

rbenv install 2.0.0-p353
rbenv global 2.0.0-p353
rbenv rehash

/!\ Installez la même version de ruby que votre application locale !

On terminera cette étape en installant bundler qui gérera lui même les GEMS

gem install bundler
rbenv rehash

#5 Installation de Nginx

Nous allons maintenant lancer l’installation de Nginx :

sudo apt-get install nginx-full

Nous en avons fini pour cette étape, même si nous créerons des fichiers de configuration plus tard.

#6 Capistrano et Unicorn

C’est l’une des parties du tutoriel qui me semble être la plus dure… J’ai longtemps eu du mal à comprendre le pourquoi du comment du chose ! C’est pour vous dire.

On va utiliser le GEM “capistrano” et “unicorn”, j’ai appris à utiliser la version 2.x de capistrano … Depuis, la version 3 est sorti, je n’ai pas encore eu le temps de me pencher dessus.

 gem "capistrano", "2.15.5"
 gem 'unicorn'

Ensuite, un petit bundle dans le shell

bundle

Vous avez maintenant installer Capistrano… Il ne reste plus qu’à l’utiliser.

Dans le shell, on va taper cette commande :

capify .

Ceci va créer plusieurs fichier de configuration que nous allons bien entendu modifier.

 config/deploy.rb

Voici le code ‘expliqué’ à insérer dans le fichier config/deploy.rb

require "bundler/capistrano"

# multi staging
set :stages, %w(staging production)
set :default_stage, "staging"
require 'capistrano/ext/multistage' # import: after defining stages

# allowing shell interactions
default_run_options[:pty] = true
set :ssh_options, { :forward_agent => true }

set :application, "myapp" # Le nom de votre application
set :user, "myapp" 
# Le nom d'utilisateur de la session (celui que l'on a créé au début

set(:deploy_to) { "/home/#{user}/#{application}_#{rails_env}" }
set(:releases_path)     { File.join(deploy_to, version_dir) }
set(:shared_path)       { File.join(deploy_to, shared_dir) }
set(:current_path)      { File.join(deploy_to, current_dir) }
set(:release_path)      { File.join(releases_path, release_name) }

set :deploy_via, :remote_cache
set :scm, :git
set :repository,  "<votre dépot git>" #Ici mettre votre repo GIT

# number of releases we want to keep
set :keep_releases, 5

set :use_sudo, false

# # unicorn informations
set(:unicorn_config) { "#{current_path}/config/unicorn.rb" }
set(:unicorn_pid) { "#{shared_path}/pids/unicorn.pid" }
set(:unicorn_bin) { "#{current_path}/bin/unicorn" }

# useful for rbenv
set :default_environment, {
  'PATH' => "$HOME/.rbenv/shims:$HOME/.rbenv/bin:$PATH"
}
set :bundle_flags, "--deployment --quiet --binstubs"

namespace :deploy do
  task :start, :roles => :app, :except => { :no_release => true } do
    run <<-CMD
      cd #{current_path} && #{unicorn_bin} -c #{unicorn_config} -E #{rails_env} -D
    CMD
  end

  task :force_stop, :roles => :app, :except => { :no_release => true } do
    run <<-CMD
      if [ -e #{unicorn_pid} ]; then
        kill -9 $(cat #{unicorn_pid});
      fi
    CMD
  end

  task :stop, :roles => :app, :except => { :no_release => true } do
    run <<-CMD
      if [ -e #{unicorn_pid} ]; then
        kill $(cat #{unicorn_pid});
      fi
    CMD
  end

  task :graceful_stop, :roles => :app, :except => { :no_release => true } do
    run <<-CMD
      if [ -e #{unicorn_pid} ]; then
        kill -s QUIT $(cat #{unicorn_pid});
      fi
    CMD
  end

  task :reload, :roles => :app, :except => { :no_release => true } do
    run <<-CMD
      if [ -e #{unicorn_pid} ]; then
        kill -s USR2 $(cat #{unicorn_pid});
      fi
    CMD
  end

  task :restart, :roles => :app, :except => { :no_release => true } do
    run <<-CMD
      if [ -e #{unicorn_pid} ]; then
        kill -9 $(cat #{unicorn_pid});
        sleep 3;
        cd #{current_path} && #{unicorn_bin} -c #{unicorn_config} -E #{rails_env} -D;
      fi
    CMD
  end

end

desc "Create shared folders."
after 'deploy:setup', :roles => :app do
  # for unicorn
  run "mkdir -p #{shared_path}/tmp/sockets"
  run "mkdir -p #{shared_path}/tmp/cache"
  run "mkdir -p #{shared_path}/tmp/sessions"
  run "mkdir -p #{shared_path}/config"
  run "mkdir -p #{shared_path}/files"
end

desc "Link database.yml and unicorn.rb"
after 'deploy:assets:symlink', :roles => :app do
  run "ln -nfs #{shared_path}/config/database.yml #{release_path}/config/database.yml"
  run "ln -nfs #{shared_path}/config/unicorn.rb #{release_path}/config/unicorn.rb"
  run "ln -nfs /home/gemstatic/static #{release_path}/public" # serve static files
end

desc "Migrate Database"
before "deploy:assets:precompile", "deploy:migrate"

desc "Tag the release in GIT"
task :git_tags, :roles => :db do
  tag_name = "#{rails_env.to_s[0,1].upcase}_#{Time.now.strftime('%y%m%d%H%M%S')}"
  puts "Tagging : #{tag_name}"
  run("cd #{release_path} && git tag #{tag_name}")
  run("cd #{release_path} && git push --tags")
end
after "deploy:update_code", "git_tags"

C’est un peu barbare, mais a force de l’utiliser vous le comprendrez !!

Dans ce fichier, le plus important est de configuré les paramètres suivant :

  • set :application, “myapp” : définit le nom de l’application
  • set :user, “myapp” : définit le nom d’utilisateur que l’on a créé tout à l’heure
  • set :repository, “<votre dépot git>” : définit votre repo GIT (vous vous souvenez, tout est géré par git !)

Puis créer votre fichier environnement : config/deploy/production.rb

set :rails_env, "production"

set :branch, 'master'

server '<ip_du_serveur>', :app, :web
role :db, '<ip_du_serveur>', :primary => true

Ici, il suffit simplement de modifier “<ip_du_serveur>” avec…. l’IP de votre serveur.

N’hésitez pas à poster vos questions sur ce fichier dans les commentaires. J’essaierai de vous aider autant que je peux.

config/unicorn.rb

Le fichier unicorn.rb est également un fichier difficile à comprendre. N’hésitez pas à le copier/coller directement. Je pense qu’au fur et à mesure de galères vous commencerez à le comprendre :

rails_env = 'production'

working_directory "/home/myapp/myapp_production/current"
worker_processes  4
preload_app       true
timeout           30

listen      '/tmp/unicorn_myapp_production.sock', :backlog => 2048

pid         "/home/myapp/myapp_production/shared/pids/unicorn.pid"

stderr_path "/home/myapp/myapp_production/shared/log/unicorn.log"

stdout_path "/home/myapp/myapp_production/shared/log/unicorn.log"

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

before_fork do |server, worker|
  ActiveRecord::Base.connection.disconnect!

  old_pid = "/home/myapp/myapp_production/shared/pids/unicorn.pid.oldbin"
  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|
  ActiveRecord::Base.establish_connection

  worker.user('rails', 'rails') if Process.euid == 0 && rails_env == 'production'
end

/!\ Ce fichier unicorn ne fonctionne que pour la partie “production” de votre application, il nécessitera quelques modifications pour le faire fonctionner pour tous types d’environnement (staging…)

7# Configuration de Nginx

Pour cette partie, on se connectera de nouveau en SSH au serveur :

ssh myapp@<ip_du_serveur>

Ensuite, on ira créer un fichier de configuration pour nginx :

sudo nano /etc/nginx/site-available/myapp_production.conf

Et encore une fois, je vous invite à copier/coller cette configuration :

upstream myapp_production {
 	server unix:tmp/unicorn.myapp_production.sock fail_timeout=0;
}

server {
	listen   80; ## listen for ipv4; this line is default and implied
	#listen   [::]:80 default ipv6only=on; ## listen for ipv6

	root /home/myapp/myapp_production/current/public;

	# Make site accessible from http://localhost/
	server_name myapp.fr;

	location /doc/ {
		alias /usr/share/doc/;
		autoindex on;
		allow 127.0.0.1;
		deny all;
	}
	
	location / {
		try_files $uri $uri/index.html $uri.html @myapp_production;
	}

	location @myapp_production {
    		proxy_read_timeout 300; # https://github.com/gitlabhq/gitlabhq/issues/694
    		proxy_connect_timeout 300; # https://github.com/gitlabhq/gitlabhq/issues/694
   		proxy_redirect     off;
	
    		proxy_set_header   X-Forwarded-Proto $scheme;
		proxy_set_header   Host              $http_host;
    		proxy_set_header   X-Real-IP         $remote_addr;
    		proxy_set_header   X-Forwarded-For   $remote_addr;
	
    		proxy_pass http://myapp_production;
  	}	

}

Dans ce fichier, il va falloir modifier quelques lignes en fonction des paramètres que vous avez indiqués précédemment.

  • server unix:tmp/unicorn.myapp_production.sock fail_timeout=0; => il faut reprendre la variable à coté de “listen” du fichier unicorn.rb
  • /home/myapp/myapp_production/ => le chemin de votre application que vous avez indiqué dans le deploy.rb (remplacer le premier myapp par le nom d’utilisateur et le second par le nom de l’application
  • myapp_production doit être le même partout !
  • server_name : le nom de domaine de votre application

#8 Déployer l’application

On revient sur notre PC (exit du SSH). Allez dans le fichier de votre application, puis on va taper :

cap production deploy:setup

Cette commande va créer les dossiers qui contiendront votre application :

  • shared  => Va contenir les fichiers de configuration commun à toutes les versions de votre application (database, unicorn…)
  • current => Est juste un lien symbolique vers la release en cours
  • release => contient la version de votre application courante et les anciennes (5 releases gardées)

Retournez sur votre serveur en SSH :

ssh myapp@<ip_du_serveur>

Puis créer le fichier de configuration suivant :

  • shared/config/database.yml

database.yml

production:
  adapter: mysql2
  encoding: utf8
  database: myapp_production
  username: myapp
  password: <mot_de_passe>
  host: localhost
  port: 3306

Revenez pour finir, sur votre PC dans votre répertoire de développement, puis tapez la commande :

cap production deploy:cold

Le “:cold” à la fin de la commande indique que c’est votre premier déploiement, ainsi Capistrano n’effectuera pas certaine tâche.

Quand vous souhaiterez de nouveau déployer votre application, tapez simplement la commande :

cap production deploy

Et votre application se déploiera automatiquement.

#9 Quelques astuces pour finir

Vous pouvez ajouter votre clef publique afin que vous n’ayez pas à entrer le mot de passe de “myapp” à chaque déploiement.

Pour cela, récupérer votre clef publique :

cat ~/.ssh/id_rsa.pub

Puis, copiez le contenu du fichier, connectez vous au serveur avec “myapp” :

ssh myapp@<ip_serveur>

Puis

sudo nano ~/.ssh/known_hosts

Dans ce fichier, collez votre clef publique.

Voilà, je signe ici l’un de mes plus gros tutoriels sur ce site depuis sa création.
J’espère qu’il vous aura aidé, sinon, n’hésitez pas à faire vos remarques en commentaire !

Merci pour votre lecture.

Soyez Sociable ! Partagez !
Published inCapistranoNginxRubyOnRailsTutorielUnicorn
banner