Déployer un site Drupal avec Git et Mina
Par Jérémy Chatard le lundi 05 mai 2014
Je vais vous présenter la manière dont je déploie les projets Drupal à l'aide de Git et de Mina. Il s'agit de la méthode que j'utilise depuis maintenant presque 2 ans et je dois dire que j'en suis satisfait. Si vous avez d'autres méthodes ou habitudes que vous jugez plus performantes, n'hésitez surtout pas à me le faire savoir dans les commentaires sur Twitter !
Mina
Mina est un outil de déploiement, conçu exactement sur le même principe que Capistrano. Il est également écrit en Ruby, mais j'ai tendance à le préférer à ce dernier car il est bien plus rapide.
Pour installer Mina, je vous laisse vous reporter à la documentation si vous souhaitez avoir des détails, mais dans le principe il vous suffit d'installer le gem
:
gem install mina
Configurer le déploiement
Nous allons maintenant initialiser Mina pour notre projet.
Placez-vous à la racine de votre projet Drupal, je prends comme exemple notre site www.breek.fr
qui, sur mon ordinateur, se trouve dans le répertoire /Users/jeremy/Documents/www/breek7
. breek7
parce que le site est sous Drupal 7. Adaptez en conséquence pour votre projet.
cd /Users/jeremy/Documents/www/breek7
mina init
La commande mina init
va créer un répertoire config
à la racine de votre projet. Ce répertoire contient un fichier nommé deploy.rb
. Ouvrez ce fichier dans votre éditeur de texte. A la fin de l'article vous trouverez l'intégralité du fichier de configuration de Mina, ça sera plus pratique pour vous.
La version de base du fichier ne peut pas vous convenir. En effet, Mina est écrit par une équipe de développeurs qui travaillent sur des projets Ruby on Rails. Il y a donc plein de choses dont nous n'avons pas besoin dans le template généré par la commande init
.
Nous allons commencer par supprimer les deux lignes suivantes :
require 'mina/bundler'
require 'mina/rails'
Nous utilisons Drupal, donc pas besoin d'inclure les dépendances Bundler et Rails.
La section suivante nous intéresse, car elle spécifie où se trouve le dépôt Git hébergeant le code source et où nous voulons que notre déploiement soit réalisé.
set :domain, 'foobar.com'
set :deploy_to, '/var/www/foobar.com'
set :repository, 'git://...'
set :branch, 'master'
Dans un contexte de projet Drupal, dans 99 % des cas, il y a au minimum deux destinations de déploiement : pré-production et production. Remplacez donc les lignes présentées précédemment par quelque chose comme :
case ENV['to']
when 'production'
set :domain, 'www.breek.fr'
set :deploy_to, '/home/breek'
set :repository, '/home/git/repositories/breek7.git'
set :branch, 'deploy'
else
set :domain, 'stage.breek.fr'
set :deploy_to, '/home/breek/domains/stage.breek.fr'
set :repository, '/home/git/repositories/breek7.git'
set :branch, 'deploy'
end
L'ajout du case ENV['to']
va nous permettre de demander à Mina de déployer vers une destination différente en fonction d'un paramètre passé lors de l'appel à la commande. On va voir ça dans quelques minutes.
Dans ce que nous venons de définir, les différentes valeurs sont les suivantes :
:domain
: le nom de domaine de l'application,:deploy_to
: le répertoire de destination de Drupal sur le serveur de production,:repository
: le chemin vers le dépôt Git de votre projet Drupal,:branch
: la branche du dépôt qui contient le code à déployer.
Vous voyez donc que, dans ma configuration, les seules paramètres qui changent entre production et l'autre configuration, pré-production sont le :domain
et le répertoire de destination :deploy_to
.
Le paramètre que nous devons maintenant configurer est :shared_path
:
set :shared_paths, ['sites/default/settings.php', 'cache', 'sites/default/files']
Il s'agit des fichiers et répertoires que mina va devoir partager entre chacun des releases. Concrètement Mina va héberger ces ressources en-dehors du répertoire de Drupal et créer des liens symboliques depuis le répertoire de Drupal vers ces éléments.
A noter que, dans ma configuration, je spécifie cache
qui est le répertoire généré par le module Drupal Boost pour la génération du cache statique. Si vous n'utilisez pas Boost ne le spécifiez pas.
Nous devons maintenant spécifier quel utilisateur est utilisé pour se connecter au serveur et définir le propriétaire des fichiers et dossiers de notre projet.
set :user, 'breek'
Vous pouvez également définir l'utilisateur au niveau des deux plateformes de destination si l'utilisateur et/ou le serveur n'est pas le même entre pré-production et production.
Nous allons maintenant remplir la tâche :setup
. Celle-ci est responsable de l'initialisation du projet sur les serveurs de destination. Ce qui se passe dans cette tâche est assez claire, je vous laisse lire pour comprendre.
task :setup => :environment do
queue! %[mkdir -p "#{deploy_to}/shared/sites/default/files"]
queue! %[chmod g+rwx,u+rwx "#{deploy_to}/shared/sites/default/files"]
queue! %[mkdir -p "#{deploy_to}/shared/sites/default/files_private"]
queue! %[chmod g+rwx,u+rwx "#{deploy_to}/shared/sites/default/files_private"]
queue! %[mkdir -p "#{deploy_to}/shared/sites/default/tmp"]
queue! %[chmod g+rwx,u+rwx "#{deploy_to}/shared/sites/default/tmp"]
queue! %[mkdir -p "#{deploy_to}/shared/sites/default/sql_dumps"]
queue! %[chmod g+rx,u+rwx "#{deploy_to}/shared/sites/default/sql_dumps"]
queue! %[mkdir -p "#{deploy_to}/shared/cache"]
queue! %[chmod g+rwx,u+rwx "#{deploy_to}/shared/cache"]
queue! %[touch "#{deploy_to}/shared/sites/default/settings.php"]
queue %[echo "Be sure to edit 'shared/sites/default/settings.php'."]
end
Parmi les répertoires créés, notons sql_dumps
qui n'est pas un répertoire standard Drupal, mais qui va nous servir à stocker les dumps SQL que nous allons réaliser lors des déploiements.
Nous devons maintenant dire à Mina comment réaliser le premier déploiement :
desc "Deploys the current version to the server for the first deployment."
task :first_deploy => :environment do
deploy do
# Put things that will set up an empty directory into a fully set-up
# instance of your project.
invoke :'git:clone'
invoke :'deploy:link_shared_paths'
end
end
Ce qui revient simplement à faire un clone du dépôt et relier les liens symboliques vers les ressources partagées.
Ensuite, nous devons spécifier quelles opérations Mina doit réaliser lors de chaque déploiement :
desc "Deploys the current version to the server, and backup database."
task :deploy => :environment do
deploy do
# Breek: Specific Drupal tasks
invoke :'site_offline'
invoke :'backup_database'
# Common Mina tasks
invoke :'git:clone'
invoke :'deploy:link_shared_paths'
# Breek: Specific Drupal tasks
invoke :'backup_database'
invoke :'database_updates'
invoke :'run_cron'
invoke :'clear_caches'
invoke :'clear_advagg'
to :launch do
invoke :'site_online'
end
end
end
Là encore, c'est assez clair. Voici le déroulement des opérations réalisées à chaque déploiement :
site_offline
: met le site en mode Hors-ligne,backup_database
: réalise une sauvegarde de la base de données,git:clone
: clone le dépôt pour récupérer les derniers commits,deploy:link_shared_paths
: mets à jour les liens symboliques,backup_database
: réalise une sauvegarde de la base de données (oui encore, juste au cas où),database_updates
: exécute le script de mise à jour de la base de données,run_cron
: exécute le cron Drupal,clear_caches
: vide les caches Drupal,clear_advagg
: vide les caches du module Advanced CSS/JS Aggregation,:launch
,invoke :'site_online'
: qui remet le site en ligne.
Chacune des tâches décrites ci-dessus sont soit des tâches standards fournies par Mina, soit des tâches spécifiques à Drupal que j'ai écrites, telles que site_offline
, backup_database
, database_updates
, run_cron
, clear_caches
, clear_advagg
. Ces tâches sont décrites dans le fichier de configuration qui se trouvent en bas de l'article.
Bien. Maintenant que la configuration est prête sur notre machine, nous devons nous assurer que le serveur est prêt pour recevoir les déploiements.
Configurer le serveur
Dans ma configuration, j'utilise le même serveur pour pré-production et production, ce qui me simplifie la vie. J'ai donc 5 choses à vérifier :
- que l'utilisateur Linux
breek
existe, - que le répertoire de déploiement de production existe
/home/breek
. - que le répertoire de déploiement de pré-production existe
/home/breek/domains/stage.breek.fr
, - que Drush est bien installé,
- que Git est lui aussi présent.
Je vous laisse faire ces vérifications par vous-même afin de voir si tout roule sur votre/vos serveurs.
Nous allons maintenant construire la structure des répertoires sur le serveur. Pour ce faire, sur votre machine locale, lancez depuis le répertoire de votre instance Drupal la commande suivante :
mina setup
La commande vient de créer la structure des répertoires de déploiements sur le serveur. Mais notez bien, uniquement sur la configuration de pré-production. Nous devons maintenant le faire sur la configuration de production. Pour ce faire nous allons utiliser notre variable d'environnement to
.
mina setup to=production
Même déroulé, mais cette fois en prod.
Dans notre fichier config/deploy.rb
le fait de préciser to=production
nous fait passer par la première condition du case ENV['to']
. Si le paramètre to
est homis ou qu'il n'est pas égale à production
, on déploit sur la pré-production. J'ai choisi cette stratégie pour éviter les déploiements par erreur en production, comme le vendredi après-midi ou le lundi matin par exemple. Safety first! Au pire, vous cassez la pré-production, tout le monde s'en remettra.
Notez qu'il vous reste quelque chose d'important à faire : configurer votre fichier settings.php
pour entrer les paramètres de la base de données. Il vous faudra bien entendu créer les bases de données de prod et pré-prod et y importer manuellement un dump intial, celui de votre machine locale sans aucun doute.
Déployer
Maintenant que tout est prêt de chaque côté, il nous reste à déployer notre première version. Dans le fichier de configuration nous avons spécifié que Mina devait utiliser la branche deploy
de notre dépôt Git. Chacun a ses petites habitudes, ce que je fais généralement, c'est que :
- je développe sur la branche
master
ou une autre branche spécifique à une fonctionnalité, - je teste en locale sous toutes les coutures, puis lorsque je considère que mes commits sont suffisemment sûrs,
- je merge la branche
master
dans la branchedeploy
, - je pousse la branche
deploy
sur le dépôt distant (que nous avons spécifié dans le fichierdeploy.rb
). - on peut alors demander à Mina de réaliser le déploiement.
Résumé en ligne de commande, cela ressemble à ceci :
# On se place sur la branche master
git checkout master
# On pousse sur le dépôt distant (rien d'obligatoire)
git push
# On se positionne sur la branche deploy
git checkout deploy
# On merge la branche master dans la branche deploy
git merge master
# On pousse la branche deploy sur le dépôt distant
git push
# On réalise le déploiement
mina deploy
Mina fait sa tambouille et, normalement, votre site est déployé en pré-production.
Pour déployer en production, vous l'aurez compris, il vous suffit d'ajouter le paramètre to
.
mina deploy to=production
Gagner du temps avec Alfred
Une fois que tout fonctionne, j'ai vite trouvé qu'il était un peu fastidieux et surtout source d'erreurs de taper toutes ces commandes à chaque déploiement. Je me suis donc construit un workflow Alfred (uniquement sous OS X) pour aller plus vite et ne plus faire d'erreur ou d'oubli de merge, de push, etc. Voici en image mon workflow Alfred, je mets un fichier ZIP le contenant à la fin de l'article.
Ainsi, désormais, lorsque dans le lanceur d'Alfred je tape :
deploy breek7
Tout le listing précédent se déroule tout seul et la pré-production est déployée.
Je garde donc le déploiement en production complètement manuel car, pour déployer en production, il faut l'avoir choisi !
Un simple appel à :
mina deploy to=production
Déploiera le projet en production !
Bon c'était un peu long, mais c'était bon non ?
N'hésitez pas à me faire part de vos remarques ou sources d'amélioration dans les commentaires ci-dessous ou sur Twitter @jchatard.
Voici comme promis :
- le fichier de configuration config/deploy.rb complet,
- le Workflow Alfred, pensez à l'adapter !