Assurer la résilience et la scalabilité d’une application

Résilience et scalabilité d'une application

Assurer la résilience et la scalabilité d’une application

Les pipelines CI/CD sont aujourd’hui au cœur de l’activité quotidienne d’un développeur. À chaque modification de code au sein d’un dépôt Git, des pipelines sont lancées. Analyses de code, lancement des tests, recherche de failles de sécurité dans le code et ses dépendances et construction d’une image exécutable sont autant de tâches automatisées qui aiguillent le développeur et assurent une meilleure efficacité lors des phases de développement, de test et de livraison.

Cependant, ces pipelines sont sensibles au matériel et au réseau sous-jacent. En effet, il n’est pas impossible qu’un centre informatique soit rendu indisponible pendant plusieurs heures, voire plusieurs jours. De plus, avec l’approche de fin de sprint (méthodologie Scrum), le nombre de modifications est plus élevé et il devient important de pouvoir gérer cette charge de travail supplémentaire.

Or, un seul point de défaillance peut se révéler fatal dans ces cas et entraîner de gros retards sur les projets (avec notamment des conséquences financières).

Ainsi, au-delà de l’aspect critique des pipelines CI/CD, Fabien, accompagné de son co-stagiaire Yann, a réalisé une preuve de concept d’un cloud hybride permettant d’assurer la meilleure scalabilité et la meilleure résilience possible d’une instance Jenkins, l’outil qui constitue le cœur du projet. Fabien nous raconte.

 

AViSTO propose chaque année des stages en Cloud / DevOps : retrouvez les offres sur notre site emploi.

Définitions :

Pour commencer, rappelons quelques définitions :

  • CI / CD : « En génie logiciel, CI/CD est la combinaison des pratiques d’intégration continue et de livraison continue ou de déploiement continu. Le CI/CD comble le fossé entre les activités et les équipes de développement et d’exploitation en imposant l’automatisation de la création, des tests et du déploiement des applications. Les pratiques DevOps modernes impliquent le développement continu, le test continu, l’intégration continue, le déploiement continu et la surveillance continue des applications logicielles tout au long de leur cycle de vie. La pratique CI/CD, ou pipeline CI/CD, constitue l’épine dorsale des opérations DevOps modernes. » (Wikipedia)
  • Jenkins : « Jenkins est un outil open source de serveur d’automatisation. Il aide à automatiser les parties du développement logiciel liées au build, aux tests et au déploiement, et facilite l’intégration continue et la livraison continue. » (Wikipedia)
  • Cloud hybride : « Un cloud hybride est un modèle de cloud computing qui réunit au moins un cloud privé et au moins un cloud public, qui fonctionnent ensemble pour fournir une combinaison flexible de services de cloud computing. » (VMware)

Jenkins

Afin de travailler sur la scalabilité et la résilience de Jenkins, nous nous sommes intéressés à sa configuration. Pour cela, nous avons choisi d’utiliser le plugin Jenkins Configuration As Code. Comme son nom l’indique, ce plugin permet d’entièrement configurer l’instance Jenkins grâce à du code stocké dans un unique fichier yaml.

Nous pouvons ainsi paramétrer à l’avance tous les comptes de la plateforme, mais aussi les plugins installés. Ce dernier point est très important car nous voulons pouvoir automatiser le déploiement d’une instance Jenkins entièrement opérationnelle, qui a donc toute la configuration pour déployer des VMs / conteneurs sur le Cloud grâce au plugin Azure VM Agents, et ce, dès son démarrage.

Une configuration qui est autrement très lourde à rentrer manuellement. Grâce à Jenkins Configuration As Code, tout sera donc déjà paramétré avant même son démarrage. Nous utilisions alors une image docker Jenkins personnalisée avec les plugins nécessaires, contenant entre autres ce dernier.

Livre blanc feuille de route DevSecOps

Scalabilité de l’application

L’un des principaux objectifs de notre preuve de concept était d’utiliser les ressources du Cloud afin de gagner en flexibilité et d’éviter des soucis de surcharge sur nos propres serveurs. Ceci a été rendu possible grâce au plugin Jenkins Azure VM Agents. En effet, celui-ci agit comme une passerelle entre Azure et Jenkins afin de pouvoir automatiquement y déployer des machines virtuelles ou des conteneurs.

Afin d’avoir accès aux ressources Azure où nous devons avoir des droits pour déployer des ressources à la volée, nous devons créer un Service Principal, comme cela est le cas pour la gestion des secrets avec Mozilla SOPS.

Cette identité est ensuite utilisée par le contrôleur Jenkins afin d’avoir les permissions de création dans un groupe de ressources Azure défini.

Lors de la configuration, de nombreux champs doivent être remplis afin que la liaison se fasse correctement. Par exemple, il est nécessaire de renseigner des détails sur le cluster Kubernetes Azure, les identifiants pour s’y connecter, l’URL du serveur, l’URL de l’instance Jenkins, toute la configuration du conteneur déployé sur l’AKS (Azure Kubernetes Service), les paramètres TLS, etc. Ici encore, Jenkins Configuration As Code nous permettait de rédiger toute cette partie directement dans le code de configuration afin que l’instance Jenkins lancée puisse, nativement, avoir en main toutes les cartes nécessaires au déploiement de jobs dans le cloud.

Mise en place de la résilience

Obtenir un contrôleur Jenkins résilient implique de mettre en place un ensemble de solutions complémentaires permettant :

  • La gestion des sauvegardes pour l’historique des jobs qui tournaient sur cet outil, ainsi que la sauvegarde de sa propre configuration.
  • La délégation de jobs via le Cloud.
  • Une bonne accessibilité.
  • La gestion des secrets.

C’est ce que nous allons voir maintenant.

Sauvegarde

Une des exigences de notre cahier des charges était la conservation de l’historique des jobs (heure de lancement, résultat, log, …) réalisés sur le contrôleur Jenkins, en plus des jobs eux-mêmes.

Les jobs et leur historique étant stockés dans le même dossier, cela rendait leur sauvegarde plus simple. Nous avons donc mis en place un système qui s’occupe de sauvegarder ce dossier, et un autre qui s’occupe d’utiliser cette sauvegarde au démarrage du contrôleur Jenkins afin que celui-ci possède tout pour être opérationnel dans la minute de son démarrage.

Pour effectuer un back-up, la solution que nous avons mise en place est un CronJob en tant que ressource Kubernetes dans le Cluster. Sa fréquence d’exécution peut être facilement modulée en fonction de ce qui est souhaité.

Dans notre cas, toutes les 5 minutes ce CronJob va pousser dans un Azure Blob Storage tout le dossier jobs de Jenkins. Ce service d’Azure permet de stocker tout type de données dans un même endroit (binaires, vidéos, etc). Nous n’avons pas besoin d’intégrer tout le dossier de Jenkins car comme expliqué précédemment, nous avons déjà toute sa configuration sauvegardée via Helm et Git.

Afin de restaurer la sauvegarde la plus récente qui a été réalisée et stockée dans le Cloud, nous utilisons un job Jenkins ! Celui-ci est présent par défaut dans l’image Jenkins et est paramétré pour s’exécuter toutes les secondes et se supprime automatiquement à la fin de son exécution.

Grâce à cette astuce, dès que le contrôleur Jenkins est opérationnel, le job de restauration va s’exécuter, récupérer la sauvegarde et l’intégrer au système de fichiers Jenkins avant de s’auto détruire. Encore une fois, cet accès est permis par une identité dédiée.

Fédération de Clusters : KubeFed

Ce que nous avons présenté jusqu’à présent mettait en avant la résilience du contrôleur Jenkins. Néanmoins, il est nécessaire de mettre en place un autre système afin d’automatiser son redéploiement en cas de problème critique sur le Cloud privé de l’entreprise.

Dans cette situation, nous souhaitons identifier que le serveur ne répond plus afin de redéployer nos ressources dans le Cloud. Ainsi, nous aurons toutes les pièces du puzzle pour répondre aux objectifs. Pour cela, nous allons utiliser KubeFed (Kubernetes Cluster Federation).

KubeFed permet de lier plusieurs clusters Kubernetes et d’y répartir des ressources. Lui-même sous la forme de ressources Kubernetes, il est simple d’intégration. Néanmoins, il nécessite des ressources Kubernetes avec des syntaxes spécifiques autres que celles que nous avions avec le Helm Chart Jenkins. Grâce à des politiques de fédération, il est possible de définir un ensemble de règles de sorte à fixer une priorité sur un des deux clusters. Dans notre cas, nous désirions avoir en temps normal l’intégralité de nos ressources déployées sur le cluster privé, et utiliser le cluster public uniquement dans le cas où le privé serait non opérationnel. Grâce à cette configuration, nous avons un redéploiement automatique de nos ressources sur le Cloud privé dès que le cluster principal hébergé par nos soins rencontre des difficultés. Mieux encore, une fois que nos serveurs redeviennent opérationnels, ceci va être détecté par KubeFed qui va rebasculer nos ressources sur ces derniers avec le minimum de temps d’arrêt. L’objectif de maximiser l’utilisation de nos ressources et de nous servir du Cloud uniquement le temps nécessaire est donc atteint.

Accessibilité : Azure Front Door

Grâce à l’architecture mise en place, notre contrôleur Jenkins est désormais opérationnel en permanence. Néanmoins, d’un point de vue utilisateur nous n’avons pas une navigation aisée sur l’instance Jenkins. En effet, en fonction du lieu de déploiement (cluster privé ou cluster publique) son adresse IP change. C’est pour cette raison que nous avons mis en place un service assurant l’accessibilité de notre Jenkins quelle que soit la localisation de son déploiement.

Comme son nom l’indique, Azure Front Door agit comme une porte d’entrée. Nous l’avons configuré avec une URL précise et il va s’occuper de router vers l’adresse IP du contrôleur Jenkins qui est opérationnel. Nous pouvons retrouver de nombreuses similarités avec la technologie Heartbeat.

Secrets

Un des aspects les plus sensibles dans ce genre de projets est la gestion des secrets. Dans notre cas, nous avons utilisé pour cela Mozilla SOPS qui permet de chiffrer tout un fichier sensible à l’aide d’une clé que nous stockions sur Azure Keyvault. L’utilisation de RBAC permet de contrôler de manière sécurisée les accès à cette clé.

Conclusion

Ainsi, la solution proposée et mise en place lors de cette preuve de concept a répondu en tout point au cahier des charges qui nous avait été fixé.

Composée d’un cluster public et d’un cluster privé reliés par KubeFed, cette instance Jenkins a été rendue entièrement résiliente et scalable grâce à de nombreuses technologies. Nous avons tiré profit au maximum des avantages que le Cloud pouvait nous offrir, en termes de flexibilité mais aussi de services.

Ce stage chez AViSTO a été extrêmement enrichissant, tant sur les plans technique qu’humain.

Les apprentissages ont été nombreux. Le fait de travailler sur autant de technologies différentes me tenait particulièrement à cœur étant donnée la stack technologique immense que l’on est amené à utiliser dans le monde du DevOps et du Cloud.

C’est donc avec joie que je continue mon expérience chez AViSTO en tant qu’ingénieur Cloud & DevOps.

 

Envie de rejoindre AViSTO pour en stage en Cloud / DevOps ? Retrouvez les offres sur notre site emploi.