28 février 2010

Application packagée et ressources externes

Dans le monde J2EE/JEE on déploie une application web sous forme d'archive compressée : l'extension « .war » du fichier signifie « webapp archive ».

Dans le monde PHP, pour ne citer que lui, on peut rencontrer le même fonctionnement, avec les fichiers à déployer fournis sous forme d'archive compressée « .zip » par exemple.

Pourtant, sauf en de rares exceptions, une application ne se limite pas à du code exécutable et à des ressources statiques, mais a besoin de ressources externes telles qu'une base de données, un service de messages, ou des services tiers comme des services web.

La base de données est une ressource externe au code déployé

Prenons le cas le plus courant, où l'application utilise une base de données seulement.


En ce qui concerne ce lien, l'application contient du code exécutable, qui encapsule du SQL, qui attaque base de données.

Pour le déploiement, d'un côté on a créé la structure de la base de données, de l'autre on a déposé l'archive de l'application.


Ces deux déploiements conjoints doivent produire un environnement global cohérent. Penser aux « contrats d'interfaces ».


La correspondance d'interfaces entre le client et la ressource invoquée, est cruciale.

Apporter la cohérence par le déploiement lui-même

L'application qui crée la base de données dont elle a besoin

Une astuce qu'emploient de nombreuses applications destinées au grand public est d'opérer la création de la base de données depuis la base de données elle-même.


(J'ai barré pour dire que cela ne me convient pas)

On met l'accent sur la facilité pour l'utilisateur lambda d'initialiser l'environnement d'exécution.

C'est le modèle choisi pour quasiment toutes les applications PHP. Plusieurs frameworks Java offrent également cette possibilité.

Cette approche est sympathique, mais n'est pas forcément cohérente avec une logique de production, et, surtout, de maintenance.

Je trouve d'autre part que c'est prendre un risque opérationnel, car même en production, le code qui permet de supprimer et de recréer les tables de la base de données est embarqué.

L'outil de déploiement couteau suisse


En posant la distinction entre composants déployés et outillage d'administration, on peut utiliser un outil de déploiement qui saura s'occuper de déployer et d'administrer aussi bien l'application que la base de données.


Même si elle a un très gros intérêt, une faiblesse de cette approche est qu'elle réclame la mise en œuvre d'un outil qui sache tout faire, et surtout son utilisation systématique : que se passe-t-il si un administrateur passe outre la procédure outillée ? Un ALTER TABLE en manuel est si vite arrivé…


Contrôler la cohérence avant le démarrage de l'application

Pour la qualité il n'est souvent pas nécessaire d'engager de grandes manœuvres ou de mettre en place des outils contraignants : il suffit parfois de contrôler quelques critères et de bloquer ou de continuer la procédure en fonction.

Une approche consiste alors à embarquer dans le module applicatif déployé une description du modèle physique de données attendu, et de le comparer avec la structure réelle de la base de données.


On bloque le processus de démarrage de l'application si les deux ne correspondent pas (un simple « hash » peut suffire).

Personnellement j'utilise comme référence du modèle un fichier YAML posé dans le répertoire WEB-INF/ de l'archive déployée, ce qui me permet d'avoir une approche unique pour PHP et J2EE/JEE.
Pour Java, on peut penser à introspecter les classes persistantes afin d'en déduire le modèle attendu.

Une sécurité supplémentaire est d'embarquer dans l'application elle-même, non pas du code de manipulation de base de données, mais l'outil de comparaison. L'application elle-même refuse alors de démarrer si la structure de la base de données n'est pas cohérente avec le modèle attendu.



Autres vérifications

Le fichier de référence qui décrit le MPD cohérent avec l'exécution de l'application peut servir à d'autres contrôles, et notamment les suivants :
  • cohérence entre le MPD et les requêtes SQL externalisées (penser aux objets PreparedStatement)
  • cohérence entre le MPD et les classes persistantes des divers frameworks ou approches (Hibernate, JDO…)
Environnements de tests

En phase de tests, on souhaite d'une façon ou d'une autre que l'application soit autoporteuse : à la fois déployer une application et exécuter des scripts SQL de modification de la base de données (de tests), peut être inutilement coûteux.

J'ai parlé plus haut des frameworks qui permettent ça nativement, en disant cependant que l'approche qui mixe modules applicatif et d'administration ne me convenait pas.

Le fait d'embarquer, dans le module applicatif lui-même, une description de référence du modèle attendu, permet de recréer à volonté une base de données de tests cohérente.



Pour ma part je fais cela avec un plugin Maven.

Approche générale, autres ressources

Le point crucial de ce qui précède est de bloquer le démarrage de modules dans le cas où on a décelé que les chaînes de liaison ne sont pas correctes.

Pour cela, l'idée proposée est simplement que le module client sache dire de quelle interface il a besoin, et que la ressource invoquée sache dire au runtime quelle est sa structure.


On obtient une cartographie « à froid » des composants déployés.

On fait ensuite une vérification statique des structures lues, grâce à des outils externes aux modules et ressources déployés.

Aucun outillage lourd n'est nécessaire en amont.

Il faut se rappeler du reste que, dans certains cas réels, l'outillage lourd en amont n'est pas suffisant à assurer la cohérence entre tous les composants déployés, en particulier quand les procédures de déploiement ne sont pas entièrement automatisées.

L'approche pragmatique présentée, complémentaire à l'outillage amont, et qui fait des sanity checks simples, permet de détecter des failles évidentes.

26 février 2010

Asso&Co

Je cite : « La Banque Postale est à l'initiative d'un nouveau site dédié au monde associatif » : http://assoandco.fr/

De bonnes idées. Pas vu si un même compte peut servir à gérer plusieurs associations.

13 février 2010

Restauration quotidienne de sauvegardes

Voici un état de mon architecture de sauvegardes, qui a un peu évolué depuis que j'avais parlé du sujet il y a deux ans (voir : Validation de backups, avril 2008).

Architecture

Les nouveautés concernent essentiellement l'ordonnancement.

Sur le plan fonctionnel, les principes sont :
  • une sauvegarde complète quotidienne
  • une restauration complète quotidienne
  • des tests d'acceptabilité sur les données restaurées (par exemple, vérifier que les données qu'on a restaurées ne sont pas constituées de répertoires vides)
Sur le plan technique, les principes sont :
  • pour tout ce qui concerne les flux entre machines, l'ordonnancement est centralisé
  • les flux sont sécurisés (SSH)
  • les accès sont sécurisés de machine à machine (restrictions des adresses IP autorisées, etc.)
  • on limite les volumes des transferts réseaux (compression BZ2, regroupement des commandes envoyées par SSH…)
L'architecture se synthétise ainsi :


Légende :
  • ref — référentiels, « Repositories » : Ce sont les éléments à sauvegarder. En l'occurrence Subversion, un wiki…
  • m2 — ordonnanceur, « Scheduler »
  • bkp — espace de sauvegarde, « Archives »
  • tmp — espace temporaire pour restauration
  • ci — outil d'intégration continue, « Continuous Integration »
Les machines « ref » et « m2 » sont sur un même réseau local (LAN).
  • « ref » est visible sur internet, ports 22 et 80. Sur le LAN elle est dans une DMZ, et en particulier n'accède pas à « m2 ».
  • « m2 » n'est pas visible sur internet.
Les machines « bkp + tmp » et « ci » sont sur internet, avec leurs accès SSH limités à certaines adresses IP.

Les étapes du processus sont les suivantes :
  1. sauvegardes locales sur « ref », par l'outil « backup-manager »
  2. copie vers une machine distante qui stocke les archives, avec éventuellement une copie intermédiaire locale sur l'ordonnanceur lui-même
  3. décompression de la dernière sauvegarde présente dans les archives, restauration, contrôles et mesures
  4. copie des résultats des contrôles et mesures, vers une machine d'intégration continue
  5. analyse des résultats des contrôles et mesures, tests sur des critères d'acceptabilité
  6. [E] si une erreur est décelée, envoi d'un e-mail à l'administrateur
En gros, j'arrête de poser des crontabs partout comme je faisais il y a deux ans (voir article : Validation de backups) ; c'est un plat de spaghetti à administrer, et, quand une machine tombe ou est recyclée, les autres continuent leur semoule sans que ça serve à quoi que ce soit.

L'ordonnancement centralisé facilite les enchaînements.

En revanche, il ne facilite pas forcément la gestion de verrous.

Autre changement par rapport à il y a deux ans, j'écris mes scripts système en Bash et délaisse Ruby, qui n'apportait pas tant que ça.


Contrôles et mesures

Pour un repository Subversion, après restauration des données, les tests d'intégration continue se feront sur les mesures suivantes :
  • nom et taille du fichier de sauvegarde restauré => la date doit être celle du jour
  • dates de début et fin de la restauration => le traitement ne doit pas avoir été trop rapide
  • numéro de version (Revision) => ?
  • nombre total de fichiers après checkout => il doit être supérieur à telle valeur
  • nombre de fichiers « pom.xml » après checkout => il doit être supérieur à telle valeur
  • nombre de fichiers « build.xml » après checkout => il doit être supérieur à telle valeur
L'idée est de repérer les cas de sauvegardes vides, ainsi que les sauvegardes obsolètes.


Quelques idées rejetées

Il est bon de fonder ses choix aussi sur l'historique des choix qui n'ont pas été retenus ;-)


Idée rejetée : copie directe depuis la machine de référentiel vers la machine de backup.


Explication : pourquoi la machine de référentiel devrait-elle avoir connaissance de l'existence d'une machine de backup ? Et en être dépendante ? Et dépenser de la charge et être administrée pour ça ?
L'idée est que les processus de restauration d'archives doivent être vus par ailleurs. La machine à sauvegarder n'a pas elle-même à savoir qu'elle devrait envoyer un e-mail, etc.


Idée rejetée : accès direct depuis la machine de backup à la machine de référentiel.


Explication : on a choisi dans l'architecture de limiter les accès SSH à « ref» depuis l'internet à l'utilisation de Subversion. En particulier, pas d'accès aux utilitaires « ls » et « tar ».


Idée rejetée : stocker les archives sur la machine d'ordonnancement.


Explication : cette machine est dans les mêmes locaux que la machine référentiel. Or on veut évidemment que les données archivées soient physiquement découplées des données d'origine.


Idée rejetée : ordonnancement par cron de la restauration depuis l'espace temporaire.

C'est ce que faisait l'ancienne architecture.



Explication : l'espace temporaire n'a pas à être administré et donc n'a pas à contenir de scripts résiduels ni de crontab.


Idée rejetée : restauration sur la machine d'ordonnancement.


Explication : Il faut que la restauration ait lieu à partir d'un élément archivé (celui qu'on ira réellement chercher en cas de problème), et pas depuis une copie intermédiaire, qui par définition disparaîtra.
De plus, ni la mémoire ni le CPU ne sont suffisants sur cette machine pour une restauration.


Idée rejetée : envoi des résultats directement depuis l'espace temporaire vers la machine d'intégration continue.


Explication : l'espace temporaire n'a pas à connaître la machine d'intégration continue, à y avoir posé sa clef publique, etc.


Idée rejetée : récupération des résultats par la machine d'intégration continue auprès de l'espace temporaire.


Explication : la machine d'intégration continue n'a pas à savoir qu'il existe un espace temporaire (en plus, comment s'y connecter s'il n'est pas résiduel ?)


Variante possible

Une variante qui semble être digne d'intérêt, consiste à stocker les résultats des contrôles et mesures dans les référentiels, et que la machine d'intégration continue y accède.


Quelques avantages :

  • l'ordonnanceur n'a plus à connaître la topographie de la machine d'intégration continue
  • les résultats des contrôles et mesures sont archivés
  • on s'appuie sur le lien de l'ordonnanceur vers la machine référentiel, qui existe déjà
  • on s'appuie sur le lien de la machine d'intégration continue vers la machine référentiel, qui existe déjà
  • les tests sur les résultats peuvent être lancés depuis n'importe quel environnement qui a accès au référentiel, pas seulement depuis la machine d'intégration continue (donc, pratique pour développer ces tests)


Quelques inconvénients :
  • le référentiel augmente de façon automatique et ininterrompue, or ce référentiel est lui-même destiné à être sauvegardé et archivé, donnant lieu à des contrôles, qui seront injectés dans le référentiel, etc.
    Certes l'augmentation est d'1 Ko par jour, ce qui est dérisoire, mais techniquement, le principe du mécanisme croissant qui s'alimente lui-même n'est pas bon.
  • d'un point de vue fonctionnel ce serait mélanger production et décisionnel.
    Production = référentiel Subversion.
    Décisionnel = tests en intégration continue après extraction de mesures.

10 février 2010

Curriculum Vitæ à jour

Après la compta à jour et même le bilan d'Avantage Compris au clair, voici que je complète mon CV dans les temps !

CV en ligne : http://www.avantage-compris.com/team/dandriana/

J'en ai même fait une version anglaise, mais pour l'instant le site de ma boîte n'est qu'en français :-P

03 février 2010

IZI-collecte, plate-forme web pour les associations

D'après la page d'accueil, IZI-collecte (http://www.izi-collecte.com/) propose :
  • un paiement sécurisé pour les dons et les cotisations à un tarif bas et fixe, comprenant l’émission du e-reçu fiscal et des fonctionnalités communautaires
  • une plateforme d’envoi d’e-mailings et d’e-newsletters.
  • la gestion de base de données adhérents et donateurs