Création d’une application ReactJS avec API SpringBoot, avec création et déploiement sur AWS (S3, CloudFront, EC2, ECS, RDS, ELB) et CI/CD GitLab : Tuto partie 4 - mettre en place le CI/CD sur GitLab

On vous explique la création de A à Z d’une application ReactJS avec API SpringBoot, avec création et déploiement sur AWS (S3, CloudFront, EC2, ECS, RDS, ELB) et CI/CD GitLab - 4/4
Arthur.jpg
Arthur COMBE, JavaScript loverMis à jour le 12 Oct 2021
Tuto Création d’une application ReactJS avec API SpringBoot

Dans cette série d’articles, nous allons mettre en place une application de A à Z.

  • Étape 1 - Nous avons commencé par la création d’une simple application ReactJS, puis de son API en Spring Boot, avec une base de données PostgreSQL.
  • Étape 2 - Nous avons ensuite détaillé toutes les étapes pour créer l'infrastructure front
  • Étape 3 - Ensuite nous avons créé l'infrastructure back
  • Étape 4 - Enfin, nous finissons par des scripts de déploiement automatisés pour GitLab, afin de permettre de mettre à jour rapidement son application sur AWS.

Avec ça, vous aurez toutes les étapes nécessaires pour créer des applications prêtes à l’emploi, 100% fonctionnelles et facilement maintenable !

Partie 4 - Mise en place du CI/CD sur GitLab

Pour terminer en beauté, nous allons voir ensemble comment mettre en place un déploiement automatisé sur GitLabGitLab, c’est une plateforme permettant d’héberger et de gérer des projets web de A à Z., en utilisant des dockers. Avec ça, vous allez pouvoir déployer de nouvelles versions de votre application en 1 clic, sans aucune commande à exécuter !

Comme son nom l'indique, il faut donc que votre projet soit sur GitLab, vous pouvez donc vous créer un compte si ce n'est pas déjà fait, et push le front et le back sur des nouveaux projets.

1 - Déploiement automatisé de l'interface

Pour commencer, il faut créer un fichier .gitlab-ci.yml, à la racine de votre projet. C'est dans ce fichier que nous allons définir la pipeline de déploiement.

Dans ce .gitlab-ci.yml, nous allons commencer par un peu de charabia.

.on_tag_release: &on_tag_release
  only:
    - tags

cache:
  paths:
    - node_modules/

Traduction : le premier bloc est une sorte de "fonction" yml, que nous pourrons injecter plus tard dans nos stages.
Le deuxième bloc quant à lui, sert à mettre en cache node_modules, ce qui évitera de télécharger tous les packages à chaque déploiement.

Puis, nous allons définir nos stages, ce qui correspond au déroulement de notre déploiement.
Cela va donc se passer en 2 étapes : build, puis déploiement sur AWSLe Cloud AWS (Amazon WebServices) est une plateforme de services cloud développée par le géant américain Amazon. avec invalidation du CloudFront.

stages:
  - build
  - deploy-aws

Nous allons ensuite définir des variables, ce qui sera plus pratique si des modifications sont à prévoir.
Remplacez bien <AWS_KEY> par la clé d'accès de votre compte robot, <AWS_SECRET> par son secret, et <DISTRIBUTION_ID> par l'ID de votre distribution CloudFront.

variables:
    APP_BUILD_DIRECTORY: "build"
    AWS_ACCESS_KEY_ID: "<AWS_KEY>"
    AWS_SECRET_ACCESS_KEY: "<AWS_SECRET>"
    AWS_REGION: "eu-west-1"
    AWS_BUCKET_NAME: "axo-generator"
    AWS_DISTRIBUTION_ID: "<DISTRIBUTION_ID>"

Il est maintenant temps de définir les stages, à commencer par le build.

build:
  <<: *on_tag_release
  tags: 
    - docker
  image: node:10-alpine
  stage: build
  script:
    - npm i
    - npm run build:prod
  artifacts:
    paths:
      - $APP_BUILD_DIRECTORY
    expire_in: 15 minute

Dans ce morceau de code nous avons donc :

  • << : permet d'injecter le bloc de yml que nous avons défini tout en haut.
  • tags : définit le fait que le stage sera executé sur un Docker.
  • image : l'image Docker qui sera utilisée
  • stage: doit correspondre au stage défini dans stages plus haut.
  • script : les commandes (en shell) à executer. Nous avons donc ici tout simplement une installation des packages, puis un build de l'application.
  • artifacts : les artifacts sont des objets à passer d'un stage à l'autre. Ici nous mettons à disposition le dossier build, pendant 15 minutes.

Passons maintenant au déploiement, avec deploy-aws.

deploy-aws:
  <<: *on_tag_release
  tags: 
    - docker
  image: mikesir87/aws-cli
  stage: deploy-aws
  script:
    - aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID
    - aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY
    - aws configure set default.region $AWS_REGION
    - aws s3 sync --delete $APP_BUILD_DIRECTORY/ s3://$AWS_BUCKET_NAME/ # Sync files to S3
    - aws cloudfront create-invalidation --distribution-id $AWS_DISTRIBUTION_ID --paths "/*"

Même principe que le premier, avec cette fois une image différente, car nous avons besoin d'un environnement avec un CLI AWS. Côté scripts, nous commençons par configurer AWS, pour ensuite envoyer le contenu du dossier de build dans le bucket S3.
On finit avec la création d'une invalidation du cache CloudFront, grâce à l'ID de la distribution.

Votre script est prêt ! Il ne vous reste plus qu'à créer un tag depuis GitLab pour que le déploiement se lance automatiquement.
Il est également possible de suivre l'avancement de votre pipeline dans le menu CI/CD sur votre gauche.

2 - Déploiement automatisé de l'API back

Même principe que pour le chapitre d'avant, on commence par créer un fichier .gitlab-ci.yml à la racine du projet (APIUne API est un programme permettant à deux applications distinctes de communiquer entre elles et d’échanger des données. cette fois).

Presque même démarrage, mais sans le cache !

.on_tag_release: &on_tag_release
  only:
    - tags

Puis définitions des stages.
Nous en aurons 4 cette fois-ci : build, connexion à AWS, création de l'image docker puis déploiement sur ECR.

stages:
  - build
  - ecr-credentials
  - dockerisation
  - deploy-aws

Ensuite les variables.
Comme tout à l'heure, pensez bien à remplacer <AWS_KEY> par la clé d'accès de votre compte robot, <AWS_SECRET> par son secret, <AWS_ECR> le nom de votre ECR, et <AWS_ACCOUNT_ID> par l'ID de votre comte AWS (que vous pouvez trouver ici).

variables:
  AWS_ACCESS_KEY_ID: "<AWS_KEY>"
  AWS_SECRET_ACCESS_KEY: "<AWS_SECRET>"
  AWS_ACCOUNT_ID: "<AWS_ACCOUNT_ID>"
  AWS_REGION: "eu-west-1"
  AWS_ECR: "<AWS_ECR>"

Puis vient la définition des stages, en commençant à nouveau par build.

build:
  <<: *on_tag_release
  tags:
    - docker
  image: gradle:6.9-jdk11
  stage: build
  script:
    - gradle build -x test
  artifacts:
    paths:
      - build/libs/*.jar
    expire_in: 15 minutes

On voit donc juste le build de gradle sur un image gradle, rien de bien compliqué jusque-là.

On arrive à la partie un peu bizarre : la récupération des crédentials de ECR.
"Mais pourquoi ne la faisons-nous pas avec le déploiement ?" me diriez-vous ? Et vous auriez raison !

Et bien car malheureusement, l'un des problèmes du déploiement sur des images Docker, et que chaque image n'a qu'une seule fonctionnalité.
Or, nous avons besoin d'un mix entre le CLI AWS, et les commandes de docker.
Nous sommes donc obligé de valser entre les stages et les images afin de tout faire dans le bon ordre.

Et c'est également pour ça que nous allons créer un bloc spécial pour la configuration AWS, que nous utiliserons plus tard.

.aws_config:
  before_script:
    - aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID
    - aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY
    - aws configure set default.region $AWS_REGION

Nous pourrons utiliser ce code à n'importe quel stage, grâce à des extends, ce qui nous permettra de ne pas avoir à réécrire ce morceau plusieurs fois.

Voici donc le fameux stage maudit, qui nous permet de récupérer les crédentials de l'ECR pour ensuite les utiliser à l'étape d'après pour se connecter depuis Docker.

ecr-credentials:
  <<: *on_tag_release
  tags:
    - docker
  image: mikesir87/aws-cli
  stage: ecr-credentials
  extends: .aws_config
  script:
    - aws ecr get-login-password > aws_pass
  artifacts:
    paths:
      - aws_pass
    expire_in: 15 minutes

Nous avons donc créé un fichier aws_pass, qui est maintenant disponible pour les futurs stages de cette pipeline.

Ce qui donnera le prochain stage de dockerisation

dockerisation:
  <<: *on_tag_release
  tags:
    - docker
  image: docker:latest
  stage: dockerisation
  script:
    - docker build -t $AWS_ECR .
    - cat aws_pass | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$AWS_ECR
    - docker tag $AWS_ECR $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$AWS_ECR
    - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$AWS_ECR

Ici nous avons tout simplement repris les commandes de la partie 3 qui nous permettaient de créer l'image Docker. C'est donc ici que nous utilisons le fichier aws_pass, que nous avons dû créer au stage d'avant.

Dernière étape, le déploiement sur AWS.

deploy-aws:
  <<: *on_tag_release
  tags:
    - docker
  image: mikesir87/aws-cli
  stage: deploy-aws
  extends: .aws_config
  script:
    - aws ecs update-service --force-new-deployment --cluster $AWS_ECR --service custom-service --region $AWS_REGION

Nous exécutons juste une commande qui va demander à l'ECS se forcer un nouveau déploiement.
Il va créer une nouvelle tâche, attendre qu'elle soit opérationnelle, puis supprimer l'ancienne. Cela permet de n'avoir aucune interruption de service.

Votre script est prêt ! Il ne vous reste plus qu'à créer un tag depuis GitLab pour que le déploiement se lance automatiquement.

Vous avez désormais tous les outils pour créer une application de A à Z. Merci de nous avoir suivi jusque-là, et bon développement !