Comment configurer un environnement de test d’intégration continue avec Docker et Docker Compose sur Ubuntu 16.04

introduction

Continuous integration (CI) fait référence à la pratique où les développeursintegrate code aussi souvent que possible et chaque commit est testé avant et après avoir été fusionné dans un référentiel partagé par unautomated build.

CI accélère votre processus de développement et minimise le risque de problèmes critiques en production, mais ce n'est pas anodin à mettre en place; les builds automatisés s'exécutent dans un environnement différent où l'installation deruntime dependencies et la configuration deexternal services peuvent être différentes de celles de vos environnements locaux et de développement.

Docker est une plateforme de conteneurisation qui vise à simplifier les problèmes de standardisation de l'environnement afin que le déploiement des applications puisse également être standardisé (find out more about Docker). Pour les développeurs, Docker vous permet de simuler des environnements de production sur des machines locales en exécutant des composants d’application dans des conteneurs locaux. Ces conteneurs sont facilement automatisables à l'aide deDocker Compose, indépendamment de l'application et du système d'exploitation sous-jacent.

Ce didacticiel utilise Docker Compose pour illustrer l’automatisation des flux de travail CI.

Nous allons créer une application Python de type «Hello world» dockerisée et un script de test Bash. L’application Python nécessite l’exécution de deux conteneurs: un pour l’application elle-même et un conteneur Redis pour le stockage requis en tant que dépendance de l’application.

Ensuite, le script de test sera ancré dans son propre conteneur et tout l'environnement de test sera déplacé vers un fichierdocker-compose.test.yml afin que nous puissions nous assurer que nous exécutons chaque exécution de test dans un environnement d'application frais et uniforme.

Cette approche montre comment vous pouvez créer un nouvel environnement de test identique pour votre application, y compris ses dépendances, chaque fois que vous le testez.

Ainsi, nous automatisons les flux de travaux CI indépendamment de l’application testée et de l’infrastructure sous-jacente.

Conditions préalables

Avant de commencer, vous aurez besoin de:

[[step-1 -—- create-the-quot-hello-world-quot-python-application]] == Étape 1 - Créer l'application Python «Hello World»

Dans cette étape, nous allons créer une application Python simple comme exemple du type d'application que vous pouvez tester avec cette configuration.

Créez un nouveau répertoire pour notre application en exécutant:

cd ~
mkdir hello_world
cd hello_world

Editez un nouveau fichierapp.py avecnano:

nano app.py

Ajoutez le contenu suivant:

app.py

from flask import Flask
from redis import Redis




app = Flask(__name__)
redis = Redis(host="redis")




@app.route("/")
def hello():
    visits = redis.incr('counter')
    html = "

Hello World!

" \ "Visits: {visits}" \ "
" return html.format(visits=visits) if __name__ == "__main__": app.run(host="0.0.0.0", port=80)

Lorsque vous avez terminé, enregistrez et quittez le fichier.

app.py est une application Web basée surFlask qui se connecte à un service de données Redis. La lignevisits = redis.incr('counter') augmente le nombre de visites et persiste cette valeur dans Redis. Enfin, un messageHello World avec le nombre de visites est renvoyé en HTML.

Notre application a deux dépendances,Flask etRedis, que vous pouvez voir dans les deux premières lignes. Ces dépendances doivent être définies avant que nous puissions exécuter l'application.

Ouvrir un nouveau fichier:

nano requirements.txt

Ajouter le contenu:

requirements.txt

Flask
Redis

Lorsque vous avez terminé, enregistrez et quittez le fichier. Maintenant que nous avons défini nos exigences, que nous mettrons en place plus tard dans lesdocker-compose.yml, nous sommes prêts pour l'étape suivante.

[[step-2 -—- dockerize-the-quot-hello-world-quot-application]] == Étape 2 - Dockerize l'application «Hello World»

Docker utilise un fichier appeléDockerfile pour indiquer les étapes requises pour créer une image Docker pour une application donnée. Editer un nouveau fichier:

nano Dockerfile

Ajoutez le contenu suivant:

Dockerfile

FROM python:2.7


WORKDIR /app


ADD requirements.txt /app/requirements.txt
RUN pip install -r requirements.txt


ADD app.py /app/app.py


EXPOSE 80


CMD ["python", "app.py"]

Analysons le sens de chaque ligne:

  • FROM python:2.7: indique que l'image de notre application «Hello World» est construite à partir de l'image Docker officielle depython:2.7

  • WORKDIR /app: définit le répertoire de travail à l'intérieur de l'image Docker sur/app

  • ADD requirements.txt /app/requirements.txt: ajoute le fichierrequirements.txt à notre image Docker

  • RUN pip install -r requirements.txt: installe les dépendancespip de l'application

  • ADD app.py /app/app.py: ajoute le code source de notre application à l'image Docker

  • EXPOSE 80: indique que notre application est accessible au port 80 (le port web public standard)

  • CMD ["python", "app.py"]: la commande qui lance notre application

Enregistrez et quittez le fichier. Ce fichierDockerfile contient toutes les informations nécessaires pour construire le composant principal de notre application «Hello World».

La dépendance

Nous arrivons maintenant à la partie plus sophistiquée de l'exemple. Notre application nécessite Redis en tant que service externe. C'est le type de dépendance qui pourrait être difficile à configurer de manière identique à chaque fois dans un environnement Linux traditionnel, mais avec Docker Compose, nous pouvons le configurer de manière répétable à tout moment.

Créons un fichierdocker-compose.yml pour commencer à utiliser Docker Compose.

Editer un nouveau fichier:

nano docker-compose.yml

Ajoutez le contenu suivant:

docker-compose.yml

web:
  build: .
  dockerfile: Dockerfile
  links:
    - redis
  ports:
    - "80:80"
redis:
  image: redis

Ce fichier Docker Compose indique comment lancer l'application «Hello World» localement dans deux conteneurs Docker.

Il définit deux conteneurs,web etredis.

  • web utilise le répertoire courant pour le contextebuild, et construit notre application Python à partir du fichierDockerfile que nous venons de créer. Ceci est une image Docker locale que nous avons créée uniquement pour notre application Python. Il définit un lien vers le conteneurredis pour avoir accès à l'IP du conteneurredis. Il rend également le port 80 accessible au public sur Internet à l’aide du réseau IP public de votre serveur Ubuntu.

  • redis est exécuté à partir d'une image Docker publique standard, nomméeredis.

Lorsque vous avez terminé, enregistrez et quittez le fichier.

[[step-3 -—- deploy-the-quot-hello-world-quot-application]] == Étape 3 - Déployer l'application «Hello World»

Au cours de cette étape, nous déploierons l’application qui, à la fin, sera accessible sur Internet. Pour les besoins de votre flux de travail de déploiement, vous pouvez considérer qu'il s'agit d'un environnement de développement, de transfert ou de production, car vous pouvez déployer l'application de la même manière à plusieurs reprises.

Les fichiersdocker-compose.yml etDockerfile vous permettent d'automatiser le déploiement des environnements locaux en exécutant:

docker-compose -f ~/hello_world/docker-compose.yml build
docker-compose -f ~/hello_world/docker-compose.yml up -d

La première ligne construit l'image de notre application locale à partir du fichierDockerfile. La deuxième ligne exécute les conteneursweb etredis en mode démon (-d), comme spécifié dans le fichierdocker-compose.yml.

Vérifiez que les conteneurs d'applications ont été créés en exécutant:

docker ps

Cela devrait afficher deux conteneurs en cours d'exécution, nomméshelloworld_web_1 ethelloworld_redis_1.

Voyons si l’application est active. Nous pouvons obtenir l'IP du conteneurhelloworld_web_1 en exécutant:

WEB_APP_IP=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' helloworld_web_1)
echo $WEB_APP_IP

Vérifiez que l'application Web renvoie le message approprié:

curl http://${WEB_APP_IP}:80

Cela devrait retourner quelque chose comme:

Sortie

Hello World!

Visits: 2

Le nombre de visites est incrémenté chaque fois que vous atteignez ce noeud final. Vous pouvez également accéder à l'application «Hello World» à partir de votre navigateur en visitant l'adresse IP publique de votre serveur Ubuntu.

Comment personnaliser pour votre propre application

La clé pour configurer votre propre application consiste à placer votre application dans son propre conteneur Docker et à exécuter chaque dépendance à partir de son propre conteneur. Ensuite, vous pouvez définir les relations entre les conteneurs avec Docker Compose, comme illustré dans l'exemple. Docker Compose est traité plus en détail dans ceDocker Compose article.

Pour un autre exemple sur la façon de faire fonctionner une application sur plusieurs conteneurs, lisez cet article sur l'exécution deWordPress and phpMyAdmin with Docker Compose.

[[step-4 -—- create-the-test-script]] == Étape 4 - Créer le script de test

Nous allons maintenant créer un script de test pour votre application Python. Ce sera un simple script qui vérifie la sortie HTTP de l’application. Le script est un exemple du type de test que vous souhaitez peut-être exécuter dans le cadre de votre processus de déploiement d'intégration continue.

Editer un nouveau fichier:

nano test.sh

Ajoutez le contenu suivant:

test.sh

sleep 5
if curl web | grep -q 'Visits: '; then
  echo "Tests passed!"
  exit 0
else
  echo "Tests failed!"
  exit 1
fi

test.sh teste la connectivité Web de base de notre application «Hello World». Il utilise cURL pour récupérer le nombre de visites et indique si le test a été réussi ou non.

[[step-5 -—- create-the-testing-environment]] == Étape 5 - Créer l'environnement de test

Afin de tester notre application, nous devons déployer un environnement de test. Et nous voulons nous assurer qu'il est identique à l'environnement d'application en direct que nous avons créé dansStep 3.

Premièrement, nous devons dockériser notre script de test en créant un nouveau fichier Dockerfile. Editer un nouveau fichier:

nano Dockerfile.test

Ajoutez le contenu suivant:

Dockerfile.test

FROM ubuntu:xenial


RUN apt-get update && apt-get install -yq curl && apt-get clean


WORKDIR /app


ADD test.sh /app/test.sh


CMD ["bash", "test.sh"]

Dockerfile.test étend l'image officielleubuntu:xenial pour installer la dépendancecurl, ajoutetests.sh au système de fichiers image et indique la commandeCMD qui exécute le script de test avec Frapper.

Une fois que nos tests sont dockérisés, ils peuvent être exécutés de manière réplicable et agnostique.

L'étape suivante consiste à relier notre conteneur de test à notre application «Hello World». C'est ici que Docker Compose vient à la rescousse. Editer un nouveau fichier:

nano docker-compose.test.yml

Ajoutez le contenu suivant:

docker-compose.test.yml

sut:
  build: .
  dockerfile: Dockerfile.test
  links:
    - web
web:
  build: .
  dockerfile: Dockerfile
  links:
    - redis
redis:
  image: redis

La seconde moitié du fichier Docker Compose déploie l'application principaleweb et sa dépendanceredis de la même manière que le fichierdocker-compose.yml précédent. C'est la partie du fichier qui spécifie les conteneursweb etredis. La seule différence est que le conteneurweb n’expose plus le port 80, de sorte que l’application ne sera pas disponible sur l’Internet public pendant les tests. Ainsi, vous pouvez voir que nous construisons l’application et ses dépendances exactement de la même manière qu’elles le sont dans le déploiement en direct.

Le fichierdocker-compose.test.yml définit également un conteneursut (nommé poursystem under tests) qui est responsable de l'exécution de nos tests d'intégration. Le conteneursut spécifie le répertoire courant comme notre répertoirebuild et spécifie notre fichierDockerfile.test. Il est lié au conteneurweb afin que l'adresse IP du conteneur d'application soit accessible à notre scripttest.sh.

Comment personnaliser pour votre propre application

Notez quedocker-compose.test.yml peut inclure des dizaines de services externes et plusieurs conteneurs de test. Docker pourra exécuter toutes ces dépendances sur un seul hôte car chaque conteneur partage le système d'exploitation sous-jacent.

Si vous avez plus de tests à exécuter sur votre application, vous pouvez créer des fichiers Docker supplémentaires pour eux, similaires au fichierDockerfile.test ci-dessus.

Ensuite, vous pouvez ajouter des conteneurs supplémentaires sous le conteneursut dans le fichierdocker-compose.test.yml, en référençant les Dockerfiles supplémentaires.

[[step-6 -—- test-the-quot-hello-world-quot-application]] == Étape 6 - Test de l'application "Hello World"

Enfin, pour étendre les idées Docker des environnements locaux aux environnements de test, nous disposons d’un moyen automatisé de tester notre application à l'aide de Docker en exécutant:

docker-compose -f ~/hello_world/docker-compose.test.yml -p ci build

Cette commande crée les images locales nécessaires àdocker-compose.test.yml. Notez que nous utilisons-f pour pointer versdocker-compose.test.yml et-p pour indiquer un nom de projet spécifique.

Maintenant, lancez votre nouvel environnement de test en exécutant:

docker-compose -f ~/hello_world/docker-compose.test.yml -p ci up -d
OutputCreating ci_redis_1
Creating ci_web_1
Creating ci_sut_1

Vérifiez la sortie du conteneursut en exécutant:

docker logs -f ci_sut_1

Sortie

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    42  100    42    0     0   3902      0 --:--:-- --:--:-- --:--:--  4200
Tests passed!

Et enfin, vérifiez le code de sortie du conteneursut pour vérifier si vos tests ont réussi:

docker wait ci_sut_1

Sortie

0

Après l'exécution de cette commande, la valeur de$? sera0 si les tests réussissent. Sinon, nos tests d'application ont échoué.

Notez que d'autres outils de CI peuvent cloner notre référentiel de code et exécuter ces quelques commandes pour vérifier si les tests réussissent avec les derniers bits de votre application sans se soucier des dépendances d'exécution ou des configurations de services externes.

C'est ça! Nous avons réussi à exécuter nos tests dans un environnement nouvellement construit identique à notre environnement de production.

Conclusion

Grâce à Docker et Docker Compose, nous avons pu automatiser la création d'une application (Dockerfile), le déploiement d'un environnement local (docker-compose.yml), la construction d'une image de test (Dockerfile.test) et l'exécution (intégration) tests (docker-compose.test.yml) pour toute application.

En particulier, les avantages de l'utilisation du fichierdocker-compose.test.yml pour les tests sont que le processus de test est:

  • Automatable: la manière dont un outil exécute lesdocker-compose.test.yml est indépendante de l'application testée

  • Light-weight: des centaines de services externes peuvent être déployés sur un seul hôte, simulant des environnements de test (d'intégration) complexes

  • Agnostic: évitez le verrouillage du fournisseur CI, et vos tests peuvent s'exécuter dans n'importe quelle infrastructure et sur n'importe quel système d'exploitation prenant en charge Docker

  • Immutable: les tests passant sur votre machine locale passeront dans votre outil CI

Ce tutoriel montre un exemple de test d'une application simple «Hello World».

Il est maintenant temps d'utiliser vos propres fichiers d'application, d'ancrer vos propres scripts de test d'application et de créer vos propresdocker-compose.test.yml pour tester votre application dans un environnement frais et immuable.

Related