Bon après-midi
Presque immédiatement après l'installation et la configuration de CI / CD selon les instructions du post précédent , l'équipe avait une question sur la façon d'effectuer correctement les tests d'intégration. Nous avions déjà l'expérience de l'exécution des dépendances de test dans les conteneurs Docker, mais cela est devenu problématique depuis que l'assemblage lui-même a été lancé dans le conteneur. Dans cet article, je voudrais décrire deux méthodes possibles de test d'intégration à l'intérieur du conteneur qui conviennent à mon équipe.
Exigences de test d'intégration
Par définition, les tests d'intégration sont des tests qui testent le fonctionnement d'une application avec ses composants dépendants. Les exemples incluent les bases de données, les files d'attente et d'autres services.
Dans le cadre des tests, nous voulions:
- exécuter des tests également localement et sur jenkins
- Évitez la pré-installation et la configuration des applications dépendantes
- déclarer et exécuter des dépendances à l'aide de fichiers dans le référentiel de projet
- pouvoir exécuter plusieurs assemblages en parallèle
- il est conseillé de pouvoir ajouter de nouvelles dépendances à l'avenir
- il est conseillé de pouvoir tester avec différentes versions de la même dépendance
Sur la base de ces exigences, nous avons immédiatement rejeté l'idée d'avoir des installations communes permanentes de bases de données de test et de files d'attente en raison de problèmes de partage des ressources entre les assemblys, de la difficulté de maintenir et de modifier cette infrastructure.
Option 1: Solutions intégrées
Il y a pas mal de bibliothèques dans l'écosystème Java qui exécutent une dépendance pour un test:
Cette approche est aussi simple que possible et satisfait la plupart des exigences décrites précédemment, mais n'est pas universelle en termes d'ajout de nouvelles dépendances de test (mysql?) Ou en utilisant des versions spécifiques ou nombreuses des dépendances.
Convient bien aux services simples avec 1-2 dépendances.
Option 2: conteneurs de test
Docker est un moyen logique de résoudre les lacunes de l'approche précédente: vous pouvez trouver (ou créer une image Docker) pour n'importe quelle dépendance avec n'importe quelle version. Il est probable que certaines des dépendances en production fonctionnent avec les mêmes images.
Si le lancement local de l'image (ou plusieurs en utilisant docker-compose) n'est pas un problème, il y aura des difficultés sur CI puisque l'assemblage lui-même a lieu dans le conteneur. Bien qu'il soit possible d'exécuter docker dans docker, il n'est pas recommandé par le créateur de dind . Le moyen préféré de contourner ce problème est de réutiliser un processus de docker déjà en cours d'exécution, souvent appelé docker frère. Pour ce faire, vous avez besoin du processus de docker enfant pour utiliser /var/run/docker.sock
partir du parent. Dans un article précédent, cela était déjà utilisé pour publier des images de docker avec une application compilée.
Il a été décidé d'utiliser la bibliothèque testcontainers car elle:
- fournit une bonne API de gestion des dépendances
- a des intégrations avec les bases de données et les files d'attente les plus populaires
- utilise l'approche de docker de frère lors de l'exécution dans un conteneur
- fonctionne de la même manière localement et sur ci
- arrête les conteneurs après l'assemblage
Convient parfaitement aux services plus complexes ou aux services nécessitant des dépendances particulières.
Gestion des ressources
Ensuite, vous devez faire attention à la consommation de ressources par l'assembly du projet (qui peut augmenter considérablement après l'ajout de tests d'intégration).
Pour le moment, l'assemblage n'indique pas la quantité requise de mémoire et de partages cpu, ce qui pourrait être deux problèmes potentiels:
- Le premier est un trop grand nombre d'assemblages parallèles sur la même machine, ce qui entraînera un facteur de charge élevé. Les assemblées passeront probablement, mais elles y consacreront beaucoup plus de temps.
- Le deuxième est OOM kill. Kubernetis peut décider que vous consommez trop de mémoire et va simplement tuer les assemblys avant qu'ils ne se terminent.
Vous pouvez limiter les ressources du conteneur dans le foyer en utilisant la construction:
resources: requests: cpu: 1 memory: 512Mi limits: cpu: 1 memory: 512Mi
Jdk9 et versions ultérieures prennent déjà en charge le travail dans un conteneur (-XX: + UseContainerSupport (activé par défaut), fonctionne en combinaison avec -XX: InitialRAMPercentage / -XX: MaxRAMPercentage)
Un exemple complet peut être trouvé ici .
Pour que Jdk8 fonctionne correctement, la mise à jour 131 ou supérieure est requise avec les -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap
pour lire la mémoire disponible à partir de cgroup et non de la machine hôte, ou à chaque fois spécifiez manuellement la taille de -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap
mémoire disponible à l'aide de Xmx
.
Un exemple est disponible ici .
Il convient de noter que kubernetes ne sait rien des ressources dépensées pour les conteneurs exécutés à l'aide de testcontainers ou de frère-docker. Pour fonctionner correctement dans cette situation, vous pouvez réserver des ressources dans le conteneur maven, en tenant compte de toutes les dépendances de test.
Conclusion
Le test d'intégration lors du lancement d'une build dans un conteneur est possible et n'est pas une tâche difficile.
Un exemple d'application avec des tests d'intégration utilisant des conteneurs de test que vous pouvez trouver ici et la configuration pour exécuter Jenkins sur kubernetes ici .