FormationSpringBoot 6 DevOps
FormationSpringBoot 6 DevOps
FormationSpringBoot 6 DevOps
2020
Il est nécessaire d'automatiser (donc d'outiller) au maximum les différentes tâches, qui vont du développement jusqu'au
déploiement sur une plateforme.
Rappel : le plus important dans un projet, c'est le code en lui-même, pas l'outil avec lequel on l'écrit.
Cet outil se situe au coeur du processus d'intégration continue car il est utilisé à la fois par :
Il permet :
Voici un article intéressant pour une gestion efficace des branches avec Git.
Une fois le build automatisé et les sources gérées en configuration, il est possible de passer à l'étape suivante : l'intégration continue
(continuous integration).
Elle doit :
– valider la capacité à construire le livrable à tout moment, à partir des sources placées en configuration
– lancer les tests (unitaires, de performance, de sécurité, d'intégration...)
– s'exécuter après chaque modification des sources en configuration (dans l'idéal).
Des environnements de tests sont créés à la volée puis supprimés lors de ces phases.
Attention : ces outils permettent de s'assurer que le code est bien écrit (lisible, maintenable, évolutif, sans vulnérabilités
connues...), mais pas qu'il répond aux besoins. C'est le rôle des tests (unitaires, d'intégration, d'acceptance...).
Le livrable généré peut être déployé automatiquement sur un serveur d'intégration, techniquement le plus proche possible de la plateforme de
production.
Il passera également des tests d'acceptation (acceptance tests) qui consistent à vérifier :
Ces tests peuvent être manuels, mais le mieux est de les automatiser. Il serait dommage d'avoir des plateformes disponibles en quelques
minutes, mais que les tests manuels prennent 15 jours.
Une fois ces tests passés, le livrable peut être déployé sur la plateforme de production, soit :
– à chaque commit dans la gestion des sources (ou très régulièrement), la nouvelle fonctionnalité/la correction de bug se retrouve en
production en quelques minutes
– une application peut être mise en production plusieurs fois par jour
– en cas de problème, le retour en arrière est très simple.
Note : veiller à ce que l'application reste très intuitive car il n'est plus possible de former les utilisateurs à chaque mise en
production.
CaaS (Container as a Service) : le projet gère des conteneurs (Docker notamment). Il peut potentiellement déployer n'importe quelle techno,
et continue de gérer le cycle de vie (backup, versions, patchs).
PaaS (Platform as a Service) : le projet ne livre que l'application (qui doit être compatible avec les technologies supportées par le PaaS). Le
PaaS instancie rapidement et à la demande des environnements d'exécution. Il prend en charge également des problématiques telles que la
sécurité, la disponibilité, le dimmensionnement, la redondance... Les produits d'infrastructure utilisés (serveur d'applications, patchs de
sécurité, technologies supportées, services offerts...) sont gérés par le PaaS (une équipe dédiée).
FaaS (Function as a Service) : le projet livre de très petites unités exécutables. Le process démarre et s'arrête à la demande. Facturation a
l'execution time du process.
SaaS (Software as a Service) : l'utilisateur final accède (via abonnement en général) aux logiciels via le web, sans devoir les installer en local.
Il utilise donc toujours la dernière version disponible. Quand un projet consomme une API, cette API peut être vue comme un SaaS.
La configuration de l'application (environnement, services...) se fait dans un fichier manifest.yml situé à la racine du projet :
Une fois l'application construite (par l'intégration continue...), pour la déployer, le PaaS va :
Le projet a plusieurs possibilités pour déployer son application. Il peut fournir à Kermit :
– l'application a des besoins spécifiques (sur les protocoles réseaux utilisés, les middlewares, le langage...)
– l'équipe doit garder un contrôle accru sur la plateforme
– l'équipe possède une large expertise sur toute la chaîne du build au run.
– avantages :
- montée en compétences plus facile sur toute l'application
- entraide facilitée entre membres du projet
- homogénéité du code
– inconvénients :
- ce langage n'est peut-être pas le plus adéquat selon les modules
- fort couplage entre les modules
- même si les technologies évoluent sur le marché, on doit continuer à coder avec l'existant (langages ou frameworks
obsolètes) même pour les nouvelles fonctionnalités.
– avantages :
- plus simple à déployer
- pas de problème réseau entre composants
- gestion des problèmes transversaux simplifiés (configuration, logs...)
– inconvénients :
- il faut souvent mettre en place une scalabilité verticale (augmenter la puissance des machines)
- on provisionne des machines surdimensionnées dont les capacités seront réellement utiles qu'à de rares moments
- il faut toujours + de CPU, de RAM... car les applications sont de plus en plus gourmandes
- la loi de Moore tendant à arriver à ses limites, on ne peut plus trop compter sur les progrès des machines pour scaler
verticalement dans le futur
- chaque modification mineure dans l'application entraîne son redéploiement complet
- en cas de problème, toute l'application tombe.
Dans une application monolithique, toutes ces contraintes sont appliquées de fait partout. On doit faire des compromis (répondre
globalement à la plupart des exigences).
Chaque module est spécialisé dans une fonctionnalité, en tenant compte de ses contraintes :
– avantages :
- organisation de l'équipe projet et efforts mis en place (tests, sécurisation) en fonction des modules :
- critiques, mises à jour fréquentes, données sensibles : les meilleurs développeurs, beaucoup de tests, sécurisation renforcée,
automatisation...
- très sollicités : tests de charge, monitoring + poussé...
- non critiques, peu de charge : montée en compétence des débutants, gestion en best effort...
- ouverture sur internet, 24/7 : métriques d'usage de l'application, tests avec les outils du public...
- utilisation de la technologie la mieux adaptée au contexte (à un instant T)
- réécriture/migration simplifiée des modules écrits avec des technologies obsolètes dans des nouvelles technologies
– inconvénients :
- plusieurs langages à apprendre
- code très hétérogène
- difficultés d'entraide entre collègues.
– avantages :
- favorise la scalabilité horizontale
- instanciation à la demande de petites machines (conteneurs...)
- chaque noeud d'un module est identique à son voisin
- seuls les microservices très sollicités sont répliqués
– inconvénients :
- il faut s'appuyer sur une infrastructure solide (plateformes, réseau...)
- ajout de nouveaux outils pour orchestrer toute l'application
- vue d'ensemble de l'application plus complexe
- moins de lisibilité sur qui appelle quoi, difficulté de tracer le parcours client
- plus de composants donc plus de risques que l'un d'eux plante
- un problème insignifiant sur un module peut faire tomber un autre module, et difficulté d'en trouver la cause
- problèmes réseau : latence réseau possible (temps de réponses dégradés), coupures...
Pour les microservices, il faut intégrer le fait que les pannes adviendront inévitablement du fait de la multiplication des composants. Il faut
faire en sorte que l'application réagisse bien dans ces cas là (Design for Failure) :
Note : ces 12 facteurs sont indépendants de tout langage de programmation, utilisant tout type de services externes (base de
données...).
Une base de code unique par application/microservice, gérée dans un gestionnaire de sources (avec des branches...). A partir de cette
base de code, on peut avoir plusieurs instances de l'application déployées (en développement, en test, en production...).
2. dépendances
Les dépendances doivent toujours être déclarées explicitement, sans dépendre de l'existence implicite de packages ou librairies fournis
par le système.
3. configuration
Avoir une stricte séparation entre le code et la configuration (portée par l'environnement). Ainsi, on peut déployer le même code sur
plusieurs environnements différents, en modifiant seulement la configuration (fichiers externalisés, variables d'environnement, serveur de
configuration...).
Il faut gérer les services externes locaux ou distants de la même manière. Chaque service externe est une ressource ayant un faible
couplage avec l'application. Ces ressources peuvent être attachées/détachées/remplacées à la demande suivant les situations.
Avoir une séparation stricte entre les étapes de build, release et run :
6. processus
Le(s) processus d'une application est (sont) sans états (stateless) et ne partage(nt) rien. Toute donnée à persister (même les sessions
HTTP utilisateurs) doit l'être dans un service externe statefull comme une base de données (mais pas en mémoire ni sur le disque dur local,
qui peuvent être effacées par redémarrage du serveur...). Si un noeud tombe, un autre peut prendre le relais.
L'application est auto-contenue (standalone) et expose ses services (ex : par HTTP) en les associant à un port qu'elle écoute pour
traiter les requêtes. Une application peut ainsi devenir le service externe d'une autre en lui fournissant son URL d'accès.
8. concurrence
Structurer l'application afin d'assigner à chaque service son processus. Cela permet de gérer au mieux le dimensionnement d'une
application, service par service, suivant leurs propres contraintes. Ainsi, il est simple d'ajouter/supprimer des processus (serveurs) en fonction
de la charge (scalabilité horizontale).
9. jetable
Les processus doivent être jetables : démarrés rapidement et arrêtés gracieusement. Les arrêts doivent terminer le travail en cours et
stopper la prise en charge de nouveaux travaux, mais aussi être robustes aux arrêts brutaux (via une file de messages...).
Garder le développement, la validation et la production aussi proches que possible pour favoriser le déploiement continu. Les
mêmes personnes développent et déploient, ceci de façon régulière et automatisée (toutes les qques heures, voire jours). Les développeurs
doivent utiliser au maximum les mêmes ressources externes que la production (base de données...).
11. logs
Une application ne doit pas gérer le stockage de ses logs (dans des fichiers) mais les considérer comme des flux de sortie. Ces flux
seront capturés par l'environnement qui pourra les aggréger avec les logs d'autres processus, les archiver...
Les processus ponctuels d'administration devraient être lancés dans un environnement identique aux processus standards de
l'application. Le code d'administration doit être livré avec le code de l'application pour éviter les problèmes de synchronisation.
– 2. dépendances : les starters définissent la grande majorité des dépendances utiles au projet
– 3. configuration : facilité de lire des valeurs définies dans des fichiers properties (avec profils) ou des variables d'environnement grâce
aux classes et annotations adéquates
– 4. services externes : l'approche microservice de Spring Boot pousse à considérer toute ressource (locale ou distante) de la même
manière (notamment via des APIs)
– 7. associations de ports : une application Spring Boot est standalone grâce au serveur embarqué, et expose ses services sur un port
HTTP configurable (application.properties externalisé)
– 8. concurrence : Spring Boot est orienté microservices, chacun ayant son processus avec ses contraintes et ses spécificités, et la
possibilité de faire de la scalabilité horizontale fine (par microservice)
– 9. jetable : une application Spring Boot démarre en quelques secondes et peut s'arrêter gracieusement (endpoint /shutdown fourni par
Actuator)
– 10. parité développement/production : grâce au serveur embarqué, le même fat jar/war peut-être déployé en développement, test,
qualification et production.
– 12. processus d'administration : Spring Actuator offre des endpoints (URLs) de monitoring.