Etat de l’Art
Le test est devenu un passage quasiment obligatoire dans le processus de développement d’une application. En eet, même après une étude approfondie en amont et une implantation rigoureuse, des bogues d’implantation peuvent subsister. La manière la plus cohérente de découvrir et de corriger ces bogues est de tester si l’application répond bien aux spécications en utilisant un jeu de test couvrant au maximum le code de l’application (i.e. impliquant l’exécution du maximum de lignes de code diérentes de l’application testée) en utilisant des entrées adéquates pour l’application testée. Cependant, pour des applications tolérantes aux fautes, le code correspondant à l’implantation de la tolérance aux fautes ne dépend pas de leurs entrées mais de l’apparition de fautes lors de leur exécution. Une manière d’inclure le test du code correspondant à la tolérance aux fautes lors d’un test est donc de générer ces fautes. L’injection de fautes est alors nécessaire pour obtenir une couverture maximum lors de la phase de test d’une application. Nous avons fait un état de l’art sur l’injection de fautes dans [40] et nous allons dans ce chapitre présenter cet état de l’art. Ainsi, quand on désire injecter des fautes, un premier problème se pose : quand déclencher les fautes ? Diérents types de déclenchement ont ainsi étédnis :
Déclenchement par rapport au temps (Time-based trigger ) : c’est le type de déclenchement le plus simple. Les fautes sont injectées à un temps donné, généralement dépendant d’une variable aléatoire.
Déclenchement par rapport à un événement (Event-based trigger ) : le déclenchement de l’injection de fautes se fait lorsque l’application testée exécute des événementsparticuliers.
Ensuite il faut adapter l’injection de fautes au type du système visé. Pour l’injection de fautes, on peut distinguer deux grandes catégories de systèmes : les systèmes centralisés et les systèmes distribués.
Injection de fautes dans les systèmes centralisés
Il existe déjà de nombreux outils pour l’injection de fautes, les mesures de performance et de résistance aux défaillances. On remarque ainsi deux grandes classes de méthodes pour injecter des fautes, la méthode hardware et la méthode software . Dans les méthodes dites hardware , on peut distinguer l’injection de fautes directe dans les broches du processeur (pin-level fault injection ), utilisée par exemple dans RIFLE [56] et la radiation du matériel par des ions (heavy-ion radiation ). Cependant, ces méthodes sont devenues trop diciles à utiliser et à mettre en place, principalement à cause de la complexité des ordinateurs actuels et de l’investissement matériel trop important. De plus les injections de fautes matérielles sont instables à cause des interférences physiques et peuvent entraîner des dommages sur le matériel. Ensuite, il y a les méthodes dites software (SFI : Sofware Fault-Injection) [1, 73, 81] utilisées par exemple dans Xception [16, 55]. En eet, ces méthodes sont plus faciles à mettre en place nancièrement. De plus, la reproductibilité des expériences est plus facilement envisageable et il n’y a pas de risque d’endommager le matériel servant au test. Nous allons donc écarter les méthodes hardware de notre étude et nous focaliser sur les méthodes software .
Injection de fautes dans les systèmes distribués
Il existe diérents types d’approche pour l’évaluation et la vérication de systèmes distribués :
L’approche orientée simulation
CECIUM [5] est un environnement de test simulant une exécution distribuée et des injections de fautes. L’exécution distribuée est ainsi simulée sur une machine unique avec un espace d’adressage unique. Le code source de l’application testée n’est pas nécessaire.
L’exécution étant simulée sur une machine unique, la reproductibilité des tests devient triviale. En eet, une injection de fautes déterministes peut se révéler nécessaire an de pouvoir relancer le même scénario de fautes plusieurs fois si nécessaire, ce qui est plus facilement réalisable dans ce type d’approche.
De plus, lors de tests sur un système réellement distribué, des fautes involontaires peuvent plus facilement apparaître spontanément, comme la perte de certains messages, faisant ainsi diverger l’injection de fautes du scénario prévu. Ensuite, les propriétés des protocoles distribués sont souvent dépendantes d’un état global et lors d’un scénario de fautes, on peut donc vouloir injecter des fautes par rapport à un état global du système.
Déterminer le bon moment pour injecter les fautes peut donc devenir très dicile dans un système distribué.
Dans les méthodes de simulation, on peut aussi remarquer MEFISTO [49] (Multi-level Error/Fault Injection Simulation Tool). MEFISTO est un outil permettant d’appliquer l’injection de fautes le plus tôt possible dans le processus de conception de systèmes tolérants aux fautes. Il permet ainsi d’injecter des fautes dans des modèles de simulation VHDL.
Néanmoins, ces approches restent de la simulation et ne remplacent pas l’exécution des applications sur un système distribué an d’obtenir des résultats les plus proches possibles d’un système distribué réel. Nous voulons émuler des applications sur un système distribué et les approches orientées simulation ne correspondent donc pas à nos besoins.
L’approche orientée émulation
Quelques outils existent déjà pour émuler des apparitions de fautes dans des systèmes physiquement répartis. L’un de ces outils, DOCTOR [36] (integrateD sOftware fault injeCTiOn enviRonment), permet l’injection de fautes dans les systèmes temps réel . Ce dernier est capable d’injecter diérents types de fautes et de synthétiser du workload (c’est-à-dire de simuler de la charge de travail sur une machine). Il supporte trois types de fautes : fautes de processeurs, de mémoires et de communications. Les fautes injectées peuvent être permanentes, passagères ou intermittentes. DOCTOR a été implanté sur un système temps réel appelé HART. Pendant les évaluations, DOCTOR collecte des informations de performance et de abilité. De plus, une interface graphique intuitive est fournie. Cependant, les types des fautes gérées ne sont pas susants pour l’étude des systèmes distribués à grande échelle. En eet, les fautes les plus fréquentes sont les fautes de type crash (arrêt total du processus) ; on doit donc pouvoir vérier si l’application est résistante à ce type de fautes. De plus, on ne peut spécier que des scénarios de fautes probabilistes (qui sont facilement paramétrables grâce à l’interface graphique).
ORCHESTRA
ORCHESTRA est un outil d’injection de fautes de type software qui permet de tester la abilité et la vivacité de protocoles distribués. Pour cela, une couche d’injection de fautes est insérée au dessous de celle du protocole an de ltrer et de manipuler les messages qui sont échangés entre les participants. Les scripts de test peuvent être probabilistes ou déterministes. En eet, les messages peuvent être retardés, perdus, réordonnés, dupliqués, modiés ou de nouveaux messages peuvent être introduits spontanément dans le système testé pour l’amener à un état particulier.
Architecture de ORCHESTRA
La couche d’injection de fautes est insérée entre la couche du protocole testé et les autres couches inférieures. Elle est composée d’un ltre pour les messages sortants et d’un ltre pour les messages entrants. Les ltres sont des scripts qui sont appliqués sur les messages entrants et sortants du protocole testé. Ils eectuent trois types d’opérations sur les messages :
ltrage des messages (interception et analyse des messages),
manipulation des messages (perte, retard, réordonnancement, duplication ou modication),
injection de messages (introduction de nouveaux messages).
Dénition des scénarios de fautes dans NFTAPE
La dénition d’un scénario dans NFTAPE se fait en écrivant le script pour le Control Host. Ensuite, il faut fournir un chier contenant toutes les valeurs pour tous les paramètres nécessaires à la réalisation de l’expérience d’injection de fautes et implanter les diérents composants de NFTAPE nécessaires au déroulement du test ou utiliser des composants déjà implantés (s’ils correspondent aux besoins de l’utilisateur).
Conclusion sur NFTAPE
Bien que NFTAPE soit modulaire et très portable, le choix d’une solution de décision complètement centralisée le rend très intrusif et peu approprié pour le passage à l’échelle, donc inadapté à l’émulation de grille distribuée à grande échelle. De plus, sa mise en place peut demander un travail d’implantation très important (du fait que l’utilisateur peut avoir à implanter chaque composant). Finalement, l’écriture d’un scénario devient rapidement complexe du fait de la nature centralisée des décisions lors de tests impliquant de nombreux noeuds.
LOKI
LOKI est un injecteur de fautes basé sur une vue partielle de l’état global du système distribué. Les fautes sont injectées par rapport à un état global du système. Une analyseposthume est exécutée à la n des tests pour calculer un temps global à partir des différentes vues partielles et détermine ainsi si les fautes ont été injectées selon le scénario prévu. Bien qu’injecter des fautes par rapport à un état global d’un système distribué soit très dicile, voire impossible, sans un impact important au niveau du temps d’exécution, cette technique permet de vérier la validité des fautes injectées et de limiter l’impact de l’injecteur de fautes.
Fondements Théoriques
Dans cette partie, nous introduisons les automates communicants (en nous appuyant sur les travaux de Nathalie Bertrand [10]) et les automates temporisés (en nous appuyant sur les travaux de Patricia Bouyer [12]) qui serviront de support pour notre langage d’injection de fautes. Plusieurs entités s’envoient (et reçoivent) des messages et changent d’états en fonction de ces communications ou du temps. Plus particulièrement, nous considérons les systèmes communicants par canaux ables (i.e. sans perte, corruption, insertion, etc, de messages) pour la construction de l’injecteur de fautes. A la n d’un test, des mécanismes identiques à ceux utilisés dans LOKI pourraient permettre de détecter la présence de messages perdus ou reçus trop tardivement et le test pourra alors être marqué comme étant non valide.
Automates communicants
Les automates communicants sont des systèmes composés d’automates nis qui peuvent communiquer par l’intermédiaire de canaux non bornés. Les communications se font de façon asynchrone et l’ordre des messages est toujours préservé, i.e les canaux sont FIFO (rst in rst out ). Ainsi, les messages sont reçus dans l’ordre où ils sont envoyés. Ce modèle permet de décrire naturellement des protocoles de communications entre plusieurs entités. Il est à la base de la sémantique de certains langage de spécication de protocoles tels que SDL [71] et Estelle [14] ainsi que de notre langage de spécication de scénarios de fautes que nous avons nommé FAIL (pour FAult Injection Language).
Le système représenté dans la gure 3.1 constitue un exemple d’automate communicant. Deux composant, le serveur et le client (représenté de part et d’autre des canaux),communiquent par l’échange de message au travers des canaux c 1et c2.
Structure des automates
Ce sont des automates probabilistes qui peuvent éventuellement communiquer par événements. Ils s’expriment sous forme de règles gardées. Ces règles sont composées d’une garde suivie d’une èche et d’actions comme indiqué dans la gure 4.3.
Remarque : dans cet exemple, ?ok est une garde et continue une action. La garde ?ok signie lorsque je reçois le message ok . Les actions servent à injecter les fautes. Ici, l’action halt signie le processus exécuté sur la machine s’interrompt dénitivement . En fait, les règles gardées et les actions sont un peu plus complexes et seront expliquées par la suite.
Dans une suite d’actions de règle, les actions sont séparées par des virgules. Le point virgule est utilisé à la n d’une règle comme indiqué dans la gure 4.4.
Utilisation de bibliothèques dynamiques
L’utilisateur peut vouloir utiliser des fonctions d’une bibliothèque dynamique. Par exemple pour calculer une variable aléatoire en utilisant une fonction aléatoire d’une bibliothèque dynamique. Pour cela il doit donner la signature de cette fonction ainsi que le nom de la bibliothèque où elle se trouve comme dans l’exemple de la gure 4.30.
Pourquoi des types de gardes ?
Le système d’injection de fautes utilise des exceptions pour le déclenchement des fautes, ce qui permet un minimum d’intrusion lors de l’exécution de l’application. En eet, le daemon d’injection de fautes est inactif jusqu’à la levée d’une exception qui correspond aux entités de type interruptible . Lorsqu’une interruption est levée, la validité de toutes les gardes dépendantes de cette interruption est testée. Cette étape correspond au test des entités de type testable . Puis les actions de l’une des règles dont les gardes sont valides seront alors exécutées. C’est pour cela qu’une et une seule entité de type interruptible est nécessaire dans chaque règle standard. La validité des gardes des règles initiales est testée à l’entrée dans le noeud et ne doit donc pas dépendre d’une interruption. Les gardes des règles initiales ne sont donc constituées que d’entités de type testable . Lorsque plusieurs gardes sont valides, le choix de la règle dont les actions seront exécutées se fait, soit de manière aléatoire, soit de manière déterministe en choisissant systématiquement la première règle valide. En eet, ce deuxième mode est nécessaire pour permettre la reproductibilité des tests bien qu’il soit peut être un peu moins intuitif.
La plate-forme FCI
La plate-forme FCI est notre implantation d’une plate-forme d’injection de fautes qui utilise FAIL comme langage de description de scénarios de fautes. Dans cette section, nous allons décrire l’implantation de la bibliothèque d’exécution, la génération de code, le déploiement de la plate-forme et enn, nous allons étudier les tests de surcoût réalisés an de déterminer l’impact de FCI sur l’application testée lors d’une expérience.
La bibliothèque FCI
La bibliothèque FCI fournit au code généré lors de la compilation du scénario de fautes des briques de base nécessaires pour l’exécution du démon FCI. Cette bibliothèque est constituée d’un ensemble de classes C++ utilisant la bibliothèque standard C++ et la bibliothèque ACE (Adaptative Communication Environment) [69, 70], qui est disponible pour un grand nombre de systèmes d’exploitation (incluant MS Windows et diérents systèmes Unix).
La bibliothèque ACE. ACE (Adaptive Communication Environment) est un framework orienté objet libre et open-source qui implante plusieurs motifs pour des logiciels utilisant des communications concurrentes. ACE fournit un ensemble d’adaptateurs C++ réutilisables et des composants framework qui eectuent les tâches courantes de communications pour plusieurs systèmes d’exploitation diérents. Les tâches de communication fournies par ACE incluent le démultiplexage d’événements, le dispatche d’événements vers les gestionnaires, la gestion des signaux, l’initialisation de services, l’exécution concurrente et la synchronisation. ACE vise à être utilisé par les développeurs d’applications haute performance à communication temps réel. Il simplie le développement d’applications réseau orientées objet qui utilisent des communications inter-processus, du démultiplexage d’événements et de la concurrence. ACE est soutenu commercialement par plusieurs compagnies utilisant un modèle de fonctionnement open-source.
Il existe plusieurs avantages à utiliser ACE :
Améliorer la portabilité : les composant ACE facilitent l’écriture d’applications réseau concurrentes sur un certain système d’exploitation et permettent également de les porter rapidement sur d’autres systèmes d’exploitation.
Améliorer la qualité d’une application : les composants de ACE utilisent plusieurs motifs clés qui améliore la exibilité, l’extensibilité, la réutilisabilité et la modularité d’une application.
Améliore l’ecacité et la prévisibilité : ACE est conçu pour fournir un grand nombre de qualités de service incluant une faible latence pour les applications sensibles à la latence, de hautes performances pour les applications sensibles à la bande passante et une bonne prévisibilité pour les applications en temps réel.
Les adaptateurs C++ simplient le développement de l’application en fournissant des interfaces C++ qui encapsulent la concurrence, les communications, la gestion de la mémoire et le démultiplexage d’événements. Les applications peuvent ainsi combiner et composer ces adaptateurs en héritant, en agrégeant, ou en instanciant les composantssuivants.
|
Table des matières
1 Introduction
2 Etat de l’Art
2.1 Injection de fautes dans les systèmes centralisés
2.2 Injection de fautes dans les systèmes distribués
2.2.1 ORCHESTRA
2.2.2 NFTAPE
2.2.3 LOKI
2.2.4 Conclusion
I Un Intergiciel pour l’Injection de Fautes Distribuées et Coordonnées
3 Fondements Théoriques
3.1 Automates communicants
3.2 Automates temporisés
3.2.1 Alphabet et notion de temps
3.2.2 Horloges et opérations sur les horloges
3.2.3 Contraintes d’horloges
3.2.4 Dénition du modèle introduit par Alur et Dill
3.2.5 Propriétés de clôture des automates temporisés
3.3 Conclusion du chapitre
4 Le Langage FAIL (FAult Injection Language)
4.1 Principes du langage
4.2 Syntaxe et sémantique du langage
4.2.1 Structure globale
4.2.2 Structure des automates
4.2.3 Les commentaires
4.2.4 La phase d’initialisation .
4.2.5 Les diérentes actions
4.2.6 Les diérentes gardes
4.2.7 Les variables et fonctions prédénies
4.2.8 Les diérents types de variables et constantes
4.2.9 Déclaration de variables et gestion de l’aléatoire
4.2.10 Précisions sur la sémantique du langage
4.2.11 Restrictions sur les gardes
4.3 Exemples de scénarios possibles
4.3.1 Premier exemple
4.3.2 Deuxième exemple
4.4 Conclusion du chapitre
5 Implantation d’une Plate-forme pour l’Injection de Fautes
5.1 Introduction
5.2 Principe de fonctionnement
5.3 La plate-forme FCI
5.3.1 La bibliothèque FCI
5.3.2 Le compilateur de FCI
5.3.3 L’exécution d’une expérience de test
5.4 Conclusion du chapitre
II Expérimentations
6 Surcoût Induit par l’Utilisation de FCI
6.1 Introduction
6.2 Application utilisée
6.3 Conguration matérielle
6.4 Scénarios de fautes utilisés et résultats obtenus
6.5 Conclusions sur le surcoût induit par FCI
7 Injection de Fautes
7.1 Introduction
7.2 XtremWeb
7.2.1 Vue d’ensemble de XtremWeb
7.2.2 Conditions Expérimentales
7.2.3 Tests préliminaires
7.2.4 Tests d’injection de fautes quantitatives
7.2.5 Tests d’injection de fautes qualitatives
7.3 MPICH-V
7.3.1 MPICH-V : MPI tolérant aux fautes
7.3.2 FAIL-MPI : les applications auto-déployantes
7.3.3 Evaluation de performance
7.3.4 Impact de la fréquence des fautes
7.3.5 Impact de l’échelle
7.3.6 Impact des fautes simultanées
7.3.7 Chasse aux Bogues en utilisant FAIL-MPI
7.3.8 Conclusion
7.4 FreePastry
7.4.1 Scénario de fautes
7.4.2 Conguration expérimentale
7.4.3 Inuence de la périodicité des fautes
7.4.4 Inuence de la probabilité des fautes
7.4.5 Inuence du nombre de n÷uds
7.4.6 Vue d’ensemble
7.4.7 Conclusion
7.5 Conclusion du chapitre
8 Stress d’Applications 155
8.1 Introduction
8.2 Utilisation d’une application dédiée
8.2.1 Présentation de OGSA-DAI
8.2.2 QUAKE : un outil de test pour les services de Grilles
8.2.3 Résultats obtenus
8.3 Utilisation de FAIL-FCI
8.3.1 Le scénario FAIL
8.3.2 Résultats obtenus
8.4 Conclusion du chapitre
9 Simulation d’Utilisateurs dans un Réseau
9.1 Introduction
9.2 Etudes de la disponibilité des ressources
9.3 La distribution Weibull
9.4 La bibliothèque Weibull pour FAIL-FCI
9.5 XtremWeb
9.5.1 Prise en charge de XtremWeb
9.5.2 Conguration expérimentale
9.5.3 Résultats
9.6 Conclusion
10 Conclusions et Perspectives
A La Grammaire de FAIL