En février dernier, j’ai rencontré un problème. J’ai remarqué que le temps d’exécution de mon pipeline Pull Request (PR) Validation avait considérablement augmenté, atteignant près de dix minutes.
De plus, il m’est arrivé que la création de l’image Docker ne tienne pas compte des dernières dépendances et, par conséquent, les tests associés au nouveau code échouaient.
J’ai remarqué que l’étape Installer les dépendances était la coupable.
Voyons comment j’ai résolu le problème avec l’aide d’un collègue expert en DevOps.
La cause
Je n’ai pas trouvé la cause profonde dans mon pipeline Pull Request (PR) Validation, mais plutôt là où je construisais l’image Docker.
L’étape en cause dans le deuxième pipeline consistait au script suivant :
|
|
On n’exécutait pas cette étape sur le pipeline Pull Request (PR) Validation, mais qu’une fois que j’avais fusionné le code dans develop, lorsque le pipeline créant la nouvelle image Docker s’exécutait.
Un autre problème existait dans la version python de mon pipeline Pull Request (PR) Validation :
|
|
Ainsi, si nous disposions de Python 3.13, la compilation pouvait s’exécuter sur une version différente de Python au fil du temps, toujours égale ou supérieure à 3.11.
Dans le pipeline Build Image For Deployment, nous avons strictement utilisé Python 3.11 afin d’éviter tout problème d’incompatibilité entre mon code et les dépendances.
Cela m’amène aux bonnes pratiques DevOps.
Mais quelles bonnes pratiques ?
Pour garantir un comportement cohérent sur les environnements de développement (votre PC, le mien, un VDI, etc.), les environnements de validation (par exemple, la machine virtuelle sur laquelle Azure DevOps exécute son agent pour exécuter les tests unitaires, qui est le cas d’utilisation présent dans cet article) ou dans les environnements de déploiement (par exemple, QA, production), nous avons besoin d’une base de référence cohérente.
Tout d’abord, dans les étapes problématiques ci-dessus, le pipeline PR Validation et le pipeline Build Image For Deployment pouvaient utiliser une version différente de Python, ce qui causait des maux de tête en cas de problème, en particulier lors des tests.
De plus, chaque fois que j’ajoutais et utilisais dans le code un nouveau package, le pipeline PR Validation échouait car le nouveau code faisait référence au nouveau package et générait une erreur d’exécution. En fait, l’image Docker utilisée dans ce pipeline ne contenait pas encore le nouveau package.
Normalisons donc les images Docker.
Création du pipeline « Build Image For CI Purposes » (Créer une image à des fins d’intégration continue)
L’objectif était d’exécuter le pipeline sur des déclencheurs qui tiendraient compte des modifications de fichiers indiquant que nous avions besoin d’une nouvelle image Docker.
Le fichier Dockerfile
Tout d’abord, nous avions besoin d’un fichier Dockerfile distinct du fichier Dockerfile utilisé pour créer l’image de l’application. Ceci afin d’éviter de perturber le pipeline existant. Cependant, nous avons conservé presque tout le contenu.
|
|
Si vous avez lu mon article initial sur le déploiement d’une application Python, vous remarquerez que j’ai simplement supprimé tout le code spécifique à l’application que nous déployons.
Créer le nouveau pipeline
Nous ajoutons ensuite la nouvelle définition du pipeline dans le dossier .azure-pipelines. Je vais vous expliquer étape par étape les ajouts que j’ai apportés au code YAML. Je suis parti de la section Build Image For Deployment (créer une image pour le déploiement). Pour le reste, veuillez vous reporter à mon article cité ci-dessus.
L’objectif était de créer une image non seulement pour l’assurance qualité ou la production, mais aussi lorsque nous avions des changements de dépendances dans l’application afin de pouvoir exécuter de nouveaux tests unitaires avec les dernières dépendances.
Voici les détails.
Tout d’abord, nous ne déclenchons le pipeline que si le fichier requirements.txt, /docker/Dockerfile.ci ou /.azure-pipelines/ap-build-ci-container.yml est modifié. En fait, l’image ne change que lorsque j’ajoute une dépendance ou que je modifie le pipeline ou le fichier Dockerfile.
|
|
Ensuite, mettons à jour la variable imageRepository avec un nom distinct des images pour le déploiement. C’est là que nous stockerons les images utilisées à des fins d’intégration continue uniquement.
|
|
Ensuite, nous devons spécifier qu’on utilise le nouveau fichier Dockerfile.
|
|
J’ai supprimé la variable semantic-version, car nous n’en avons pas besoin dans l’image Docker que nous configurons.
C’est dans l’étape Build and push que nous trouvons les changements les plus importants.
Tout d’abord, nous supprimons la version sémantique, qui n’est pas nécessaire ici, mais nous la conservons pour l’autre pipeline qui se charge de créer l’image que nous déployons en QA ou en production.
Par conséquent, la tâche Set version and image tags est renommée Set image tags et le code exécuté devient :
|
|
En gros,
- Si
$(Build.SourceBranch)estdevelop, alors la balise d’image estready-qa. - Si
$(Build.SourceBranch)estmain, alors la balise d’image estlatest. - Sinon, nous sommes sur une branche de développement et la balise d’image est donc
branch-[nom de la branche].
Rappel : le pipeline s’exécute UNIQUEMENT si le déclencheur trouve une modification sur les fichiers listés dans le déclencheur. Une nouvelle fonctionnalité, qui ne déclenche pas le pipeline lorsque vous poussez la branche associée vers le référentiel distant, ne déclenchera pas de mise à jour de l’image lorsque vous fusionnerez la fonctionnalité vers develop ou main.
La dernière modification apparaît dans la tâche de génération, où nous supprimons les arguments passés à la commande de génération contenant la valeur de version sémantique.
|
|
Enfin, nous conservons la tâche Push image to container registry telle quelle.
Tester le nouveau pipeline
Tout d’abord, vous devrez peut-être fusionner dans develop et main pour ajouter le pipeline à Azure DevOps. Et, si Azure DevOps le détecte pas automatiquement, suivez ces étapes :
- Sélectionnez deux fois l’onglet « Pipelines » et cliquez sur « Nouveau pipeline ».
- Sélectionnez Azure Repos Git.
- Sélectionnez le référentiel qui contient votre fichier YAML.
- Sélectionnez Fichier YAML Azure Pipelines existant.
- Sélectionnez le fichier dans la branche « develop ».
- Enregistrez pour terminer.
J’écris cet article un an après les faits, et même si j’ai pris beaucoup de notes, j’ai un doute sur le point ci-dessus.
Ensuite, pour le tester, vous devez pousser une nouvelle branche vers le référentiel avec une modification dans requirements.txt (un espace supplémentaire ou un commentaire suffira). Cela devrait déclencher le nouveau pipeline.
Une fois la compilation terminée, vous devriez voir un nouveau référentiel myapp-ci dans le Azure Container Registry (ACR) avec une image taguée branch-[nom de votre branche].
Mettre à jour le pipeline PR Validation
Maintenant que nous disposons d’une image Docker, nous pouvons mettre à jour le pipeline PR Validation afin d’utiliser l’image appropriée du référentiel ACR myapp-ci.
Je vous explique tout en détail dans ce qui suit.
Nouvelles variables
Commencez par ajouter de nouvelles variables qui permettent d’extraire l’image cible :
|
|
devient :
|
|
Cela est nécessaire dans la première étape du pipeline de mise à jour PR Validation. En effet, une étape supplémentaire est nécessaire pour lire le registre d’image via l’interface CLI Azure.
Nouvelle étape
Nous avons besoin d’une nouvelle étape contenant une étape de type script AzureCLI qui nous aidera à assigner la variable contenant la balise d’image à utiliser.
Le script est simplement un script bash qui analyse la sortie de la requête de l’interface CLI Azure.
|
|
Ensuite, nous utilisons la variable imageTag dans l’étape suivante :
|
|
Tester le pipeline mis à jour
Pour tester, vous devrez peut-être d’abord fusionner avec develop et vérifier que l’image appropriée est correctement extraite et que les tests unitaires s’exécutent sans problème.
Essayez avec et sans modification des trois fichiers marqués comme déclencheurs pour valider l’ensemble du processus.
Conclusion
Désormais, votre processus de requêtes de tirage gère à la fois les développements qui modifient les dépendances ou la CI, et ceux qui ne le font pas.
Grâce à cela, vous n’avez plus à vous soucier d’exécuter des tests unitaires sur une image Docker obsolète ni à penser à préparer l’image avant cela.
En prime, vous ne créez de nouvelles images Docker à jour que lorsque cela est nécessaire. La prochaine étape logique serait de mettre à jour le compte d’automatisation qui nettoie le référentiel des images obsolètes. En êtes-vous capable ? J’en suis sûr !
Suivez-moi !
Merci d’avoir lu cet article. Assurez-vous de me suivre sur X, de vous abonner à ma publication Substack et d’ajouter mon blog à vos favoris pour ne pas manquer les prochains articles.