Skip to content
Snippets Groups Projects
Forked from Cours GL / Atelier GitLab CI-CD
Up to date with the upstream repository.
user avatar
Raed authored
a07f3eb7
History

GitLab CI / CD et documentation

Environnement de développement linux. Outils et programmes utilisés :

Introduction

Pour suivre ce tutoriel, il faut commencer par fork ce dépôt GitLab https://githepia.hesge.ch/cours-gl/atelier-gitlab-ci-cd pour avoir une copie du projet dans votre namespace, et avoir le contenu en local sur votre machine de travail (git clone ...)

Ce dépôt contient une application Node.js conçue pour ce tutoriel : une simple calculatrice qui effectue des additions et des soustractions, et qui enregistre l'historique des opérations dans une base de donnée MongoDB. Le code contient des tests unitaires utilisant Jest.js. On peut aussi générer de la documentation utilisant la syntaxe JSDoc et l'outil de génération documentation.js.

Voici la structure du projet :

Installation et utilisation

Mise en place de la base de données

Pour lancer l'application, il faut

  1. avoir une base de données MongoDB qui tourne. Pour cela, Nous vous proposons de lancer un container docker pour la base de données et exposer le port 3001 :

    docker run --name mongo_gl -d -p 3001:27017 mongo:3.4

    Pour vérifier que le container fonctionne, taper la commande suivante docker ps, un container ayant le nom mongo_gl doit exister :

  2. Dans la racine du projet, créer une copie du fichier .env_example et nommer le .env. Ce fichier va contenir les variables d'environnement utilisées par l'application Node.js (l'adresse ip et le port de la base de données par exemple).

  3. Modifier le contenue de ce fichier pour qu'il s'adapte à votre base de données MongoDB en mettant les bonnes valeurs. Par exemple, si vous avez choisi que votre base de données tourne sur le port 3001 et que le nom de la base de données soit tuto_gl, alors le contenu du fichier .env sera le suivant : DATABASE=mongodb://localhost:3001/tuto_gl

Lancer l'application Node.js

Toujours dans la racine du projet GitLab :

  1. Installer les dépendances :

    npm install
  2. Lancer l'application :

    npm run watch

    Cette commande permet de lancer l'application utilisant l'outil nodemon qui va relancer l'exécution après chaque modification dans un des fichiers dans le dossier de l'application. Cet outil est très utile durant la phase de développement.

    L'application est donc lancée sur le port 3000. Dans un navigateur, visitez l'adresse localhost:3000 :

    Essayez d’effectuer quelques opérations d'addition et de soustraction. Après quelques essaies, vous allez remarquer que les résultats des additions ne sont pas corrects (sciemment). Nous allons corriger ça plus tard dans le tutoriel.

Lancer les tests unitaires Jest

Trois tests unitaires pour chaque opération arithmétique (addition et soustraction) sont implémentés dans ce projet. Les tests se trouve dans le fichier compute.test.js sous le répertoire test :

// test Compute.add function
test.each([[1, 1, 2], [-1, 2, 1], [-2, -1, -3]])(
    '%i + %i equals %i', (a, b, expected) => {
        expect(operation.add(a, b)).toBe(expected);
    }
);

// test Compute.sub function
test.each([[1, 1, 0], [-1, 1, -2], [5, 3, 2]])(
    '%i - %i equals %i', (a, b, expected) => {
        expect(operation.sub(a, b)).toBe(expected);
    }
);

Pour lancer les tests :

npm test

Vous pouvez remarquer que les tests pour l'addition échouent (nous allons corriger ça dans la section suivante).

Générer la documentation

Pour générer la documentation, nous avons à disposition deux commandes :

  • npm run docs:md : cette commande va générer la documentation sous forme d'un fichier Markdown doc.md sous le dossier documentation dans la racine du projet
  • npm run docs:html : cette commande va générer la documentation sous forme d'un site web statique html accessible depuis le ficher index.html sous le dossier public/docs

Intégration, documentation et déploiement continus

Nous allons mettre en place 3 jobs dans notre CI / CD pipeline :

  • Un job test pour lancer les tests

  • Un deuxième job doc pour générer la documentation sous forme d'un fichier Markdown, et le publier comme objet (artifact)

  • Un dernier job deploy pour déployer l'application sur un serveur et générer la documentation sous forme html

Créer une branche de développement dev

Nous allons commencer par créer une nouvelle branche dans notre projet GitLab appelée dev :

git branch dev
git checkout dev
git push -u origin dev

Ces commandes permettent de créer la branche dev et la définissent comme la branche actuellement active.

Créer le fichier de configuration .gitlab-ci.yml

Dans la racine du projet, créez un fichier .gitlab-ci.yml :

image: node:latest

stages:
  - test
  - doc

before_script:
  - npm install

test:
  stage: test
  script:
    - npm test

doc:
  stage: doc
  artifacts:
    paths:
      - documentation/doc.md
  script:
    # Build documentation in md file that will be available from gitlab interface
    - npm run docs:md

Avec cette configuration, deux étapes ont été définies : une étape test pour lancer les tests, et une étape doc pour la génération de la documentation, avec un artifact comme output de l'étape doc.

Vous pouvez trouver des exemples de fichiers .gitlab-ci.yml ici

Une fois créé, poussez les changements locaux vers le projet distant :

git add --all
git commit -m "Add project's CI pipeline configuration"
git push origin dev

Maintenant, vous pouvez observer le pipeline gitlab-ci en cours d'exécution sous CI/CD → Pipelines dans votre projet GitLab :

Vous pouvez remarquer que le pipeline échoue à l'étape test tel qu'attendu.

Dorénavant, après chaque commit, le pipeline s'exécute, et on peut vérifier s'il réussit ou pas.

Corrigeons le code pour que les tests réussissent.

Corriger le fonction de l'addition

Le fichier compute.js dans la racine du projet, contient les implémentations des opérations de soustraction et d'addition. Dans ce fichier, nous avant laissé sciemment une faute de calcul : signe * à la place du signe +.

Corrigez cette erreur, et poussez les changement vers le dépôt GitLab distant :

git add --all
git commit -m "Fix error in addition implementation"
git push origin dev

Maintenant, notre pipeline réussit les test, génère et publie la documentation dans un artifact :

Le bouton à droite vous permet de télécharger les artifacts générés :

Déployer l'application

Jusqu'ici, notre application est disponible seulement en local sur notre machine, en mode développement, sur le port 3000. Maintenant nous allons déployer notre application sur un serveur de déploiement, une instance aws par exemple.

Pour cela, il faut d'abord créer et configurer une instance aws EC2. Dans le dossier aws_script, nous avons mis à disposition un script en python3 create_and_run_aws_instance.py qui permet de créer et lancer une instance aws à partir d'une image personnalisée (avec node et npm installés et une base de données MongoDB qui écoute sur le port 27017).

  1. Créer des "credentials"

  • Depuis la console aws, naviguez à My security credentials depuis la liste quand vous cliquez sur votre nom d'utilisateur (en haut à droite) :

  • Cliquez sur le menu Access keys (access key ID and secret access key), puis sur le bouton Create New Access Key :

  • Dans la fenêtre qui s'affiche, cliquez sur Download Key File, nommez le fichier rootkey.csv et enregistrez le dans le dossier aws_script dans la racine du projet.

    Ce fichier contient vos identifiants (AWSAccessKeyId et AWSSecretKey), qui vont être consommés par le script python pour créer et lancer une instance aws dans votre compte. Le fichier contenu du fichier rootkey.csv dois être sous cette forme :

    AWSAccessKeyId=votre_access_key_id
    AWSSecretKey=votre_secret_key
  1. Créer et lancer une instance ec2 aws

Dans le dossier aws_script, exécutez les commandes suivante :

# se déplacer dans le dossier où se trouve le script
cd aws_script
# créer un environnement virtuel python3
virtualenv venv
# activer l'environnement virtuel
source venv/bin/activate
# installer les dépendences
pip3 install -r requirements.txt
# lancer le script
python3 create_and_run_aws_instance.py
# désactiver l'environnement virtuel
deactivate

Le script create_and_run_aws_instance.py permet de :

  • Créer une pair de clé pour l’accès ssh à une instance,
  • Enregistrer cette pair de clé dans un fichier nommé gl_tuto_key.pem
  • Créer un groupe de sécurité avec une règle pour l'accès ssh (port 22) et une règle pour l'application web (port 8080)
  • Finalement, créer une instance gl_tuto_instance et afficher son adresse ip public affectée

Si le script est lancée pour la première fois, vous aurez le output suivant :

Notez l'adresse ip public affecté à cette instance aws, on en aura besoin dans la section suivante.

Vous pouvez vérifier la création de l’instance dans la console aws :

  1. Ajouter l'étape "déploiement" dans le pipeline ci/cd

Ajoutez le contenu suivant dans le fichier .gitlab-ci.yml :

  • Dans stages, ajoutez une nouvelle étape deploy
  • A la fin du fichier, ajoutez une tache dans l'étape deploy :
deploy:
  stage: deploy
  only:
    - master
  # Disable the cache for this job (not needed)
  before_script: []
  script:
    # Any future command that fails will exit the script
    - set -e

    # Install ssh-agent if it's not installed to use scp to copy files into our server
    - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'

    # Add the secret key of our deployment server to this gitlab docker image.
    # GitLab at times add spaces which will fail ssh into server
    # and hence the [tr -d '\r']. 
    # Issue here https://gitlab.com/gitlab-examples/ssh-private-key/issues/1
    - eval $(ssh-agent -s)
    - echo "$PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null

    # Below script will disable the prompt we get whenever we ssh into the box 
    # and get the message like this: "The authenticity of the host 'ip address'...
    # To disable this, we need to create a entry in ~/.ssh/config to look something 
    # like this:
    # Host *
    #     StrictHostKeyChecking no
    - mkdir -p ~/.ssh
    - touch ~/.ssh/config
    - echo -e "Host *\n\tStrictHostKeyChecking no\n\n" >> ~/.ssh/config

    - echo "deploying to ${SERVER_IP}"

    # Create the .env file with database ip, port and name from gitlab environment variables
    - echo "DATABASE=mongodb://${DB_IP}:${DB_PORT}/${DB_NAME}" >> .env
    - cat .env

    # Copy project files to the deployment server
    - ssh ubuntu@${SERVER_IP} "mkdir -p app"
    - scp -r app.js compute.js models package-lock.json package.json public
        routes tests views .env ubuntu@${SERVER_IP}:app/

    # Restart the project on the deployment server (using forever)
    # https://www.npmjs.com/package/forever
    - ssh ubuntu@${SERVER_IP} "
        forever stopall;
        cd ~/app;
        npm install --only=prod;
        npm run docs:html;
        NODE_ENV=prod forever start app.js
      "

La tâche de déploiement fait appel à des variables d'environnement qu'on peut définir directement depuis l'interface web de GitLab. Pour cela, dans la page de votre projet GitLab :

  • Naviguez à SettingsCD / DI.
  • Dans la partie Environment variables , ajoutez les variables suivantes :
Nom variable Description Valeur
DB_IP Adresse ip de la base de donnée. Dans notre cas, la base de données s'exécute dans le serveur de déploiement (ce qui est fortement déconseillé dans un environnement de déploiement) localhost
DB_NAME Le nom de la base de données tuto_gl
DB_PORT Le port sur lequel la base de données écoute 27017
PRIVATE_KEY La clé privé permettant l’accès en ssh à notre serveur de déploiement Copier-coller le contenu du fichier gl_tuto_key.pem
SERVER_IP L'adresse ip public du serveur de déploiement L'adresse IP de l'instance aws créée par le script create_and_run_aws_instance.py

  • Finalement, cliquez sur Save variables

Poussez les changement vers le dépôt GitLab distant :

git add --all
git commit -m "Add deployment stage in ci pipeline"
git push origin dev

Notre pipeline réussit les test, génère et publie la documentation dans un artifact, mais n'exécute pas l'étape de déploiement :

Ce comportement est attendu vu que nous avons spécifier que l'étape de déploiement ne s'exécute que pour la branche master dans le fichier .gitlab-ci.ymé :

deploy:
  stage: deploy
  only:
    - master

Maintenant nous allons fusionner la branche "dev" avec la branche "master" :

# basculement vers la branche master
git checkout master
# fusion de la branche dev dans la branche actuelle (master)
git merge dev
# pousser les modification locale vers le dépôt distant
git push origin master

L'étape de déploiement est maintenant effectuée :

Maintenant l’application tourne sur votre serveur de déploiement sur le port 8080 : SERVER_IP:8080

Remarque: Si vous voulez pousser des changements sans exécuter le pipeline de GitLab, vous pouvez ajouter [skip ci] dans votre message de commit.