Introduction
Dans la section précédente, nous avons découvert les concepts de :
- Variables d'environnement prédéfinies
- L'utilisation de cache
- Des déploiements par environnement et comment créer un flux de production
Dans cette section, nous allons essayer de reproduire le schéma suivant :
Il s'agit d'un processus plus complexe, mais nous sommes proches de réaliser un processus proche de la réalité. Pour cela, nous allons changer de langage de programmation pour créer une application plus complexe que nous déploierons sur AWS. Êtes-vous prêt pour ce défi ?
Installation
Pour commencer, voici les étapes pour installer IntelliJ, Java et Gradle :
- Pour installer IntelliJ, il suffit de télécharger le fichier d'installation correspondant à votre système d'exploitation depuis le site officiel et de suivre les instructions d'installation : jetbrains
- Pour installer Java, il est recommandé de télécharger et d'installer la version correspondant à votre système d'exploitation depuis le site officiel : java
- Pour installer Gradle, vous pouvez suivre les instructions d'installation disponibles sur le site officiel : gradle
Une fois ces étapes terminées, nous pouvons commencer.
Contexte de l'application
Gitlab branch: Java-AWS/init
Pour accéder au projet Java, vous pouvez vous rendre sur la branche Java-AWS/init
. Je vous recommande de "forker" le projet pour plus de confort. Une fois cela fait, vous pouvez lancer l'application directement depuis IntelliJ. En cas d'erreur, vous pouvez exécuter la commande suivante :
mvn clean install
Ce projet est une API, donc il n'y a pas d'interface utilisateur. Pour tester notre API, vous pouvez utiliser Postman. Tous les appels possibles sont disponibles dans le repository. En important cette collection, vous devriez trouver l'ensemble des endpoints que notre API permet d'exposer.
Dans cette section, nous allons nous concentrer sur la création du job de build pour notre application Java. Cette étape est essentielle, car elle permet de transformer le code source en langage machine.
Étape de build
GitLab Branch: Java-AWS/build
Le résultat du job de build est un fichier "JAR", qui sera ensuite interprété par le serveur. Pour cette formation, nous allons utiliser Gradle, mais vous pouvez également utiliser Maven si vous préférez.
Voici les étapes nécessaires pour la création du job de build ::
- Une image Docker pour exécuter Java
- Lancer un build avec Gradle
- Sauvegarder notre artifact dans GitLab
stages:
- build
build:
stage: build
image: openjdk:12-alpin
script:
- ./gradlew build
artifacts:
paths:
- ./build/libs/
Test stage
Gitlab branch: Java-AWS/test
Pour s'assurer que l'application fonctionne correctement, nous allons créer des tests de fumée. Dans un premier temps, nous allons effectuer des appels pour vérifier que l'application répond correctement.
Étape de tests de fumée
GitLab Branch: Java-AWS/smoke-tests
Dans cette étape, nous devons :
- Créer un nouveau stage
- Utiliser l'image Alpine par défaut
- Créer un script qui lancera le fichier JAR créé dans l'artifact précédent avec Java
- Attendre quelques instants pour que l'application se lance
- Appeler l'endpoint health pour s'assurer que tout fonctionne correctement
- Créer un before_script pour installer la dépendance permettant d'utiliser "curl"
stages:
- build
- test
build:
stage: build
image: openjdk:12-alpine
script:
- ./gradlew build
artifacts:
paths:
- ./build/libs/
smoke test:
stage: test
image: openjdk:12-alpine
before_script:
- apk --no-cache add curl
script:
- java -jar ./build/libs/cars-api.jar &
- sleep 30
- curl http://localhost:5000/actuator/health | grep "UP"
Deploy to AWS
Gitlab branch: Java-AWS/deploy
Je vous propose dans un premier temps, si vous n'avez pas de compte, de créer votre compte sur AWS. Une fois connecter rendez vous dans "AWS Management Console" :
All Services -> Compute -> Elastic Beanstalk -> Create new Application -> ajouter le nom de l'application et créer cette application Nous allons avoir besoin d'un environnement mais que cela fonctionne : Dans l'application créé -> Environments -> Create one now -> Web server environment -> Environment name (production) && platform (Java) && Sample application Si l'on veut déployer manuellement, nous pouvons directement le faire dans le dashboard et sélectionner notre projet (jar file). Nous allons maintenant faire ce déploiement de manière automatique ! AWS fournit des outils qui permet de nous faciliter la vie, comme "AWS Command Line Interface". C'est ce que l'on va utiliser pour intéragir avec AWS. Un autre soucis c'est que l'on ne peut pas directement intérargir avec Elastic Beanstack, nous allons devoir passé par AWS S3. C'est comme un dropbox pour nos fichiers AWS qui nous permettra de déposer notre fichier compilé.
Pour déployer notre application sur AWS S3, nous devons d'abord créer un bucket S3 sur la console AWS Management :
- Accédez à la console AWS Management.
- Sélectionnez "All Services".
- Accédez à la section "Storage" et sélectionnez "S3".
- Créez un bucket en spécifiant un nom (ex : cars-api-deployments), et cliquez sur "Next" puis sur "Create bucket".
Ensuite, pour configurer GitLab :
- Accédez à "Settings" puis "CI/CD".
- Dans la section "Variables", ajoutez une variable nommée "S3_BUCKET" avec la valeur "cars-api-deployment".
Enfin, pour interagir avec AWS, nous allons utiliser le système de ligne de commande et une image Docker. Voici les étapes à suivre :
- Créez un nouveau stage dans votre fichier de configuration CI/CD.
- Puller l'image Docker AWS.
- Configurez l'image pour qu'elle n'ait pas de point d'entrée.
- Écrivez un script pour déployer l'artifact créé vers le bucket S3 que vous avez créé précédemment.
stages:
- build
- test
- deploy
build:
stage: build
image: openjdk:12-alpine
script:
- ./gradlew build
artifacts:
paths:
- ./build/libs/
smoke test:
stage: test
image: openjdk:12-alpine
before_script:
- apk --no-cache add curl
script:
- java -jar ./build/libs/cars-api.jar &
- sleep 30
- curl http://localhost:5000/actuator/health | grep "UP"
deploy:
stage: deploy
image:
name: amazon/aws-cli
entrypoint: [""]
script:
- aws configure set region us-east-1
- aws s3 cp ./build/libs/cars-api.jar s3://$S3_BUCKET/cars-api.jar
Pour accéder à notre bucket S3, nous devons générer des credentials (identifiants) :
- Accédez à la console AWS Management.
- Sélectionnez "All Services".
- Accédez à la section "IAM" et sélectionnez "Users".
- Cliquez sur "Add user".
- Donnez un nom à l'utilisateur (ex : gitlabci) et sélectionnez "Programmatic access".
- Dans la section "Permissions", cliquez sur "Attach existing policies directly".
- Cherchez "S3" et sélectionnez "AmazonS3FullAccess".
- Cliquez sur "Attach policy".
- Dans la section "Permissions", cliquez à nouveau sur "Attach existing policies directly".
- Cherchez "S3" et sélectionnez "AdminstratorAccess-AWSElasticBeanStalk".
- Cliquez sur "Attach policy".
- Ne pas ajouter de tags et cliquez sur "Create user".
- Enregistrez les informations d'identification (access key ID et secret access key).
Maintenant, nous pouvons ajouter ces credentials dans les variables GitLab :
- Accédez à "Settings" puis "CI/CD".
- Dans la section "Variables", ajoutez une variable nommée "AWS_ACCESS_KEY_ID" avec la valeur de l'access key ID enregistré précédemment.
- Ajoutez une autre variable nommée "AWS_SECRET_ACCESS_KEY" avec la valeur du secret access key enregistré précédemment.
Maintenant que nous avons créé notre pipeline pour déployer notre application sur S3, nous avons besoin de la déployer sur notre serveur en utilisant une application-version dans AWS. Cela garantira la cohérence de nos déploiements et nous permettra de disposer d'un historique des versions déployées.
Voici les étapes à suivre :
- Créez des variables locales pour le nom de l'artifact et le nom de l'application.
- Utilisez la variable prédéfinie $CI_PIPELINE_IID pour créer un identifiant unique pour chaque version.
- Créez une application-version en utilisant le nom de l'application et l'identifiant unique créé précédemment.
- Mettez à jour l'environnement en précisant la version que vous venez de créer.
Ces étapes vous permettront de déployer votre application de manière cohérente et traçable sur AWS.
variables:
ARTIFACT_NAME: cars-api-v$CI_PIPELINE_IID.jar
APP_NAME: cars-api
deploy:
stage: deploy
image:
name: amazon/aws-cli
entrypoint: [""]
script:
- aws configure set region us-east-1
- aws s3 cp ./build/libs/$ARTIFACT_NAME s3://$S3_BUCKET/$ARTIFACT_NAME
- aws elasticbeanstalk create-application-version --application-name $APP_NAME --version-label $CI_PIPELINE_IID --source-bundle S3Bucket=$S3_BUCKET,S3Key=$ARTIFACT_NAME
- aws elasticbeanstalk update-environment --application-name $APP_NAME --environment-name "production" --version-label=$CI_PIPELINE_IID
Maintenant que tout fonctionne comme prévu, vous pouvez améliorer votre code pour qu'il fournisse des informations utiles telles que :
- L'ID de la pipeline
- L'ID du commit
- Le nom de la branche
Ces informations vous aideront à mieux comprendre et à tracer vos déploiements. Voici comment les ajouter :
- Utilisez la variable prédéfinie $CI_PIPELINE_IID pour obtenir l'ID de la pipeline.
- Utilisez la variable prédéfinie $CI_COMMIT_SHA pour obtenir l'ID du commit.
- Utilisez la variable prédéfinie $CI_COMMIT_REF_NAME pour obtenir le nom de la branche.
Vous pouvez ensuite afficher ces informations dans les journaux de votre pipeline pour les rendre plus facilement accessibles. En ajoutant ces informations, vous pourrez mieux suivre vos déploiements et identifier les problèmes plus rapidement.
build:
stage: build
image: openjdk:12-alpine
script:
- sed -i "s/CI_PIPELINE_IID/$CI_PIPELINE_IID/" ./src/main/resources/application.yml
- sed -i "s/CI_COMMIT_SHORT_SHA/$CI_COMMIT_SHORT_SHA/" ./src/main/resources/application.yml
- sed -i "s/CI_COMMIT_BRANCH/$CI_COMMIT_BRANCH/" ./src/main/resources/application.yml
- ./gradlew build
- mv ./build/libs/cars-api.jar ./build/libs/$ARTIFACT_NAME
artifacts:
paths:
- ./build/libs/
Maintenant que votre application est déployée sur AWS, vous pouvez utiliser GitLab pour vérifier que la version déployée est bien celle que vous avez choisie. Voici les étapes à suivre :
- Installez la dépendance CURL pour effectuer des appels API.
- Installez la dépendance JQ pour extraire facilement des informations d'un fichier JSON.
- Utilisez CURL pour effectuer un appel à l'application AWS et vérifier que la version déployée est bien celle que vous avez choisie.
- Utilisez JQ pour extraire les données de CNAME du résultat JSON.
- Vérifiez que l'application est bien en train de tourner sur AWS.
deploy:
stage: deploy
image:
name: amazon/aws-cli
entrypoint: [""]
before_script:
- apk --no-cache add curl
- apk --no-cache add jq
script:
- aws configure set region us-east-1
- aws s3 cp ./build/libs/$ARTIFACT_NAME s3://$S3_BUCKET/$ARTIFACT_NAME
- aws elasticbeanstalk create-application-version --application-name $APP_NAME --version-label $CI_PIPELINE_IID --source-bundle S3Bucket=$S3_BUCKET,S3Key=$ARTIFACT_NAME
- CNAME=$(aws elasticbeanstalk update-environment --application-name $APP_NAME --environment-name "production" --version-label=$CI_PIPELINE_IID | jq '.CNAME' --raw-output)
- sleep 45
- curl http://$CNAME/actuator/info | grep $CI_PIPELINE_IID
Code quality
Gitlab branch: Java-AWS/quality
Lorsque plusieurs développeurs travaillent ensemble sur un projet, il est important de s'assurer que les bonnes pratiques et les guidelines sont respectées, ainsi que le formattage du code. Pour cela, nous allons utiliser PMD, un outil d'analyse de code.
Si PMD n'est pas déjà installé dans votre projet, vous pouvez l'installer en suivant les instructions de votre système de gestion de paquets ou en le téléchargeant directement depuis le site web de PMD.
Une fois PMD installé, voici comment l'utiliser :
- Ouvrez un terminal de commande dans le projet.
- Exécutez la commande PMD pour lancer l'analyse de code.
- Analysez les résultats de PMD pour identifier les erreurs et les violations de guidelines.
- Corrigez les erreurs et les violations de guidelines pour améliorer la qualité de votre code.
En utilisant PMD, vous pourrez vous assurer que votre code respecte les bonnes pratiques et les guidelines, ce qui facilitera la collaboration entre les développeurs et améliorera la qualité du code de votre projet.
./gradlew pmdMain pmdTest
Pour utiliser PMD et appliquer des règles de qualité de code à votre projet, vous pouvez ajouter les règles dans le fichier "pmd-ruleset.xml" qui se trouve à la racine du projet. Pour automatiser cette vérification, vous pouvez créer un nouveau job dans votre pipeline.
Voici comment procéder :
- Créez un nouveau job dans votre fichier de configuration CI/CD.
- Liez-le au stage de test pour vérifier la qualité du code avant le déploiement.
- Écrivez un script qui exécute la commande PMD que vous avez utilisée précédemment.
- Enregistrez le résultat de PMD sous forme d'artifact pour pouvoir le consulter facilement en cas d'erreur de code.
En ajoutant ce job à votre pipeline, vous pourrez automatiser la vérification de la qualité de votre code à chaque fois que vous lancez un déploiement. Cela vous permettra de détecter rapidement les erreurs et les violations de guidelines, et de les corriger avant de déployer votre application.
code quality:
stage: test
image: openjdk:12-alpine
script:
- ./gradlew pmdMain pmdTest
artifacts:
when: always
paths:
- build/reports/pmd
Unit test stage
Gitlab branch: Java-AWS/unit-test
Dans votre application, vous avez déjà des tests unitaires qui garantissent que de nouvelles implémentations ne cassent pas les anciennes fonctionnalités. Pour intégrer cette contrainte dans votre pipeline de déploiement, vous pouvez ajouter un nouveau job pour exécuter les tests unitaires.
Voici comment procéder :
- Créez un nouveau job dans votre fichier de configuration CI/CD.
- Liez-le au stage de test pour exécuter les tests avant le déploiement.
- Utilisez Gradle (ou un autre outil) pour exécuter les tests unitaires.
- Enregistrez le rapport de tests sous forme d'artifact pour pouvoir le consulter facilement.
unit tests:
stage: test
image: openjdk:12-alpine
script:
- ./gradlew test
artifacts:
when: always
paths:
- build/reports/tests
reports:
junit: build/test-results/test/*.xml
API test stage
Gitlab branch: Java-AWS/api-test-stage
Pour assurer le bon fonctionnement de vos endpoints, vous pouvez ajouter des tests fonctionnels à votre pipeline de déploiement. Voici comment procéder :
- Créez un fichier JSON avec les données que vous souhaitez utiliser pour tester vos endpoints.
- Ajoutez ce fichier à votre projet, par exemple dans le dossier de tests.
- Créez un nouveau job dans votre fichier de configuration CI/CD.
- Liez-le au stage de test pour exécuter les tests avant le déploiement.
- Dans ce job, lancez votre application et utilisez Postman (ou un autre outil) pour envoyer des requêtes à vos endpoints.
- Vérifiez que les résultats retournés par vos endpoints sont bien ceux que vous attendez.
- Enregistrez les résultats des tests sous forme d'artifact pour pouvoir les consulter facilement.
{
"info":{
"_postman_id":"e4a42f44-a58f-4bfa-a5d0-5557a0a36247",
"name":"Cars API",
"schema":"https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item":[
{
"name":"CRUD",
"item":[
{
"name":"Get all cars",
"event":[
{
"listen":"test",
"script":{
"id":"838290a6-f7c6-417d-a387-7df2d2c719f4",
"exec":[
"pm.test(\"Status code is 200\", function () {",
" pm.response.to.have.status(200);",
"});"
],
"type":"text/javascript"
}
}
],
"request":{
"method":"GET",
"header":[
],
"url":{
"raw":"{{baseUrl}}/cars",
"host":[
"{{baseUrl}}"
],
"path":[
"cars"
]
}
},
"response":[
]
},
{
"name":"Add car",
"request":{
"method":"POST",
"header":[
{
"key":"Content-Type",
"name":"Content-Type",
"value":"application/json",
"type":"text"
}
],
"body":{
"mode":"raw",
"raw":"{\n \"manufacturer\": \"Dacia\",\n \"model\": \"Logan\",\n \"build\": 2000\n}",
"options":{
"raw":{
"language":"json"
}
}
},
"url":{
"raw":"{{baseUrl}}/cars",
"host":[
"{{baseUrl}}"
],
"path":[
"cars"
]
}
},
"response":[
]
},
{
"name":"Get single car",
"event":[
{
"listen":"test",
"script":{
"id":"907cccdc-8da6-426d-9bef-2a618a44e8e2",
"exec":[
"pm.test(\"Status code is 200\", function () {",
" pm.response.to.have.status(200);",
"});"
],
"type":"text/javascript"
}
}
],
"request":{
"method":"GET",
"header":[
],
"url":{
"raw":"{{baseUrl}}/cars/4",
"host":[
"{{baseUrl}}"
],
"path":[
"cars",
"4"
]
}
},
"response":[
]
},
{
"name":"Delete car",
"request":{
"method":"DELETE",
"header":[
],
"url":{
"raw":"{{baseUrl}}/cars/1",
"host":[
"{{baseUrl}}"
],
"path":[
"cars",
"1"
]
}
},
"response":[
]
}
],
"protocolProfileBehavior":{
}
},
{
"name":"Statistics",
"item":[
{
"name":"Average fleet age",
"event":[
{
"listen":"test",
"script":{
"id":"79b19feb-1129-451d-8d19-f8a1a512e5dd",
"exec":[
"pm.test(\"Status code is 200\", function () {",
" pm.response.to.have.status(200);",
"});"
],
"type":"text/javascript"
}
}
],
"request":{
"method":"GET",
"header":[
],
"url":{
"raw":"{{baseUrl}}/statistics/age",
"host":[
"{{baseUrl}}"
],
"path":[
"statistics",
"age"
]
}
},
"response":[
]
}
],
"protocolProfileBehavior":{
}
},
{
"name":"Health",
"item":[
{
"name":"Health",
"event":[
{
"listen":"test",
"script":{
"id":"1743a75d-932a-4e2d-b5d4-cf5b98c3c7f1",
"exec":[
"pm.test(\"Status code is 200\", function () {",
" pm.response.to.have.status(200);",
"});"
],
"type":"text/javascript"
}
}
],
"request":{
"method":"GET",
"header":[
],
"url":{
"raw":"{{baseUrl}}/actuator/health",
"host":[
"{{baseUrl}}"
],
"path":[
"actuator",
"health"
]
}
},
"response":[
]
},
{
"name":"Info",
"event":[
{
"listen":"test",
"script":{
"id":"72e253db-70d1-4fce-9d7f-a3647081b16f",
"exec":[
"pm.test(\"Status code is 200\", function () {",
" pm.response.to.have.status(200);",
"});"
],
"type":"text/javascript"
}
}
],
"request":{
"method":"GET",
"header":[
],
"url":{
"raw":"{{baseUrl}}/actuator/info",
"host":[
"{{baseUrl}}"
],
"path":[
"actuator",
"info"
]
}
},
"response":[
]
}
],
"protocolProfileBehavior":{
}
}
],
"protocolProfileBehavior":{
}
}
Maintenant que vous avez créé des tests fonctionnels pour vos endpoints, vous pouvez ajouter un nouveau stage à votre pipeline pour les exécuter. Voici comment procéder :
- Ajoutez un nouveau stage à votre fichier de configuration CI/CD, par exemple "api testing".
- Créez un nouveau job dans ce stage pour exécuter les tests fonctionnels.
- Importez Newman (ou un autre outil) pour lancer Postman.
- Fixez la version de Newman que vous voulez utiliser pour vous assurer de ne pas être contraint par de nouvelles images.
- Lancez votre collection de tests à l'aide de Newman.
- Enregistrez les résultats de Newman sous forme d'artifact pour pouvoir les consulter facilement.
stages:
- build
- test
- deploy
- api testing
api testing:
stage: post deploy
image:
name: vdespa/newman
entrypoint: [""]
script:
- newman --version
- newman run "Cars API.postman_collection.json" --environment Production.postman_environment.json --reporters cli,htmlextra,junit --reporter-htmlextra-export "newman/report.html" --reporter-junit-export "newman/report.xml"
artifacts:
when: always
paths:
- newman/
reports:
junit: newman/report.xml