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

De Get Docs
Aller à :navigation, rechercher

Introduction

L'intégration continue (CI) fait référence à la pratique selon laquelle les développeurs intègrent le code aussi souvent que possible et chaque validation est testée avant et après avoir été fusionnée dans un référentiel partagé par une construction automatisée .

CI accélère votre processus de développement et minimise les risques de problèmes critiques en production, mais il n'est pas anodin à mettre en place ; les builds automatisés s'exécutent dans un environnement différent où l'installation des dépendances d'exécution et la configuration des services externes peuvent être différentes de celles de vos environnements local et de développement.

Docker est une plateforme de conteneurisation qui vise à simplifier les problématiques de standardisation des environnements afin de standardiser également le déploiement des applications (en savoir plus sur 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 de Docker Compose, indépendamment de l'application et du système d'exploitation sous-jacent.

Ce didacticiel utilise Docker Compose pour démontrer l'automatisation des workflows CI.

Nous allons créer une application Python Dockerisée de type « Hello world » et un script de test Bash. L'application Python nécessitera 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 pour l'application.

Ensuite, le script de test sera Dockerisé dans son propre conteneur et l'ensemble de l'environnement de test déplacé vers un fichier docker-compose.test.yml afin que nous puissions nous assurer que nous exécutons chaque exécution de test dans un nouveau et uniforme environnement applicatif.

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 la testez.

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

Conditions préalables

Avant de commencer, vous aurez besoin de :

É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

Modifier un nouveau fichier app.py avec nano :

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 = "<h3>Hello World!</h3>" \
           "<b>Visits:</b> {visits}" \
           "<br/>"
    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 sur Flask qui se connecte à un service de données Redis. La ligne visits = redis.incr('counter') augmente le nombre de visites et conserve cette valeur dans Redis. Enfin, un Hello World message avec le nombre de visites est retourné en HTML.

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

Ouvrez un nouveau fichier :

nano requirements.txt

Ajoutez le contenu :

exigences.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 le docker-compose.yml, nous sommes prêts pour la prochaine étape.

É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. Modifier un nouveau fichier :

nano Dockerfile

Ajoutez le contenu suivant :

Fichier Docker

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 notre image d'application "Hello World" est construite à partir de l'image officielle python:2.7 Image Docker
  • 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 fichier requirements.txt à notre image Docker
  • RUN pip install -r requirements.txt: installe l'application pip dépendances
  • ADD app.py /app/app.py: ajoute le code source de notre application à l'image Docker
  • EXPOSE 80: indique que notre application est joignable au port 80 (le port web public standard)
  • CMD ["python", "app.py"]: la commande qui lance notre application

Enregistrez et quittez le fichier. Cette Dockerfile contient toutes les informations nécessaires pour créer le composant principal de notre application "Hello World".

La dépendance

Passons maintenant à la partie la 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 reproductible à chaque fois.

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

Modifier 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 faire tourner l'application "Hello World" localement dans deux conteneurs Docker.

Il définit deux conteneurs, web et redis.

  • web utilise le répertoire courant pour le build contexte, et construit notre application Python à partir du Dockerfile fichier que nous venons de créer. Il s'agit d'une image Docker locale que nous avons créée uniquement pour notre application Python. Il définit un lien vers le redis conteneur afin d'avoir accès au redis IP du conteneur. Il rend également le port 80 accessible au public depuis Internet en utilisant l'adresse IP publique de votre serveur Ubuntu.
  • redis est exécuté à partir d'une image Docker publique standard, nommée redis.

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

Étape 3 - Déployez l'application "Hello World"

Dans cette étape, nous allons déployer l'application et, à la fin, elle 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, intermédiaire ou de production, car vous pouvez déployer l'application de la même manière plusieurs fois.

La docker-compose.yml et Dockerfile 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 notre image d'application locale à partir du Dockerfile dossier. La deuxième ligne exécute le web et redis conteneurs en mode démon (-d), comme indiqué dans le docker-compose.yml dossier.

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

docker ps

Cela devrait montrer deux conteneurs en cours d'exécution, nommés helloworld_web_1 et helloworld_redis_1.

Vérifions que l'application est active. Nous pouvons obtenir l'IP du helloworld_web_1 conteneur 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 bon message :

curl http://${WEB_APP_IP}:80

Cela devrait retourner quelque chose comme :

Production

<h3>Hello World!</h3><b>Visits:</b> 2<br/>

Le nombre de visites est incrémenté chaque fois que vous atteignez ce point de terminaison. Vous pouvez également accéder à l'application "Hello World" depuis 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 est de placer votre application dans son propre conteneur Docker et d'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 couvert plus en détail dans cet article Docker Compose.

Pour un autre exemple d'exécution d'une application sur plusieurs conteneurs, lisez cet article sur l'exécution de WordPress et phpMyAdmin avec Docker Compose.

Étape 4 - Créer le script de test

Nous allons maintenant créer un script de test pour notre 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 pouvez exécuter dans le cadre de votre processus de déploiement d'intégration continue.

Modifier un nouveau fichier :

nano test.sh

Ajoutez le contenu suivant :

test.sh

sleep 5
if curl web | grep -q '<b>Visits:</b> '; 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.

É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éé à l'Étape 3.

Tout d'abord, nous devons dockeriser notre script de test en créant un nouveau fichier Dockerfile. Modifier 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 prolonge l'officiel ubuntu:xenial image pour installer le curl dépendance, ajoute tests.sh au système de fichiers image, et indique le CMD commande qui exécute le script de test avec Bash.

Une fois nos tests dockerisés, ils peuvent être exécutés de manière reproductible et agnostique.

L'étape suivante consiste à lier notre conteneur de test à notre application "Hello World". C'est là que Docker Compose vient à nouveau à la rescousse. Modifier 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 le principal web demande et ses redis dépendance de la même manière que la précédente docker-compose.yml dossier. Il s'agit de la partie du fichier qui spécifie le web et redis conteneurs. La seule différence est que le web conteneur n'expose plus le port 80, l'application ne sera donc 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 que dans le déploiement en direct.

La docker-compose.test.yml fichier définit également un sut conteneur (du nom de system under tests) responsable de l'exécution de nos tests d'intégration. La sut conteneur spécifie le répertoire courant comme notre build répertoire et précise notre Dockerfile.test dossier. Il fait le lien avec le web conteneur afin que l'adresse IP du conteneur d'application soit accessible à notre test.sh scénario.

Comment personnaliser pour votre propre application

Notez que docker-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 Dockerfiles supplémentaires pour eux, similaires au Dockerfile.test fichier présenté ci-dessus.

Ensuite, vous pouvez ajouter des conteneurs supplémentaires sous le sut conteneur dans le docker-compose.test.yml fichier, faisant référence aux Dockerfiles supplémentaires.

Étape 6 - Testez l'application "Hello World"

Enfin, en étendant les idées de Docker des environnements locaux aux environnements de test, nous avons 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 construit les images locales nécessaires à docker-compose.test.yml. Notez que nous utilisons -f pointer vers docker-compose.test.yml et -p pour indiquer un nom de projet spécifique.

Lancez maintenant 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 sut conteneur en exécutant :

docker logs -f ci_sut_1

Production

  % 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 sut conteneur pour vérifier si vos tests ont réussi :

docker wait ci_sut_1

Production

0

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

Notez que d'autres outils 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 exécuté avec succès notre test dans un environnement fraîchement construit identique à notre environnement de production.

Conclusion

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

En particulier, les avantages de l'utilisation de la docker-compose.test.yml fichier à tester sont que le processus de test est :

  • Automatable : la façon dont un outil exécute la docker-compose.test.yml est indépendant de l'application testée
  • Léger : des centaines de services externes peuvent être déployés sur un seul hôte, simulant des environnements de test complexes (d'intégration)
  • Agnostique : é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
  • Immuable : les tests passant sur votre machine locale passeront dans votre outil CI

Ce didacticiel montre un exemple de test d'une simple application "Hello World".

Il est maintenant temps d'utiliser vos propres fichiers d'application, de dockeriser vos propres scripts de test d'application et de créer les vôtres docker-compose.test.yml pour tester votre application dans un environnement frais et immuable.