Chez Padok, on utilise le framework ROSE qui contient 100 critères qui caractérisent une bonne infrastructure. Ce qui nous permet de minimiser les risques d’incidents sur nos infrastructures. Cependant, on est tout de même confrontés régulièrement à des incidents en production avec l’équipe d’infogérence. Quand ça se produit, on doit prêt et entraîné pour gérer cette urgence.
Durant ces situations, il y a deux enjeux clés, mitiger puis résoudre le problème. La mitigation est utile pour minimiser l’impact sur les utilisateurs, éviter que le problème ne s’étende et qu’il n’y ait pas de répercussions plus importantes. Une fois le problème mitigé, on peut approfondir les recherches pour apporter une solution durable.
Assez souvent on passe directement à la résolution du problème sans passer par la mitigation. Mais trop souvent le problème n’est pas résolu efficacement. C’est pour limiter l’impact de cette lente résolution qu’il est important de mitiger vos problèmes.
Ce concept de mitigation est évoqué dans des standards de gestion de crise comme celui des pompiers. Comme précisé dans l’article incident managment du book SRE de Google, ils se sont inspirés du standard ICS des pompiers de Californie pour construire leur standard de gestion de crise.
Cela peut sembler contre-intuitif, mais parfois les pompiers brûlent d’eux-mêmes les bordures de champs ou une partie de la forêt d’une manière contrôlée. Brûler volontairement dans un but préventif permet d’endiguer la propagation du feu. C’est cette méthode que j’essaye d’appliquer dans le contexte du cloud.
Dans le cloud, cette pratique est bien trop souvent négligée. Des incidents durent souvent plus longtemps que nécessaire. Les SRE cherchent avant tout à trouver puis à corriger un bug, alors qu’ils pourraient dans premier temps se concentrer sur la mitigation pour limiter l’impacte du bug. Et donc réduire la nuisance occasionnée.
En effet, il est parfois plus facile d’empêcher l’accès aux services pour pouvoir trouver la source d’un problème. C’est le cas, avec les pics de charge sur une application qui ne marche pas par exemple.
De plus, si on peut se soustraire à l’impact néfaste d’un trafic important, tout en avertissant les utilisateurs que le service qu’il cherche à joindre n’est pas disponible, c’est encore mieux. En effet, vous allez donner une visibilité à la situation et éviter aux utilisateurs de réessayer d’accéder en vain à votre service, ce qui nuit à votre image de marque.
La meilleure manière d’atteindre les deux objectifs cités précédemment est d’avoir préparé en amont une belle page de maintenance et de l’afficher aux utilisateurs. Une simple page statique qui avertit vos utilisateurs qu’une maintenance est en cours calmera toutes leurs ardeurs à rafraîchir leur page lorsqu’ils ont des erreurs.
Selon moi c'est la meilleure solution qui peut être facilement mise en place avec un service tel que Cloudfront. En une redirection DNS plus tard, ou en une modification d’un ingress dans kubernetes, vous évitez que vos utilisateurs fassent exploser votre service déjà KO.
Ainsi, si vous êtes bien préparé, en quelques minutes vous êtes apte à vous lancer dans le debug du véritable problème, sans la pression des utilisateurs.
Pour ceux qui n’ont pas de page de maintenance préparée, il est alors tout simplement possible de couper le service. En effet, que votre site timeout, renvoie des erreurs 5XX ou 404, il n’est pas accessible.
Il est donc alors possible de couper le service pour éviter que les utilisateurs n’aggravent la situation. C’est un moyen d’avoir de l’air pour pouvoir debuguer dans de meilleures conditions. Attention, il se peut aussi que votre problème ne se produise que quand votre plateforme est stressée et donc cette solution n’est pas toujours adaptée.
Dans certains cas, vous n’arriverez pas à identifier le problème sans un minimum de trafic et d’utilisateurs. Vous pouvez essayer de faire une répartition du trafic 95%-5% pour ne subir qu’une partie supportable du trafic. 5% des utilisateurs vont servir à reproduire le problème et les 95% restant peuvent être redirigés vers une page de maintenance.
La plupart de vos utilisateurs ne pourront pas atteindre l’application, mais au moins, vous soulagerez chaque composant de votre infrastructure. Ainsi, vous ne ferez plus face à une masse des utilisateurs mais vous en aurez toujours suffisamment pour reproduire les comportements problématiques.
Voici différentes manières de limiter l’impact d’un incident. Ces méthodes ne sont pas révolutionnaires, mais sont simples à mettre en place. Toute la difficulté réside dans le choix d’accepter de détourner son propre trafic et de savoir décider si une situation se prête ou non à ce genre de mitigation.
On va revenir sur un problème que j’ai rencontré en tant qu’Ops de l’équipe RUN, qui démontre l’efficacité d’une méthode de mitigation qu’on utilise.
18h05 mon téléphone sonne, c’est une alerte et je me lance sur l’identification du problème. Je préviens mon client qu’il y a des perturbations sur certaines de ces API. L’équipe en charge de ces APIs est disponible, et au bout de 10 minutes d’investigation on se rejoint dans un meet.
Le but étant de partager nos découvertes et réfléchir ensemble à la situation à laquelle ont fait face. À ce stade, je sais juste qu’un endpoint ne répond plus, on a un système de monitoring ‘blackbox’.
Comme on vient de le comprendre avec le schéma, il y a une boucle d’appels infinie entre les différentes API qui est problématique. Or mon.api est également utilisée par de nombreux autres utilisateurs qui n’utilisent ni toto.api ni tata.api.
Avec le client, on se met d’accord notre objectif est de trouver un moyen de mitiger le problème pour le week-end. Puis il le résoudra réellement dès le lundi matin.
Les contraintes à ma connaissance sont les suivantes :
Dans l’idéal je préfère ne pas toucher aux ressources actuelles, et malgré toutes ces contraintes, j’ai trouvé une solution pour mitiger ce problème. Je veux rediriger toutes les requêtes de mon.api à destination de toto.api ou de tata.api vers un service qui n’existe pas.
Ainsi je brise la chaîne infinie qui posait problème. Je suis capable de le tester super rapidement car justement je ne change aucune ressources existante. Et moins d’une minute après la validation de mon client, j’applique la configuration à l’aide d’un kubectl apply -f ingress-blabla.yaml
.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: fix-ingress
tag: to-delete
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: toto.api
http:
paths:
- backend:
service:
name: this-service-doesnt-exist
port:
number: 80
path: /certificate/metadata/toto
pathType: Prefix
- host: tata.api
http:
paths:
- backend:
service:
name: this-service-doesnt-exist
port:
number: 80
path: /certificate/metadata/tata
pathType: Prefix
Le nombre de requêtes a diminué instantanément et a mitigé l’incident. J’adore cet exemple car il illustre parfaitement comment, en cassant une partie de l’infrastructure, j’ai eu de nouveau le contrôle de la situation.
Pour résoudre un incident efficacement, il faut parfois accepter de casser temporairement une infrastructure ou de fermer un service. Ce n’est pas grave surtout si elle ne fonctionne pas. Dans l’exemple que j’ai raconté, ce qui est cool, c’est qu’il est très simple d’annuler mes changements pour revenir à une situation normale.
Cette solution est pratique pour faire un test rapide sans aucun risque. On a géré un incident qui a nécessité aux développeurs plusieurs heures de debug intense la semaine suivante en 45 minutes. On a évité à notre client de faire un travail de debug applicatif en plein week-end, sous stress.
Et enfin, on a limité l’impact pour les utilisateurs finaux. Voici un cas de figure typique, où il est important de mitiger un problème plutôt que de chercher à le résoudre. Et voici une bonne histoire où j’ai cassé mon infrastructure pour mieux la réparer.