Cette section détaille mon implémentation pour la première partie du projet, qui consistait à conteneuriser deux microservices, l'un en Spring Boot (Java) et l'autre en Flask (Python), puis les faire fonctionner ensemble via Docker Compose.
Le projet est organisé selon l'arborescence suivante :
.
├── .dockerignore
├── .gitignore
├── .gitlab-ci.yml
├── docker-compose.yml
├── service-java
│ ├── Dockerfile
│ ├── pom.xml
│ └── src/
├── service-python
│ ├── app.py
│ ├── Dockerfile
│ ├── requirements.txt
│ └── test_app.py
└── terraform/
Pour le service Flask, j'ai créé un Dockerfile qui :
- utilise une image python 3.9 légère pour optimiser la taille de l'image au mieux
- installe les dépendances via pip install -r requirements.txt
- exécute les tests avant de démarrer l'application
- expose le port 5000
Pour le service Spring, j'ai créé un Dockerfile qui :
- implémente une approche multi-stage pour optimiser la taille de l'image finale
- utilise une image maven complète lors de la compilation pour être sûr que les étapes clean, compile, test et package puissent toutes passer sans souci de dépendances
- n'inclut que le JAR final dans l'étape d'exécution
- expose le port 8080
Quant à Docker Compose, j'ai créé un fichier qui :
- établit 2 services à part pour spring et flask
- ces services sont connectés au même réseau Docker afin de pouvoir communiquer
- le service spring dépend du service flask qui permet à flask de démarrer avant
Pour construire l'image Flask :
cd service-python
docker build -t flask-service:latest .
Pour construire l'image Spring :
cd ../service-java
docker build -t spring-service:latest .
Pour exécuter le service Flask :
docker run -d -p 5000:5000 --name flask-app flask-service:latest
Pour exécuter le service Spring en le connectant au service Flask :
docker run -d -p 8080:8080 -e FLASK_URL=http://flask-app:5000/ --link flask-app --name spring-boot-app spring-service:latest
Pour lancer les services :
docker-compose up -d
Pour arrêter les services :
docker-compose down
Une fois que les services sont démarrés:
Le service Python est accessible à l'adresse http://localhost:5000/api/message
Il devrait retourner {"message": "Hello from Flask!"}
Le service Java est accessible à l'adresse http://localhost:8080/proxy
Il devrait afficher Hello from Flask! si le service Python est accessible.
Il affichera Service Flask injoignable ! si le service Python est hors ligne
Cette section détaille l'implémentation d'un pipeline CI (Continuous Integration) pour la partie 2 du projet. L'objectif était de créer un pipeline automatisé qui compile et teste les applications Java (Spring Boot) et Python (Flask) à chaque modification du code et commit sur le dépôt.
Le pipeline a été configuré dans le fichier .gitlab-ci.yml et comprend deux étapes principales :
- Compilation (compile): Prépare et compile les applications
- Test (test): Exécute les tests unitaires sur les applications
Ces étapes sont exécutées dans l'ordre, garantissant que le code est d'abord compilé avant d'être testé.
Début du Pipeline
→ Stage: Compile
→ [Job: java-compile, Job: python-compile]
→ Stage: Test
→ [Job: java-test, Job: python-test]
Fin du Pipeline
Job java-compile
-
Image : maven:3.8-openjdk-17
-
Actions :
- Se positionne dans le répertoire service-java
- Exécute mvn compile pour compiler le code Java -
Artefacts : Conserve le contenu du dossier service-java/target/ pendant 1 heure pour être utilisé par les jobs suivants
Job python-compile
-
Image : python:3.9-slim
-
Actions préalables : Met à jour pip pour garantir la compatibilité
-
Actions principales :
- Se positionne dans le répertoire service-python
- Installe les dépendances listées dans requirements.txt -
Artefacts : Conserve le contenu du dossier service-python/ pendant 1 heure
Job java-test
Image : maven:3.8-openjdk-17
Dépendances : Dépend du job java-compile pour récupérer le code compilé
Actions :
- Se positionne dans le répertoire service-java
- Exécute mvn test pour lancer les tests unitaires
Job python-test
Image : python:3.9-slim
Dépendances : Dépend du job python-compile pour récupérer le code compilé
Actions préalables:
- Se positionne dans le répertoire service-python
- Réinstalle les dépendances nécessaires
Actions principales : Exécute python test_app.py pour lancer les tests unitaires
Le pipeline utilise des dépendances explicites pour garantir l'ordre d'exécution et transférer les résultats entre les jobs :
- Le job java-test dépend de java-compile
- Le job python-test dépend de python-compile
Ces dépendances sont définies par le mot-clé dependencies et permettent aux jobs de test d'accéder aux artefacts générés lors de la compilation.
Les artefacts sont des fichiers générés par un job et qui peuvent être utilisés par les jobs suivants :
- Le job java-compile génère des artefacts dans le dossier service-java/target/ (classes compilées)
- Le job python-compile génère des artefacts dans le dossier service-python/ (code source et dépendances)
Ces artefacts sont conservés pendant 1 heure (expire_in: 1 hour) pour ne pas saturer le serveur.
Cette section détaille la créatoin de l'infrastructure Azure à l'aide de Terraform. L'objectif était de créer une infrastructure cloud capable d'héberger les deux microservices (Java Spring Boot et Python Flask) conteneurisés.
-
main.tf définit toutes les ressources Azure nécessaires
- Un registre de conteneurs (ACR) est créé pour stocker les images Docker
- Une identité managée est créée et reçoit le rôle AcrPull pour accéder au registre
- Un plan de service App Service est créé avec un SKU B1 (Basic)
- Deux applications web Linux sont créées pour exécuter les conteneurs Java et Python
- Les applications sont configurées pour utiliser les images Docker du registre ACR -
variables.tf définit les variables utilisées dans la configuration
-
outputs.tf définit les informations à afficher à la fin de la création de l'infrastructure
-
providers.tf configure la connexion au service azure
- az login
- az acr login --name devopsscenarioname62727
- $env:ARM_CLIENT_ID=___
- $env:ARM_CLIENT_SECRET=___
- $env:ARM_SUBSCRIPTION_ID=___
- $env:ARM_TENANT_ID=___
- cd ./terraform/
- terraform destroy --auto-approve (et/ou supprimer sur le portal)
- cd ../service-java/
- docker build -t devopsscenarioname62727.azurecr.io/java-service:latest .
- cd ../service-python/
- docker build -t devopsscenarioname62727.azurecr.io/python-service:latest .
- cd terraform
- terraform init
- terraform validate
- terraform plan
- terraform apply --auto-approve
- docker push devopsscenarioname62727.azurecr.io/java-service:latest
- docker push devopsscenarioname62727.azurecr.io/python-service:latest
- Via les urls données par les outputs terraform :
- https://devopsscenarioapp62727-python.azurewebsites.net/api/message → Résultat attendu: {"message":"Hello from Flask!"}
- https://devopsscenarioapp62727.azurewebsites.net/proxy
→ Résultat attendu: {"message":"Hello from Flask!"}
- Via des commandes curl :
- curl https://devopsscenarioapp62727-python.azurewebsites.net/api/message
→ Résultat attendu: {"message":"Hello from Flask!"} - curl https://devopsscenarioapp62727.azurewebsites.net/proxy
→ Résultat attendu: Hello from Flask!
Pour économiser nos crédits Azure, il est utile de détuire l'infrastructure lorsqu'elle n'est plus nécessaire :
- cd ./terraform/
- terraform destroy --auto-approve
Cette section détaille l'automatisation du déploiement dans le pipeline CI/CD sur Azure. L'objectif était d'automatiser le déploiement des microservices sur l'infrastructure Azure.
Pour ce faire, j'ai étendu la pipeline CI/CD dans le fichier .gitlab-ci.yml en ajoutant deux nouvelles étapes/stages :
- infrastructure : déploye l'infrastructure azure avec terraform
- deploy : construit et déploye les images Docker sur l'infrastructure Azure
Il faut bien configurer docker-in-docker avec son runner au préalable, voici les commandes utilisées pour en recréer from scratch après avoir supprimé le précédent :
docker run -d --name gitlab-runner-scenario-dockers --restart always -v C:\Users\jasha\Desktop\62727\gitlab-runner\config:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock gitlab/gitlab-runner:latest
docker run --rm -v C:\Users\jasha\Desktop\62727\gitlab-runner\config:/etc/gitlab-runner gitlab/gitlab-runner register --non-interactive --url "https://git.esi-bru.be" --token $env:RUNNER_TOKEN --executor "docker" --docker-image alpine:latest --description "docker-runner" --docker-volumes /var/run/docker.sock:/var/run/docker.sock --docker-privileged
-
Image : Utilise l'image officielle de Terraform, avec une configuration personnalisée de l'entrypoint pour éviter les conflits avec GitLab CI.
-
Variables d'environnement : Configure les variables d'authentification Azure nécessaires pour que Terraform puisse accéder à Azure. Ces variables sont récupérées depuis les variables masquées et sécurisées du projet GitLab.
-
Commandes Terraform:
terraform init : Initialise le répertoire de travail
terraform validate : Vérifie la syntaxe des fichiers de configuration
terraform plan : Crée un plan d'exécution
terraform apply --auto-approve : Applique le plan sans demander de confirmation
- Artefacts : Sauvegarde le fichier d'état Terraform (terraform.tfstate) qui contient des informations sur l'infrastructure déployée. Ce fichier peut être utile pour les opérations ultérieures et est conservé pendant une semaine.
Le job échouera sûrement à première vue car terraform aura créer une nouvelle infrastructure qui possède un nouveau password, il convient donc de modifier la valeur de la variable gitlab dédiée avant de relancer le job qui devrait alors réussir.
Voici les commandes à suivre pour retrouver le nouveau password :
- az login
- az acr login --name devopsscenarioname62727
- az acr credential show --name devopsscenarioname62727
- Image et services :
- Utilise l'image docker:20.10.16 comme base
- Configure Docker-in-Docker (DinD) via le service docker:20.10.16-dind, permettant d'exécuter Docker à l'intérieur d'un conteneur Docker
- Variables :
- DOCKER_TLS_CERTDIR: Configuration nécessaire pour sécuriser la communication avec le démon Docker
- ACR_NAME: Nom du registre Azure Container Registry
- JAVA_APP_NAME et PYTHON_APP_NAME: Noms des applications déployées
- Dépendances :
- java-compile et python-compile : Récupère le code compilé
- deploy-terraform : S'assure que l'infrastructure est déployée avant le déploiement des applications
- Scripts au préalable :
Connexion au registre Docker Azure en utilisant les identifiants stockés dans les variables sécurisées de GitLab
- Construction et déploiement :
- Construction des images Docker pour les services Java et Python
- Push des images vers le registre Azure Container Registry
- L'infrastructure déployée précédemment est configurée pour utiliser ces images
- Outputs :
Affichage des URLs des applications déployées pour faciliter la vérification
- Via les urls données par les outputs :
- https://devopsscenarioapp62727-python.azurewebsites.net/api/message → Résultat attendu: {"message":"Hello from Flask!"}
- https://devopsscenarioapp62727.azurewebsites.net/proxy
→ Résultat attendu: {"message":"Hello from Flask!"}
- Via des commandes curl :
- curl https://devopsscenarioapp62727-python.azurewebsites.net/api/message
→ Résultat attendu: {"message":"Hello from Flask!"} - curl https://devopsscenarioapp62727.azurewebsites.net/proxy
→ Résultat attendu: Hello from Flask!
Pour économiser nos crédits Azure, il est utile de détuire l'infrastructure lorsqu'elle n'est plus nécessaire.
Cependant, le shell n'a pas les fichiers de configuration terraform à jour car le déploiement a été effectuée dans la pipeline. Il faut donc aller manuellement supprimer le groupe de ressources sur le portail Azure.