Une des grandes forces du SaaS est de s’autoriser à oublier tous les petits tracas techniques des logiciels informatiques. Le service est tout le temps disponible et accessible de n’importe où. Pas d’installation de mises à jour, pas de matériels défectueux, pas de maintenance.

Ces tracas n’ont pourtant pas disparu, ils ont simplement changé de mains.

Dès qu’un lot de nouvelles fonctionnalités est développé, c’est à nous de les installer. Les évolutions de Kiubi se font par ces petits bonds qu’on appelle en interne : "release". C’est au final un ensemble de procédures administratives et techniques qui vont amener la plateforme de la version en cours à une nouvelle version.

Pour cela, nous mettons au point un programme qui contient toutes les instructions nécessaires à la mise à jour, comme par exemple la nouvelle version du code source ou les requêtes SQL pour modifier les bases de données. Ce programme est tout simplement appelé un patch. Chez nous, c’est un assemblage de scripts Shell, de PHP et de SQL qui sera exécuté en une fois sur la plateforme.

Le choix du patch par rapport à d’autres techniques de diffusion de mises à jour s’est imposé à nous car, d’une part, le patch permet plus facilement de transmettre les mises à jour à des tiers qui utilise notre technologie sur leur infrastructure et, d’autre part, car il permet d’enrober la mise à jour de tout un ensemble de sécurités qui nous semblent critiques et qu'on détaillera ci-dessous.
 
Un prérequis de base, qui peut sembler évident, est de disposer AVANT la mise à jour d'une copie de sauvegarde des données à distance ainsi qu’une architecture de secours. C’est la sécurité ultime dans l’hypothèse où tout devait se passer affreusement ou horriblement mal.

Branle-bas de combat

Une fois le patch prêt et testé, il est copié en production puis exécuté pour démarrer la mise à jour. Le patch s’assure d'abord de la compatibilité de l’environnement dans lequel il est installé. C’est à dire qu'il vérifie les versions des logiciels installés, la cohérence des données et si la plateforme est dans une version supportée par le dit patch.

Le patch arrête ensuite tous les services qui doivent l’être pour leur mise à jour : apache, démons, mysql, serveur ftp. La mise à jour peut maintenant commencer en tout sérénité. Le patch crée une nouvelle copie complète du code source dans un dossier qui contient déjà toutes les versions antérieures. Un simple lien symbolique désigne le dossier qui contient la dernière version. Cela permet facilement de passer d’une version à une autre en modifiant un lien symbolique.
 
Un lien symbolique est une sorte d’alias qui peut pointer vers un fichier ou un dossier existant. Imaginons deux répertoires qui contiennent des versions différentes d’un même logiciel :
$ mkdir version1 version2 
$ ls 
version1 version2
On peux alors créer un lien symbolique "courant" qui pointe vers "version2" :
$ ln -s version2 courant
$ ls
courant version1 version2
En prenant l’habitude de toujours utiliser les sources dans "courant", on pourra faciliter la mise à jour du logiciel lors de la prochaine version :
$ mkdir version3
$ ln -sf version3 courant
Maintenant "courant" pointe vers "version3".

Les bases de données principales et celles de tous les sites sont ensuite scannées et mises à jour au besoin. Pour ça, nous avons disposé dans chaque base de données une table contenant un petit compteur indiquant dans quelle version elle est. Le patch peut alors déterminer quelle série de requêtes SQL il doit accomplir en comparant ce compteur avec sa version interne.
 
Le patch vérifie ensuite la cohérence des données avant de relancer tous les services qui avaient été arrêtés. Puis, ultime étape, il lance un ensemble de tests automatiques pour vérifier que tout à l’air de bien fonctionner. L’exécution du patch aura duré un peu moins de 10 minutes.
 
Une des dernières vérifications en date est un système de test de non-régression par carottage. Le principe est de collecter des échantillons de code HTML issus d’un panel représentatif de nos sites. Nous allons par exemple insister sur le catalogue produits ou le tunnel de commande des sites e-commerce, et sur le blog ou les pages de contenus des sites institutionnels. La comparaison des échantillons avant et après la mise à jour pourra garantir que les sites n’ont pas été altérés.

Après c’est au tour des humains avec leurs mains pleines de doigts de faire une petite recette manuelle du bon fonctionnement de tout ce qui a été modifié par la mise à jour avant de réouvrir l'accès à tout le monde.

10 ans de retours d'expérience

Notre système de release a évolué petit à petit en 10 ans pour être de plus en plus fiable. Il s’est construit empiriquement au contact de tout ce qui a mal fonctionné. Nous avons évidement anticipé le maximum de soucis que nous pouvions rencontrer, mais la réalité est toujours pleine de surprises ! Alors c’est parti pour un top 7 des enseignements que nous avons tiré de tout ça :

1. Le dev, c'est pas la prod

Le patch doit être testé sur un environnement le plus proche possible de la production. Même type de serveurs, mêmes systêmes d'exploitation, mêmes versions de tout ce qui a été installé dessus. Et dans l’idéal avec une copie des données de production, même partielle. Nous utilisons des machines virtuelles avec snapshot, ce qui nous permet de figer un environnement dans un état idéal puis de rejouer la mise à jour autant de fois que nous le souhaitons.

Dans la lise des pépins qui nous est arrivé, on peut citer la fois où l’inclusion d’un fichier fonctionnait sur les environnement de tests où le système de fichier n’était pas sensible à la casse, mais plantait en production où le système de fichier est sensible à la casse. Tout cela aurait pu être détecté avec un test de mise à jour sur un environnement ISO.

2. L’erreur est humaine

Le patch est un bout de code à poser et à exécuter tel quel. Le minimum d’opérations doit être effectué à la main car il est toujours possible de se tromper dans l’ordre des étapes à suivre ou de faire une faute de frappe/copier/coller dans une ligne de commande. Quel est le sysadmin qui n’a jamais fait de "rm -rf /" me kill -9 le premier shell.

Il y a 10 ans, l’arrêt des services était fait manuellement, ce qui était source d’erreurs. Maintenant c’est fait de façon automatique. C'est fiable et beaucoup plus rapide. On y a gagné également en confort, on sait que tout est arrêté dans les règles de l’art.

3. Chérie, ça va couper

Ne pas hésiter à faire une interruption de service pendant les mises à jour pour des raisons de simplicité et de fiabilité. Nous avons le luxe d’arrêter tous les services qui utilisent le code source, de purger les caches, puis de les redémarrer une fois le nouveau code en place. On exclut donc les possibilités d’avoir différents bouts de la plateforme qui exécutent des versions différentes du code en manipulant différentes versions des données. Et des bouts, il commence à y en avoir un paquet, entre les APIs, les sites, les consoles d’administration, sans compter les différents démons qui s’occupent des imports/exports, des envois d'emails, etc.

Pour assurer une mise à jour à chaud, il faudrait prévoir et tester le fonctionnement des différentes parties de la plateforme malgré des variations dans les versions de code source et des données. C’est beaucoup de temps, d’efforts et de complexité pour garantir un fonctionnement dans des conditions exceptionnelles qui ne durent en principe que le temps de la mise à jour.

Un dernier avantage est aussi de pouvoir dédier toute la puissance des serveurs à la mise à jour. Les bases de données sont par exemple soulagées de quasiment toute leur charge. Et malgré tout, c’est la mise à jour des quelques milliers de tables nécessaires qui reste l’opération la plus longue actuellement (dans les 5 minutes). Il en serait autrement à pleine charge.

Il faut toujours néanmoins penser aux utilisateurs et proposer un écran de courtoisie expliquant qu'une mise à jour est en cours. Une petite page statique "Mise à jour en cours" est la moindre des choses plutôt qu’un site qui ne réponds pas du tout.
 
Petit détail SEO, pendant nos périodes de maintenance, nous prennons soin de diffuser un code HTTP 503 pour signaler aux moteurs de recherche qu’une maintenance est en cours et qu’il faudra repasser un peu plus tard.

4. Ça passe ou ça casse c’est comme avant

C’est bien d’avoir un plan d’action quand tout se passe bien, mais c’est encore mieux d’en avoir un dans le cas où ça dérape. Le point le plus crucial est qu'un patch sache non seulement comment mettre à jour l'architecture mais aussi comment revenir en arrière en cas de soucis. Il doit pouvoir défaire de façon autonome tout ce qu’il a fait. C’est à dire qu’à la moindre erreur, à n’importe quelle étape, la plateforme doit revenir à son état d’origine.

Pour le code source, c’est facile car le code est archivé. Un simple changement de lien symbolique et tout est à nouveau en place. Pour les données, c’est un peu plus complexe. Hors de question de restaurer la sauvegarde, ça prendrait bien trop de temps. Le patch doit contenir le code permettant directement de faire les opérations inverses. Si un patch ajoute par exemple un champ dans une table Mysql, il doit savoir l’enlever aussi.

5. Les logs, c’est la vie

Le patch doit écrire dans un coin absolument tout ce qu’il fait. La moindre petite commande, et sa sortie, aussi insignifiante soit-elle, doit être loggée et horodatée. C’est très important pour identifier les problèmes, les corriger et améliorer le patch pour la prochaine fois.

Ça nous est arrivé d’analyser ces logs pour identifier les parties du patch les plus lentes et les optimiser. C’est comme ça que le temps d’exécution des patchs est passé de 45 minutes à moins de 10 actuellement.
 
Nous utilisons deux petites fonctions shell bien pratiques pour exécuter ET logger une commande en même temps :
function log {
echo "-- $(date) : $*" >> update.log
}
function xlog {
log "$*"
$* >> update.log
}
Par exemple la copie d’un fichier :
$ xlog cp fileA fileB

6. Notez tous les accros

A chaque release, notez tout ce qui s’est mal passé et trouvez une solution pour que ça ne puisse plus arriver la prochaine fois, quitte à rajouter des tests, des sécurités ou en automatisant mieux les taches. Dans le cas où ça ne concerne pas directement le patch, mettez à jour une checklist des points à vérifier avant la mise à jour.

Des fois, il s’agit juste de circonstances exceptionnelles. Il nous est arrivé d’avoir un problème de réseau au moment où nous testions une mise à jour, ce qui a coupé la connexion avec la machine virtuelle de test. Les sessions SSH se sont évidement coupées également en entrainant l’arrêt de l’exécution d'un patch en plein milieu. Depuis, le patch refuse de se lancer s’il n’est pas dans un screen.

7. Bien plus qu’un patch

Le patch n’est pas le seul élément d’une release. Nous préparons également un ensemble de documents pour cadrer l’intervention, à savoir :
  • un bulletin de révision qui contient une liste de toutes les améliorations, nouveautés ou correctifs de la mise à jour
  • la checklist des quelques étapes manuelles à faire
  • une fiche d’intervention qui précise qui touche à quoi, pour quelle raison et avec le détail de ce qui s’est bien/mal passé
  • une cahier de tests pour effectuer la recette de la mise à jour

TL;DR

Une relase est un processus rafiné par le temps et l'experience, qui nous permet d’aborder les mises à jour avec un état d’esprit apaisé et confiant : nous savons exactement quoi faire, que le tout a déjà été exécuté avec succès sur un environnement ISO prod, qu’une stratégie de retour en arrière est en place et que des diagnostiques automatiques vont nous aiguiller rapidement vers d’éventuels problèmes.

Mais on vous rassure, nous gardons quand même un petit frisson quand on est sur le point d’appuyer sur le bouton ;)