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.