Dans bon nombre de projets logiciels basés sur le cloud, un processus CI/CD est conçu et maintenu pour déployer les applications vers les environnements cloud de manière efficace, sûre et productive.
L'attention se porte généralement sur le versant cloud/distant du processus CI/CD, mais on évoque rarement la phase qui le précède : le développement et les tests réalisés en local sur le poste du développeur.
Dans cet article, je vais montrer pourquoi de nombreuses méthodes courantes de développement local sont loin d'être idéales, et présenter une démo d'une approche alternative, cloud-native, qui améliorera votre productivité (ou celle de vos développeurs). 🌤
Photo de Christina @ wocintechchat.com sur Unsplash
👨🏼💻 Le développement local
Il n'existe pas de bonne pratique universelle — tout dépend des besoins et des exigences de chaque organisation ou projet — mais le flux CI/CD type ressemble en général à ceci :

Examinons ce qui se passe juste avant l'étape n°1 : le développement local.
Dans la plupart des cas, il se déroule sur la machine du développeur : planification, conception, écriture du code, écriture des tests, exécution des tests, parfois un POC — et, à l'occasion, une vérification manuelle que tout fonctionne comme prévu.
Beaucoup de projets investissent massivement dans des configurations locales qui imitent l'environnement cloud, afin de vérifier que la nouvelle fonctionnalité ou le correctif fonctionne correctement — sans provoquer de régression sur d'autres fonctionnalités ou logiques métier.
Ce type de configuration sert principalement à exécuter certaines catégories de tests automatisés (tests système, tests d'intégration, tests de bout en bout). Et bien sûr, rien ne vous empêche de faire aussi des tests manuels 😏.
Les environnements locaux servent également à déboguer le code. Certains développeurs sont des adeptes convaincus de ce que j'appelle le debug-driven development, mais les usages vont bien au-delà. Le débogage est précieux quand on cherche à investiguer un bug coriace ou à mieux comprendre un flux complexe.
Méthodes courantes de développement local
Imaginez que vous travailliez sur un projet impliquant plusieurs microservices déployés sur Kubernetes. Voici quelques méthodes que j'ai pu observer dans l'industrie :
- Méthode Docker-compose : écrire et maintenir des manifestes docker-compose (en quelque sorte un doublon des manifestes YAML K8s existants) qui démarrent vos services et leurs dépendances tierces pour les tests locaux.
- Méthode Kubernetes local : faire tourner un cluster Kubernetes local via minikube/k3s/kind ou autre, et y déployer ses services pour les tester, le tout combiné à une série de scripts.
- Méthode des suites de tests : Makefiles ou scripts bash qui lancent des suites de tests d'intégration par service, avec des dépendances tierces démarrées localement sous forme de conteneurs Docker (dockertest, par exemple, est un choix répandu pour ce genre de configuration).
Voilà autant d'options possibles pour exécuter et tester votre application en local.
Inconvénients des méthodes courantes de développement local
Ces approches comportent toutefois quelques limites :
Absence de support local côté services tiers
Certaines dépendances cloud tierces ne proposent aucun moyen de fonctionner en local. Dans certains cas, vous pouvez les émuler en mockant/stubbant les API tierces, mais cela demande énormément de maintenance et ne reflète pas vraiment la réalité. Une forme de triche, en somme. Si l'une de vos requêtes en base de données est défectueuse, par exemple, comment pourrez-vous le détecter via vos tests ou votre débogage si vous ne dialoguez pas avec une véritable instance de base ?
Problèmes liés à la consommation de ressources
Si votre application est gourmande, votre poste risque de ne pas suivre. Imaginez par exemple que vous lanciez un test de charge pour reproduire une fuite mémoire. Ou qu'un bug ne se reproduise qu'avec 20 réplicas de votre application en parallèle. Résultat : votre poste finit par ne plus répondre 😅. La productivité en pâtit.
Mises en veille pendant le débogage
Si vous voulez tester ou déboguer un bug rare et difficile à reproduire, qui ne survient que tous les quelques jours, votre machine locale risque de s'éteindre, de se mettre en veille ou en hibernation, et de vous obliger à tout recommencer.
Pas de source unique de vérité
Avec la méthode Docker-compose, vous enfreignez le principe de la source unique de vérité en maintenant plusieurs représentations de vos environnements.
Exécuter des tests d'intégration ≠ véritable intégration
Avec la méthode des suites de tests, exécuter des tests d'intégration reste une bonne pratique aux nombreux avantages (même en local), mais cela ne remplace pas l'intégration réelle de votre changement dans un véritable environnement cloud. Certains bugs et défaillances d'intégration ou de déploiement peuvent passer inaperçus (problèmes de connectivité réseau vers d'autres services, problèmes de configuration, plantages au démarrage, etc.).
Les inconvénients ci-dessus dégradent l'expérience de développement et nuisent à la productivité. Et bien souvent, ils ne reflètent pas la réalité (un grand classique du It works on my machine!) et apportent donc peu de valeur.
Existe-t-il une approche plus moderne du développement local, qui améliore productivité et confort d'utilisation ? Oui, et heureusement.
☁️ **_Les environnements de développement cloud-native_**
Explorons à présent les environnements distants, cloud-native.
En transformant votre environnement local en environnement distant, les problèmes évoqués plus haut s'évanouissent. Mieux : votre expérience de développement gagne en modernité, en fiabilité et devient pleinement cloud-native.
Comment fonctionnent les environnements de développement cloud-native ?
Vous attribuez un environnement cloud distinct à chacun de vos développeurs : une réplique (pas tout à fait, mais presque) de vos environnements de développement et de production.
Avec cette approche d'environnement personnel cloud-native, chaque développeur de l'équipe dispose de l'autonomie nécessaire pour utiliser son environnement à sa guise : tester de nouvelles fonctionnalités et des correctifs, reproduire des bugs, ou simplement bricoler pour apprendre. Le tout dans un environnement très proche de la production. ✌️
Vous pouvez ensuite adapter votre outillage pour rendre ces environnements encore plus utiles, par exemple en y exécutant des tests manuels ou automatisés, en y reproduisant le trafic de production, etc.
Autre atout précieux : la possibilité d'effectuer du débogage à distance en direct dans ces environnements, en isolation totale, sans interférer avec les autres. Vous pouvez vous appuyer sur des outils existants conçus pour cela (nous en verrons un exemple dans le prochain chapitre).
De vrais bénéfices en productivité, maintenabilité et fiabilité
Les raisons de passer à des environnements de dev cloud-native sont nombreuses, mais voici mon top huit.
Vous pourrez :
- Réutiliser vos manifestes YAML K8s existants, sans maintenir de configurations de déploiement en doublon (DRY 🌵).
- Déployer vers un environnement cloud de la même manière que vers dev/prod, mais avec un isolement supplémentaire.
- Vous affranchir de votre matériel local. L'infrastructure cloud monte facilement en charge pour exécuter des tests gourmands, et redescend le week-end.
- Lancer des tests longue durée dans votre environnement sans craindre que votre poste ne s'éteigne ou ne passe en veille.
- Détecter rapidement les problèmes d'intégration, puisque vous utilisez de vraies API de services cloud et non des mocks (vous pouvez par exemple communiquer avec PubSub, Cloud SQL, Datastore et bien d'autres services cloud).
- Profiter de l'infrastructure intégrée du cloud pour le monitoring, l'alerting, le profiling, le tracing et l'agrégation de logs, exactement comme en production.
- Gagner du temps en cessant d'exécuter des scripts de setup compliqués en permanence. Le cloud est rapide. Votre environnement est prêt quand vous en avez besoin.
- Utiliser ces environnements pour apprendre Kubernetes par la pratique (si vous débutez), sans impacter les autres environnements.
Enfin — au-delà des développeurs — les ingénieurs DevOps/SRE peuvent eux aussi tirer parti d'un environnement personnel pour valider des changements de configuration d'infrastructure avant de les appliquer à des environnements partagés.
🙋♂️ Salut ! J'ai une question !
Ma facture cloud va-t-elle exploser ?
La première idée qui vient à l'esprit consiste à provisionner pour chaque développeur une réplique entièrement séparée et indépendante de vos instances cloud, isolée par projets, où chaque projet contient une réplique de votre infrastructure.
C'est généralement une bonne pratique, surtout côté sécurité, puisqu'elle offre une bonne isolation. Mais le coût peut vite poser problème, en particulier à mesure que votre entreprise ou votre équipe grandit.
Puisqu'il s'agit ici d'environnements de développement, le plus souvent isolés de la production, vous pouvez accepter un compromis entre coûts et sécurité.
Note : ce compromis entre sécurité et coûts varie selon les organisations et leurs exigences — assurez-vous d'en mesurer les implications. Ce guide de sécurité GKE peut vous y aider.
Alors, comment arbitrer ? Vous pouvez tirer parti des fonctionnalités multi-tenant de vos services cloud pour réduire les coûts en partageant les ressources entre environnements.
On parle souvent de Soft Multi-Tenancy : le " Soft " signifie que vous limitez le risque en n'autorisant que des utilisateurs d'une même organisation " de confiance " à partager les ressources.
Par exemple, tous les développeurs d'une équipe travaillant sur une même application peuvent partager un cluster K8s, où chaque développeur dispose de l'intégralité de la stack applicative répliquée dans son propre namespace K8s.
Autre exemple : tous les développeurs peuvent partager une seule instance de base de données, où chacun dispose d'un utilisateur, d'un schéma et de tables dédiés.
C'est l'approche que nous allons illustrer dans le prochain chapitre.
Est-ce pénible à mettre en place et à maintenir ? 🤕🥸
Pas vraiment ! Vous pouvez (et devriez) automatiser le tout et le rendre facilement reproductible grâce aux principes d'Infrastructure as Code et de GitOps.
Une fois la configuration entièrement décrite sous forme de code, créer ou détruire un environnement personnel ne prend que quelques clics. Et puisque le processus est automatisé, le risque d'erreur humaine est réduit.
Pourquoi cela ne rentre-t-il pas dans le périmètre de l'environnement de développement ? 🤔
L'environnement de développement classique n'est pas isolé : il est partagé entre tous les développeurs. Vous mergez votre code, votre collègue merge le sien au même moment, et vous obtenez un véritable melting-pot. Déboguer et mener des expérimentations dans un environnement partagé devient impraticable dès que l'équipe grandit.
Par ailleurs, le stade de l'environnement de développement intervient un peu trop tard. Vous avez déjà créé une PR, passé la code review, mergé la PR. Et là, vous découvrez que votre changement fait planter le service dans la foulée. C'est un blocage qui impose un revert, un rollback ou un hotfix : du temps perdu pour vous comme pour vos collègues.
Environnements personnels ou environnements éphémères ? 👀
La réponse simple : à vous de voir.
En substance, avec les environnements personnels, vous dédiez un environnement à une personne précise sur le long terme. Avec les environnements éphémères, vous créez un environnement pour un changement précis (qui est détruit après le merge de ce changement).
Mon collègue
a écrit un excellent article sur les environnements éphémères, je vous invite à le consulter également.
Vous pouvez tout à fait combiner les deux — même si cela risque d'être disproportionné par rapport à vos besoins.
Dans la Partie 2, j'illustrerai une telle configuration à travers un exemple d'architecture, avec Terraform, ArgoCD et Telepresence. Direction la Partie 2 !