Sekoia.io traite près d’un milliard d’événements clients par jour. Cela représente des dizaines de milliers d’entrées de registre par seconde. Chaque événement doit être analysé rapidement et de manière fiable par notre pipeline de détection pour déceler les cybermenaces et réagir le plus rapidement possible. Vous pouvez imaginer qu’il est inenvisageable de rompre ce pipeline, ne serait-ce qu’un instant.
Déployer en toute sécurité les modifications dans ce workflow à haut débit et à faible latence est un défi majeur que nous essayons continuellement de relever.
Bien entendu, nous utilisons l’architecture des microservices : la production est assurée par un grand nombre de composants faiblement couplés. Cette approche nous permet de faire évoluer et mettre à jour facilement et indépendamment des parties de notre infrastructure. Cela implique également que nous devons fréquemment gérer un grand nombre de déploiements.
Tous nos microservices fonctionnent sous Kubernetes, idéal pour la réparation automatique et les déploiements progressifs. Kubernetes est intelligent, mais pas tant que ça. Même combiné avec tous les CI/ CD et mise en scènes du monde, on pourrait quand même déployer des services cassés. Cela a de multiples implications :
- Les déploiements doivent être amorcés et supervisés par une personne physique (une équipe d’ingénierie de fiabilité du site dans notre cas) pour en assurer le bon déroulement.
- Les développeurs doivent compter sur l’équipe SRE pour déployer leur code.
Ces deux problèmes nuisent à la productivité et à la proactivité et peuvent créer du stress chez les responsables des déploiements.
Même avec ces dispositions, nous avons parfois rencontré des problèmes lors du déploiement de mises à jour : des services qui fonctionnaient dans l’environnement de test ont été interrompus en production lorsqu’ils ont été mis sous pression, ou n’ont tout simplement pas fonctionné en raison d’une configuration asymétrique.
Comment éviter cela ? Nous avions besoin d’un moyen de déployer en toute sécurité les mises à jour des microservices, sans avoir à craindre que cela ne déclenche un incident.
Heureusement, l’automatisation existe et peut être appliquée à presque tout.
Déploiements Canaris ?
Les canaris étaient utilisés dans les mines de charbon pour alerter les travailleurs des fuites de monoxyde de carbone, le « tueur silencieux ». Comme les canaris mouraient du monoxyde de carbone bien avant les travailleurs des mines, ils étaient, à l’époque, le seul moyen de détecter une fuite de gaz.
Aujourd’hui, le terme est utilisé pour décrire tout sujet de test.
Dans le domaine des opérations, les déploiements Canaris correspondent à cela : en déployant volontairement la dernière version d’un service à un nombre restreint d’utilisateurs, vous êtes en mesure de voir comment la nouvelle version fonctionne sans tout compromettre en cas de bug. Ainsi, si cette version fonctionne correctement, vous pouvez continuer à la déployer progressivement auprès d’un plus grand nombre d’utilisateurs. Si elle continue à bien fonctionner, vous avez de bonnes raisons de penser qu’elle peut remplacer l’ancienne version.
Comme Kubernetes fournit une API pour intéragir avec les déploiements, il est bien adapté à l’automatisation de ce type d’opérations.
Solutions existantes
Comme on peut s’y attendre, il existe déjà un grand nombre de solutions différentes pour tenter de résoudre ce problème. Cependant, nous avions de strictes exigences avant d’adopter une quelconque solution :
- Nous ne voulions pas avoir à réécrire nos piles de Kubernetes, car nous avons environs 80 micro-services.
- Nous ne voulions pas installer de lourdes dépendances dans le cluster Kubernetes, car nos environnements de production et CI/ CD sont déjà suffisamment complexes.
- La solution doit fonctionner non seulement pour les travailleurs basés sur HTTP mais également pour les services basés sur Kafka. Nous n’avons pas besoin d’équilibrage du trafic HTTP, d’épinglage de session et de toutes ces choses fantaisistes.
- Les déploiements canaris ne doivent avoir lieu que lors du déploiement de nouvelles images Docker, sans modifier les exigences de RAM/CPU ou les métadonnées.
En prenant en comptes toutes ces exigences, nous avons commencé à chercher des solutions, sans grand succès. Des solutions tel que Gloo ou Flagger sont très largement basées sur HTTP, et Argo est un écosystème entier, qui nécessite des changements dans les piles de déploiement.
Nous n’avons pas pu trouver de solution simple pour les travailleurs basés sur Kafka. Après quelques jours de lecture de la documentation et d’essais sur notre environnement de test, il est devenu évident qu’aucune des solutions existantes ne répondait à nos besoins.
Aviary ?
Comme tout ingénieur frustré le ferait, nous avons décidé de créer notre propre solution, baptisée Aviary (parce qu’elle manipule des canaris toute la journée !).
Ce que nous voulions réaliser pouvait simplement être mis en œuvre de cette manière :
- A l’initialisation, Aviary duplique le déploiement du service en ajoutant le suffixe
-primary
à son nom et met à l’échelle le déploiement initial à 0. - Ensuite, elle surveille les modifications apportées au déploiement initial.
- Lorsqu’une modification est détectée, Aviary l’enregistre et produit une altervative entre l’ancien et le nouveau déploiement.
- L’alternative est traitée pour décider si les modifications doivent être directement ou progressivement déployées. nous ne voulons pas étaler le déploiement sur plusieurs heures si nous mettons simplement à jour les critères CPU, par exemple.
- En cas de déploiement direct, le déploiement
-primary
est instantanément remplacé par une nouvelle version et s’arrête là. Dans le cas ou c’est un déploiement progressif, une copie du nouveau déploiement est créée avec le suffixe :-canary
- Le déploiement
-canary
passe progressivement à l’échelle, tandis que le-primary
est proportionnellement réduit. À chaque étape, les métriques clés sont extraites de Prometheus et Kubernetes pour décider si la nouvelle version fonctionne correctement.
Deux résultats possibles sont à prévoir :
- Si nous atteignons un point de rupture où suffisamment d’instances
-canary
ont été efficaces sur une période de temps, le déploiement-canary
est promu-primary
, et le déploiement initial reste inchangé. - Si un certain nombre d’instances échouent ou si les métrics ne sont pas idéales, le déploiement
-canary
est abandonné et le déploiement initial est restauré à ce qu’il était avant les changements.
Cette logique nous permet de mettre en place des déploiements canaris progressifs et une réduction automatique de la production, tout en n’apportant absolument aucun changement à notre code base existant. Notre pipeline de CI/CD reste également intact, car il interagit avec le même objet qu’avant, le déploiement initial. Aviary s’occupe du reste.
La configuration reste minimale, avec la possiblité de définir, par service :
- Le pourcentage de points de rupture (après lequel le pourcentage d’instances de canaris réussies nous permet de déployer complètement la nouvelle version).
- Combien d’instances de canaris doivent être ajoutées à chaque itération du déploiement ?
- Le nombre maximum d’échecs tolérés en termes de métrics.
- Un délai de mise en route.
- Une liste des métrics et de seuils pour valider les nouveaux déploiements
Il est également très interactif et permet à un opérateur d’annuler un déploiement en cours, ainsi que de se contourner pour le prochain déploiement d’un service (pour le push de hotfixes).
Conclusion
Notre solution est en production depuis quelques mois maintenant, et elle a déjà permis d’éviter de nombreux mauvais déploiements et régressions. L’absence de changement du code de base et sa très faible empreinte dans le cluster Kubernetes en font une solution très satisfaisante.
Elle ne change absolument rien aux opérations normales de SRE et à l’automatisation car elle gère de manière transparente les déploiements à grande échelle et les redémarrages des services.
Nous sommes très heureux d’avoir fait ce pas en avant, car les développeurs ont fait un pas de plus vers la possibilité de déployer eux-mêmes leurs changements, sans avoir à se soucier des problèmes non détectés qui surviendraient en production.
Bien entendu, Aviary a été réalisée de la manière la plus agnostique possible pour les entreprises et est disponible gratuitement sur notre dépôt GitHub.
À lire aussi sur notre blog :
- Hatching Triage pour améliorer SEKOIA.IO Cyber Threat Intelligence (CTI)
- Une guerre sur plusieurs fronts – le paysage turbulent de la cybercriminalité
- XDR n’est pas un EDR++
- Amélioration de la détection des menaces avec Sigma correlation
- Les nouvelles fonctionnalités de SEKOIA.IO du mois de juillet
Échangez avec l’équipe
Vous souhaitez en savoir plus sur nos solutions de protection ? Vous voulez découvrir nos produits de XDR et de CTI ? Vous avez un projet de cybersécurité dans votre organisation ? Prenez rendez-vous et rencontrons-nous !