Architecture SI Développement applicatif

Même le plus petit projet mérite d’avoir son pipeline CI/CD (Partie 2)

Publié le 29/10/2019 Par Meher Hedhli

Dans la partie précédente, nous nous sommes arrêtés sur une étape d’un pipeline CI/CD permettant de construire notre livrable projet, de builder son image Docker et de la publier sur le registry GitLab. Maintenant, la séquence logique des tâches est de pouvoir déployer automatiquement notre application à partir du livrable que nous venons de créer. C’est ce que nous allons voir dans cette 2e partie de l’article.

Retrouvez l’article précédent ici

L’idée désormais est donc de rajouter une nouvelle étape (pour les étapes précédentes, le premier article est ici) dans notre pipeline afin de déclencher un déploiement automatique de l’application dès qu’un “Merge Request” est validé sur la branche “master”.

Comment configurer le serveur hébergeant l’application

Jusqu’à présent, lorsque nous poussons un commit sur la branche master, une nouvelle image Docker est créée et stockée dans le registre de conteneurs GitLab. Désormais, nous souhaitons prendre cette image et la déployer sur notre serveur.

Commençons alors par configurer le serveur :

1. Installer Docker

La première étape consiste bien évidement à installer Docker sur le serveur. Suivez les étapes d’installation telles que décrites dans la documentation de Docker.

2. Créer un utilisateur

Ensuite, créons un utilisateur dédié que GitLab CI utilisera pour se connecter au serveur. Nous appellerons cet utilisateur « deployer ».

Nous l’ajouterons au groupe des utilisateurs « Docker ». De cette façon, l’utilisateur pourra démarrer et arrêter les conteneurs Docker sans nécessiter d’accès root.

$ sudo adduser deployer
$ sudo groupadd docker
$ sudo usermod -aG docker deployer

3. Créer une clé publique / privée

Enfin, nous devons créer une clé privée / publique pour cet utilisateur et donner la clé privée à GitLab CI. La clé publique a quant à elle été placée dans le fichier authorized_keys pour permettre l’accès à l’utilisateur à l’aide de la clé privée correspondante.

$ su deployer
$ ssh-keygen -t rsa
# (Quand on vous demande un passphrase, choisissez no passphrase)
$ cp ~/.ssh/id_rsa.pub ~/.ssh/authorized_keys

Copions maintenant la clé privée dans GitLab (pour que GitLab CI puisse se connecter au serveur et déployer la nouvelle application).

$ cat ~/.ssh/id_rsa

4. Copier le contenu du fichier

Dans l’espace GitLab du projet, accédez à : Settings -> CI/CD -> Secret variables
Créez une nouvelle variable nommée SSH_PRIVATE_KEY, dont la valeur est le contenu du fichier ~ /.ssh/ id_rsa que nous venons de copier.
Rajoutez également une autre variable nommée DEPLOYMENT_SERVER_IP ayant comme valeur l’adresse IP de la VM sur laquelle on va déployer l’application.

Vous pouvez trouver plus d’informations sur l’utilisation du SSH avec GitLab CI dans la documentation GitLab.

Voilà, notre serveur est maintenant prêt pour un premier déploiement. 😀

Déployer l’application depuis GitLab CI/CD

À ce stade, nous pouvons procéder au déploiement de l’application avec GitLab CI/CD. Nous sommes alors amenés encore une fois à mettre à jour notre manifeste GitLab pour y rajouter une nouvelle étape que nous intitulons “deploy”.

Examinons maintenant le job “deploy_staging” qui s’exécutera sous cette nouvelle étape :

deploy-staging:
stage: deploy
image: gitlab/dind:latest
cache: {}
services:
- docker:dind
variables:
DOCKER_DRIVER: overlay
before_script:
# add the server as a known host
- mkdir -p ~/.ssh
- echo "$SSH_PRIVATE_KEY" | tr -d 'r' > ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- eval "$(ssh-agent -s)"
- ssh-add ~/.ssh/id_rsa
- ssh-keyscan -H $DEPLOYMENT_SERVER_IP >> ~/.ssh/known_hosts
script:
- ssh deployer@$DEPLOYMENT_SERVER_IP "docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY}"
# stop container, remove image.
- ssh deployer@$DEPLOYMENT_SERVER_IP "docker stop ${IMAGE_TAG}" || true
- ssh deployer@$DEPLOYMENT_SERVER_IP "docker rm ${IMAGE_TAG}" || true
- ssh deployer@$DEPLOYMENT_SERVER_IP "docker rmi -f ${IMAGE_NAME}" || true
# start new container
- ssh deployer@$DEPLOYMENT_SERVER_IP "docker run --publish=8888:8888 -d ${IMAGE_NAME}"
only:
- master

Les étapes de déploiement

Dans le code ci-dessus, nous créons manuellement une clé privée à l’aide du contenu de la variable d’environnement SSH_PRIVATE_KEY. Puis, nous déclarons que l’adresse IP de notre serveur fait partie des machines connues, de telle sorte que la machine virtuelle créée sous Digital Ocean ne nous le demande pas.

Ensuite, nous lançons des commandes SSH sur le serveur cible afin d’exécuter nos commandes Docker permettant dans l’ordre de :

• Se connecter à notre registre des containers Docker sous GitLab afin de pouvoir récupérer notre nouvelle image (docker login).
• Arrêter et éventuellement supprimer le container correspondant à l’image Docker que nous souhaitons re/déployer (docker stop & docker rm).
• Supprimer l’ancienne image créée, si elle existe (docker rmi).
• Démarrer un nouvel container (docker run).


“Vous l’avez remarqué, le || true s’assure que toute erreur survenant lors de l’arrêt d’un container est ignorée. En effet, si c’est la première fois que nous démarrons le conteneur, il n’y aura pas besoin de l’arrêter. ”


Suivre l’état des pipelines CI/CD

Nous sommes maintenant au bout de notre chaîne de livraison continue. Lorsqu’on pousse un commit sur la branche master, notre pipeline se déclenche pour dérouler ses différentes étapes. Les jobs de build-project, release et deploy-staging s’exécutent ainsi d’une manière séquentielle. L’échec d’un job entraîne l’arrêt de la suite des tâches de notre pipeline.

Depuis le menu CI/CD -> Pipelines, nous pouvons suivre l’état de nos pipelines. Voilà à quoi ressemble le statut d’un pipeline qui aurait abouti au déploiement de notre application. Toutes ses tâches sont au vert 😀 :

Statut d’un pipeline GitLab CI/CD

Enfin, nous pouvons maintenant apprécier le résultat de notre travail en allant voir notre application déployée sur le serveur dédié.

Conclusion – Pipeline CI/CD

La mise en place d’une chaîne de livraison continue pour un projet dont le code source est géré par GitLab n’a rien de vraiment complexe. Certains détails néanmoins doivent être maîtrisés pour que la configuration d’un tel pipeline soit bien établie et fonctionnelle.

Il existe des dizaines de solutions pour déployer des applications dans le cloud et celle que je présente dans cet article (SSH sur un VPS) est probablement la plus élémentaire et éventuellement la moins chère.

Si vous avez des besoins plus exigeants, n’hésitez pas à voir Docker Swarm ou Kubernetes pour des déploiements “de niveau production”. Et même si vous décidez finalement d’utiliser une autre option de déploiement, la première partie de ce tutoriel (concernant le packaging de l’application et le build des images Docker) serait tout de même utile.

Dans la prochaine partie de l’article, nous allons apporter des améliorations à notre chaine CI/CD et y introduire la solution Traefik. Un outil de “Reverse Proxy” permettant de gérer et de router les requêtes HTTP vers différents containers Docker déployés sur notre serveur d’intégration. Chacun de ces containers correspondrait au résultat d’une feature branche. Nous verrons également de nombreuses autres astuces de GitLab CI/CD qui vont nous permettre de mieux piloter notre petite infra. 😉

Vos commentaires

  1. Par aaron, le 28 Nov 2019

    Bonjour,

    a la ligne :

    ssh deployer@$DEPLOYMENT_SERVER_IP « docker rmi -f ${IMAGE_NAME} » || true

    Ou est initialisé vote variable ${IMAGE_NAME}

  2. Par Meher Hedhli, le 29 Nov 2019

    Bonjour,
    La variable est déclaré dans le début du manifeste .gitlab-ci.yml dans la section « variables ». ${IMAGE_NAME} correspondant au nom de la branche actuelle.

    variables:
    IMAGE_NAME: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG

Publier un commentaire

Auteur

Meher Hedhli

Issu d'une formation d’ingénieur en informatique complétée par un Master 2 MIAGE de l'Université Paris Est-Créteil, Meher cumule plus de 3 ans d'expérience dans la conception et le développement des applications web. Il est ainsi capable d'intervenir aussi bien sur le backend que le frontend des projets en développement autour des technologies Java et Angular. Il découvre également les technologies de containérisation Docker et se forme sur les pratiques DevOps.