Quel que soit le type de service, nous utilisons le même ensemble de principes d'ingénierie pour atteindre la résilience et la disponibilité, car les principaux défis d'ingénierie de la création de systèmes distribués tolérant les pannes et évolutifs sont les mêmes pour tous les services.
Pour bénéficier de la résilience et d'une disponibilité continue, il est nécessaire de comprendre, puis de traiter toutes les causes d'indisponibilité (dégradation des performances et échecs non gérés) des systèmes à l'échelle du cloud. Ces causes sont très diverses. Nous les regroupons donc par catégorie, selon leur nature fondamentale.
Traditionnellement, l'analyse de la disponibilité des systèmes informatiques professionnels met l'accent sur la catégorie des pannes matérielles. Toutefois, dans le cas des systèmes cloud, la défaillance matérielle constitue un problème à la fois mineur et bien maîtrisé. Il est aujourd'hui relativement simple d'éviter la plupart des pannes matérielles ponctuelles, ou d'en limiter l'impact. Par exemple, les rack peuvent disposer de deux alimentations et d'unités d'alimentation associées. Nombre de composants sont par ailleurs échangeables à chaud. Les pertes et les pannes matérielles à grande échelle sont naturellement possibles, par exemple, dans le cas d'une catastrophe naturelle. Cependant, notre expérience et les rapports d'analyses publié a posteriori sur les problèmes par d'autres fournisseurs de cloud indiquent que les cas de panne ou de perte de tout un data center sont extrêmement rares par rapport aux autres causes d'indisponibilité. Les pannes matérielles à grande échelle doivent quand même être prises en compte (par exemple, avec un mécanisme de récupération après sinistre), mais elles ne représentent pas le principal problème pour la disponibilité.
Voici les causes majeures d'absence sur les systèmes à l'échelle du cloud :
- Bugs logiciels
- Erreur de configuration
- Erreurs des opérateurs
Remarque : l'étape principale du secteur est que ces trois types d'erreur humaine représentent de loin les principales causes d'indisponibilité. Si leur fréquence peut être diminuée grâce à des outils, à l'automatisation et à des formations, elles ne peuvent pas être éliminées. Par conséquent, elles doivent être considérées comme un défi majeur pour l'architecture, la conception et la mise en œuvre du système.
- Variation inacceptable des performances (latence ou débit), quelle qu'elle soit, par exemple :
- "Voisins bruyants" multi-locataires (défaillance des mécanismes QoS)
- Incapacité à rejeter efficacement les surcharges (accidentelles ou malveillantes) et à continuer à effectuer des tâches utiles
- Ecroulement, pics de messages, pics de nouvelles tentatives et autres interactions « émergentes » très coûteuses en ressources
- Choc à froid (caches vides) après un redémarrage, notamment dans le cas d'un redémarrage simultané de plusieurs systèmes
- Frais généraux lors de la mise à l'échelle du système (par exemple, resharding)
- Echec de la limitation du « rayon d'impact » (nombre de clients et de systèmes affectés) des différents problèmes ci-avant
Ces problèmes sont universels. Ils font partie intégrante des « lois de la physique » qui s'appliquent aux systèmes distribués à l'échelle du cloud.
Pour répondre aux problèmes de chacune des catégories précédentes, nous mettons en oeuvre des stratégies d'ingénierie ayant déjà fait leurs preuves. Voici les stratégies les plus importantes :
- Principes d'architecture et de conception de système
- Nouveaux concepts d'architecture (qui résultent généralement de l'application des principes)
- Procédures d'ingénierie appliquées aux services
Principes d'architecture et de conception de système
Plusieurs de ces principes existent, mais nous allons nous concentrer sur les plus pertinents pour la résilience et la disponibilité.
Calcul orienté récupération
Pour gérer les bugs logiciels et les erreurs des opérateurs dont l'impact est relativement localisé, nous appliquons les principes de l'informatique orientée récupération1. À un niveau élevé, cela signifie qu'au lieu d'essayer de garantir que nous n'avons jamais de problème (ce qui est impossible à tester), nous nous nous concentrons sur le traitement de tous les problèmes de manière discrète, d'une manière qui peut être testée. En particulier, nous mettons l'accent sur la réduction de la durée moyenne de récupération (MTTR), qui combine la durée moyenne de détection, de diagnostic et d'atténuation.
Notre objectif est de procéder à une récupération si rapide que le problème ne pose pas de problème aux utilisateurs. Les points suivants nous permettent d'atteindre cet objectif :
- Détecter rapidement et automatiquement les symptômes des bugs et des erreurs des opérateurs, à l'aide d'assertions généralisées dans le code, et à la surveillance et aux alertes actives à tous les niveaux.
- Regrouper les fonctionnalités en de nombreuses unités d'isolation détaillées distinctes (threads, processus, fibres, machines à états, etc.) présentant un couplage léger : pas de partage direct de la mémoire susceptible d'être altérée.
- En cas de détection des symptômes d'un bug ou d'une erreur d'un opérateur, redémarrage automatique et rapide de l'unité d'isolation concernée. Le redémarrage est un moyen pratique de tenter une récupération suite à une défaillance arbitraire, car il tente de rétablir un état testé et restaure les invariants.
- Si la récupération au niveau d'isolation le plus détaillé ne fonctionne pas (par exemple, la fréquence de déclenchement des applications à ce niveau est toujours trop élevée et provoque des pannes en boucle), passez à l'unité supérieure suivante (processus, exécution, hôte, centre de données logique, pagination d'un opérateur).
- Création de mécanismes autorisant une « annulation à l'échelle du système », dont la gestion des versions de l'état persistant et de la configuration, afin d'identifier et d'annuler rapidement les validations erronées.
Limiter les effets des problèmes
Pour faire face aux bugs et aux erreurs susceptibles d'avoir des effets plus larges, nous créons des mécanismes pour limiter le « rayon d'impact ». C'est-à-dire que nous mettons l'accent sur la réduction du nombre de clients, de systèmes ou de ressources affectés par tous les problèmes, y compris ceux, particulièrement délicats, des « voisins bruyants », de la surcharge, de la diminution de la capacité et de le trashing des systèmes distribués. Pour ce faire, nous avons recours à divers périmètres isolés et pratiques de gestion des modifications (voir sections suivantes).
Concepts architecturaux résultant des principes de conception
Plusieurs de ces concepts existent, mais nous allons nous concentrer sur des concepts pour limiter le rayon d'impact.
Concepts de positionnement englobés dans notre API publique : Régions, domaines de disponibilité et domaines de pannes
Les domaines de pannes étant relativement nouveaux, nous allons les décrire plus en détail.
Les domaines de pannes servent à limiter le rayon d'expiration des problèmes susceptibles de survenir lors de la modification active d'un système, par exemple : déploiement, application de patches, redémarrage d'hyperviseur et maintenance physique.
L'utilisateur a l'assurance que, dans un domaine de disponibilité donné, les ressources d'un seul et même domaine de pannes sont en cours de modification à tout moment. Si quelque chose ne fonctionne pas correctement dans le processus de modification, il est possible que tout ou partie des ressources du domaine de pannes soient disponibles pendant un moment. En revanche, les autres domaines de pannes du domaine de disponibilité ne sont pas affectés. Chaque domaine de disponibilité contient au moins trois domaines de pannes. Cela permet d'héberger des systèmes de réplication basés sur un quorum (comme Oracle Data Guard) à haute disponibilité au sein d'un seul et même domaine de disponibilité.
Ainsi, pour une catégorie majeure de problèmes de disponibilité, tels que les bugs, les erreurs de configuration, les opérateurs et les problèmes de performances survenus au cours d'une procédure de modification, chaque domaine de pannes fait office de centre de données logique distinct dans le domaine de disponibilité.
Les domaines de pannes offrent également une protection contre certains types de panne matérielle localisée. Les propriétés des domaines de pannes garantissent que les ressources qui y sont placées ne partagent aucun point potentiel de panne matérielle au sein du domaine de disponibilité, dans la mesure du possible. Par exemple, les ressources de domaines de pannes différents ne partagent pas le même commutateur réseau en haut de rack. La redondance est en effet limitée à la conception standard de ce type de commutateur.
Toutefois, la protection assurée par les domaines de pannes contre les problèmes liés au matériel ou à l'environnement physique s'exerce seulement au niveau local. Contrairement aux domaines de disponibilité et aux régions, les domaines de pannes ne permettent pas d'isolement physique à grande échelle de l'infrastructure. Dans le rare cas d'une catastrophe naturelle ou d'une panne d'infrastructure concernant tout le domaine de disponibilité, les ressources des différents domaines de pannes seraient probablement affectées en même temps.
Nos services internes utilisent les domaines de pannes de la même façon que les clients doivent les utiliser. Par exemple, les services Block Volumes, Object Storage et File Storage stockent des répliques des données dans trois domaines de pannes distincts. Tous les composants de l'ensemble des plans de contrôle et de données sont hébergés dans les trois domaines de pannes (ou, dans plusieurs domaines de disponibilité, dans le cas d'une région à plusieurs domaines de disponibilité).
Cellules de service
Les cellules de service servent à limiter le rayon d'expiration des problèmes susceptibles de survenir même en l'absence de modification active d'un système. Ces problèmes peuvent survenir car la charge de travail d'un système cloud multi-locataire peut changer de manière extrême à tout moment, et parce que des pannes partielles complexes peuvent survenir à tout moment dans n'importe quel système distribué volumineux. Ces scénarios peuvent occasionner des bugs cachés ou l'apparition de problèmes de performances.
En outre, les cellules de service limitent également le rayon d'impact dans quelques rares scénarios complexes impliquant une modification active d'un système. Citons un exemple classique : le déploiement vers un domaine de pannes donné aboutit (pas d'erreur ou de modification des performances), mais dès la mise à jour du deuxième ou du dernier domaine de pannes, les interactions au sein du système (à l'échelle de l'ensemble du cloud avec une charge de travail de production) nuisent aux performances.
L'utilisation des cellules de service est un modèle architectural, et non un concept explicitement nommé dans le SDK ou l'API Oracle Cloud. Tous les systèmes multi-utilisateurs peuvent recourir à ce modèle architectural. Il ne requiert pas de prise en charge spécifique par la plate-forme cloud.
Les cellules de service fonctionnent comme suit :
- Chaque instance du service (par exemple, dans une région particulière ou dans un domaine de disponibilité donné pour les services locaux de domaine de disponibilité) consiste en plusieurs déploiements distincts de la pile logicielle correspondante. Chaque déploiement est appelé cellule. Chaque cellule est hébergée sur sa propre infrastructure, dans la mesure du possible. Au minimum, les cellules ne partagent ni hôte ni machine virtuelle.
- Un service peut démarrer avec une poignée de cellules dans chaque région ou domaine de disponibilité. Au cours de l'évolution du service et en fonction de la demande croissante, des cellules sont ajoutées pour limiter le rayon d'impact des éventuels problèmes. Un service d'ensemble très utilisé peut comporter de nombreuses cellules. En d'autres termes, les cellules effectuent un multiplexage n à m des workloads client vers des environnements d'hébergement distincts, qui constituent des îlots d'isolation des ressources. Les cellules n'ont pas de cardinalité claire, telle qu'elle existe pour les domaines de pannes. (tel que mentionné précédemment, le choix évident pour les domaines de pannes est une cardinalité de trois par domaine de disponibilité, ce qui permet aux systèmes de réplication basés sur un quorum d'être hébergés dans un seul et même domaine de disponibilité.)
- Chaque « unité naturelle » de charge globale client est affectée à une cellule particulière. La notion d'« unité naturelle » dépend de la nature du service considéré. Par exemple, pour notre service de workflow partagé interne (voir plus loin), l'unité naturelle peut désigner tous les workflows du domaine de disponibilité ou de la région pour un plan de contrôle donné.
- Avant chaque groupe de cellules se trouve une couche de routage minimaliste ou une API de repérage d'adresses de cellule. Par exemple, le système de Streaming/Messaging comporte une API destinée à repérer l'adresse de plan de données en cours d'une rubrique particulière. La banque de métadonnées interne dispose par cellule d'une adresse. Toutefois, d'autres services basés sur les cellules comportent une adresse de plan de données unique et une couche de routage partagée. La couche de routage est une cause potentielle de défaillance corrélée des cellules. Pour atténuer ce risque, l'accent est mis sur sa simplicité, sa prévisibilité et ses performances (pas d'opération coûteuse en ressources). De plus, elle est provisionnée avec une capacité de réserve importante et des mécanismes de quota et de limitation QoS sophistiqués.
- Si nécessaire, les propriétaires de service peuvent déplacer une charge de travail d'une cellule à une autre à partir d'une autre. Exemples de scénarios :
- Eviter le problème des « noisy neighbor » de plusieurs clients en déplaçant une charge de travail lourde de sorte que les autres utilisateurs d'une cellule ne soient pas affectés.
- Facilitez la reprise après une surcharge ou une baisse d'efficacité, par exemple en raison d'une attaque par déni de service distribué. Les mécanismes de ralentissement et de quota en place permettent de se défendre contre ce type d'attaque. Cependant, parfois, des cas d'utilisation en périphérie se produisent, dans lesquels un cas d'utilisation particulier (API, modèle d'accès) peut se révéler plus problématique pour le service que ne le perçoivent les quotas ou les ralentissements. Les cellules offrent un mécanisme d'atténuation à court terme.
- Répartir les charges de travail cruciales dans différentes cellules, pour nettement réduire le risque de défaillance corrélée Par exemple, pour notre workflow partagé interne relatif aux plans de contrôle, les plans de contrôle de « cœur critique » (par exemple, Platform, Compute, Networking et Block Volumes) sont affectés à des cellules distinctes et présentent ainsi une corrélation de défaillance nettement moins importante que si des cellules n'étaient pas utilisées ou qu'elles étaient affectées à la même cellule.
Remarque : grâce aux cellules, les clients ont moins besoin de tenir compte des dépendances internes des services pour créer des applications résilientes. L'examen du graphe des dépendances demeure une bonne pratique (lire la suite pour plus de détails), mais il se révèle moins nécessaire quand un mécanisme de décorrélation est en place.
Chaque cellule de service constitue donc encore un autre type de « centre de données logique » (groupe logique d'isolation des pannes et d'isolation des performances) au sein d'un domaine de disponibilité ou d'une région unique.
En résumé, les cellules de service et les domaines de pannes sont complémentaires des façons suivantes :
- Les domaines de pannes offrent une protection contre les problèmes susceptibles de survenir en cas de modification active d'un système.
- Les cellules de service limitent le rayon d'impact en cas de problèmes potentiellement graves, que le système fasse l'objet d'une modification active ou pas.
Nous combinons les propriétés des domaines de pannes et des cellules de service en une stratégie unifiée lors des déploiements et de l'application de patches.
Procédures d'ingénierie appliquées aux services
Les tests et l'excellence opérationnelle étant essentiels pour la fiabilité des systèmes cloud, nous suivons de nombreuses procédures d'ingénierie. Vous trouverez ci-dessous certaines de nos procédures les plus importantes, qui utilisent les concepts mentionnés dans la précédente section :
- Nous déployons les services de façon incrémentielle, effectuons une validation soigneuse entre les étapes et procédons à une annulation automatique si un événement surprenant survient. Concrètement, nous appliquons le processus suivant :
- Dans chaque domaine de disponibilité, nous procédons au déploiement vers une seule cellule de service à la fois. Dans chaque cellule, nous déployons vers un domaine de pannes à la fois, jusqu'à les avoir tous traités. Ensuite, nous passons à la cellule suivante du domaine de disponibilité.
- Après chaque étape du déploiement (après chaque domaine de pannes et cellule), nous vérifions que la modification fonctionne comme prévu : pas de performances moindres ni d'erreurs internes ou externes. Si un événement inattendu ou une erreur se produit, nous annulons automatiquement la modification. Nous accordons beaucoup d'importance à la préparation et aux tests automatisés des procédures d'annulation, y compris les modifications affectant l'état persistant ou les schémas.
- Nous déployons ainsi la modification domaine de disponibilité par domaine de disponibilité dans chaque région. Le déploiement dans l'ensemble des régions d'un domaine est effectué de manière à ne pas modifier simultanément deux régions susceptibles d'être utilisées par un client comme sites principal et de récupération après sinistre.
- Nous vérifions régulièrement que les mécanismes de gestion des erreurs et autres systèmes d'atténuation fonctionnent comme prévu et n'améliorent pas le problème. Sans de tels tests, il arrive couramment que les mécanismes de gestion des erreurs (comme les nouvelles tentatives, les algorithmes de récupération après défaillance et les algorithmes de reconfiguration de machine à l'état) comportent des bugs, soient trop chers ou interagissent de manière surprenante, et engendrent donc un écroulement ou autres graves problèmes de performances.
- Nous vérifions notre capacité à revenir rapidement et en toute sécurité aux derniers logiciels et configuration corrects, y compris l'état persistant et le schéma, comme décrit précédemment.