Déployer son application javascript

Pour déployer une application, il existe pas mal de solutions, mais celle que j'ai trouvé la plus pratique est Capistrano. Seul soucis, c'est du ruby, et quand on est dev fullstack javascript et qu'on veut déployer une app node.js ou simplement une SPA, ça la fout mal de devoir se coltiner du ruby. Heureusement, il existe une alternative en javascript qui offre le même principe de fonctionnement : ShipitJS.

Cet outil va vous permettre de cloner votre dépôt git dans le dossier local de votre choix, d'effectuer des tâches puis enfin d'envoyer sur votre ou vos serveurs les fichiers de votre choix, avec la possibilité de rollback à n'importe quel moment. On pourra alors utiliser les commandes :

shipit <env> deploy

et

shipit <env> rollback

Pour deployer sur l'environnement configuré, puis pour revenir à la version précédente. On pourra créer autant d'environnements que nécessaire.

Alors comment ça marche ?

Installation

On va commencer par installer shipit-cli et shipit-npm. Le premier étant la commande à utiliser et le second, une liste de tâches qui vont s'ajouter à shipit-deploy(inclus dans shipit-cli) et qui permettront de lancer automatiquement npm install. Mais nous le verrons plus loin en détail.

$ npm install --save-dev shipit-cli shipit-npm

Vous pouvez bien entendu installer shipit-cli en global, mais c'est moche, je préfère le garder en local et utiliser npm run pour lancer la commande. Nous le verrons par la suite.

Configuration

Il nous faut maintenant un fichier shipitfile.js à la racine de votre projet dans lequel nous allons configurer ce qui doit être fait.

module.exports = function (shipit) {  
  require('shipit-deploy')(shipit);
  require('shipit-npm')(shipit);

  shipit.initConfig({
    default: {
      repositoryUrl: 'git@github.com:you/yourproject.git',
      workspace: '/tmp/your-project',
      deployTo: '/home/deploy/your-project',
      deleteOnRollback: false,
      keepReleases: 2,
      npm: {
        remote: false
      }
    },
    staging: {
      servers: 'deploy@192.168.33.42',
      branch: 'feature/deploy'
    },
    production: {
      servers: 'deploy@your-project.com',
      branch: 'master'
    }
  });
};

Nous commençons par charger shipit-deploy qui est une liste de tâches pré-configurées pour permettre un fonctionnement à la Capistrano : le dépôt git est d'abord cloné dans un dossier temporaire, puis la branche spécifiée est sélectionnée, puis l'ensemble du dossier est copié sur le serveur distant en gardant les précédentes releases. Le dossier deployTo contient un dossier releases ainsi qu'un lien symbolique nommé current qui pointe vers le dossier dans releases correspondant à la version courante. On peut ainsi spécifier combien de versions précédentes on souhaite garder et facilement revenir à la version précédente en créant un autre lien symbolique.

On charge ensuite shipit-npm qui va se charger de lancer la commande npm install au bon moment.

Il ne reste qu'à configurer les différents paramètres que vous retrouverez dans la doc mais dont les principaux sont les suivants :

  • repositoryUrl : indique l'url du dépôt à déployer
  • workspace : emplacement du dossier de travail, attention à ne surtout pas indiquer le dossier courant car la première étape est de tout y supprimer. Privilégiez un dossier dans /tmp.
  • deployTo: le chemin sur le serveur distant où copier les fichiers.
  • deleteOnRollback : est-ce-qu'on supprime la version la plus récente après un rollback ?
  • keepReleases : le nombre de versions qu'on garde

Ensuite, on peut surcharger ces paramètres selon les environnements. On va d'abord y spécifier le ou les serveurs où deployer et on peut aussi indiquer la branche à sélectionner. Par défaut, c'est master, mais on peut en choisir une autre pour le staging par exemple.

Une fois tout ça fait, il faut encore configurer son serveur. Tout ce dont il a besoin, c'est d'un user deploy (ou ce que vous voulez finalement, puisque vous pouvez le spécifier au niveau du host) et d'un accès ssh avec clé publique. On met ça en place très facilement :

# useradd deploy -m  

# mkdir /home/deploy/.ssh  
# cat my_id_rsa.pub >> /home/deploy/.ssh/authorized_keys  
# chown -R deploy:deploy /home/deploy/.ssh  
# chmod 700 /home/deploy/.ssh  
# chmod 600 /home/deploy/.ssh/authorized_keys

Si vous pouvez vous connecter d'un simple ssh deploy@myhost.com alors c'est bon.

Et là, vous pouvez deployer d'un simple :

$ ./node_modules/.bin/shipit staging deploy

Ou mieux :

$ npm run deploy-staging

Après avoir ajouté à package.json les scripts suivant :

"scripts": {
    // […]
  "deploy-staging": "shipit staging deploy",
  "rollback-staging": "shipit staging rollback",
  "deploy-production": "shipit production deploy",
  "rollback-production": "shipit production rollback"
}

Tâches personnalisées

Mais il manque encore quelque chose !! Deux cas de figures : le premier : vous avez besoin de lancer une commande de compilation avant d'envoyer sur le serveur distant. Typiquement un gulp export par exemple qui va se charger de générer la version combinée et optimisée de votre application dans un dossier non versionné. Le second cas, c'est quand vous avez besoin de lancer une commande sur votre serveur distant après avoir envoyé les fichier, par exemple un petit forever restart myproject.

Dans les deux cas, il faut d'abord créer une tâche, puis écouter l'évènement correspondant au moment où on veut lancer cette tâche. Il existe deux types de taches : les taches simples et les tâches bloquantes. Le second type permet d'attendre la fin de celle-ci avant de continuer la liste de tâches. On va donc utiliser soit shipit.task(name, taskFn) soit shipit.blTask(name, taskFn).

Lancer une commande en local

On va partir de l'exemple de la tâche gulp export qui génère un dossier export avec la version compilée de l'application. On veut donc lancer cette commande après avoir chargé le projet dans l'espace de travail local. Il s'agit donc de l'évènement fetched. Puisqu'on a chargé shipit-npm, notre tâche se fera après la tâche npm install. Ça tombe bien. Voici notre tâche :

shipit.blTask('jspm:install', () => {  
  return shipit.local(`cd ${shipit.config.workspace} && ./node_modules/.bin/jspm install`);
});
shipit.blTask('gulp:deploy', () => {  
  return shipit.local(`cd ${shipit.config.workspace} && ./node_modules/.bin/gulp deploy`);
});

shipit.local permet donc d'exécuter une commande sur la machine locale. Par défaut, nous sommes situé à l'emplacement où le script shipit staging deploy a été lancé, c'est pourquoi il faut commencer par se déplacer dans l'espace de travail.

Ensuite, on attache notre tâche à l'évenement concerné :

shipit.on('fetched', () => shipit.start(['jspm:install', 'gulp:deploy']));  

Dans la cas d'un client SPA, sans backend, on n'a que le dossier compilé à envoyer, on peut donc utiliser l'attribut dirToCopy dans la configuration de shipit pour spécifier le dossier en question au lieu d'envoyer le dépôt entier.

Lancer une commande à distance

Maintenant que nous avons envoyé notre code sur la machine distante, nous voudrions pouvoir redémarrer le serveur node afin qu'il fonctionne avec la dernière version du code. On va prendre comme exemple l'utilisation de forever qui propose la commande forever restart <name> afin de facilement redémarrer une application node. Nous allons donc créer la tâche suivante :

shipit.blTask('forever:restart', () => {  
  return shipit.remote('forever restart myproject');
});
shipit.on('published', () => shipit.start('forever:restart'));  

Et c'est parti pour du deploy à volonté sur n'importe quel serveur avec accès ssh. Il est même possible de deployer sur du AWS ou de sauvegarder ses bases de données.

Hadrien

Hi, I'm a french Web Lead Developer, Front End Architect from Toulouse, France. I've worked for 7 years for Overblog then 2 years with AngularJS. Now, I'm a great fan of Aurelia.io.

Toulouse, France https://hadrien.eu