Déployer un site Drupal avec Git et Mina

Photo de Jérémy

Par Jérémy Chatard le lundi 05 mai 2014

Déployer Drupal avec Mina

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 se trouve le dépôt Git hébergeant le code source et 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 branche deploy,
  • je pousse la branche deploy sur le dépôt distant (que nous avons spécifié dans le fichier deploy.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.

Alfred workflow déploiement Drupal avec Mina - Général

Alfred workflow déploiement Drupal avec Mina - Keyword

Alfred workflow déploiement Drupal avec Mina - Commandes de déploiement

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 :

Ecrit par Jérémy le lundi 05 mai 2014 — Tags : Drupal

comments powered by Disqus