Déploiement continue
Table of Contents
- Déployer automatiquement ses packages : release source, paquet debian, image docker
- réfléchir aux notions de intégration continue vs. distribution continue vs. déploiement continu
- discuter du versioning : https://semver.org/
- Déployer automatiquement sa documentation via les gitlab pages (gitlab.com avec mirror ou remote)
1 Introduction
Vérifiez que vos pipelines sont toujours fonctionnels.
Pour cela trois pré-requis à vérifier :
- le "personnal access token" gitlab,
- le service
gitlab-runner
, - le service
podman
.
Pour avoir accès à votre token et à la bonne configuration podman vous pouvez utiliser le script setenv-podman.sh,
commande à taper dans chaque nouveau terminal ou à ajouter dans votre .bashrc
:
source ~/setenv-podman.sh # rappel : un personal access token valide doit se trouver dans le fichier ~/.gitlabtoken
Ensuite pour lancer le service gitlab-runner
soit le service est déjà configuré et fonctionnel
systemctl --user status gitlab-runner.service # répond : active (running)
soit vous le lancez manuellement dans un nouveau terminal
~/gitlab-runner run
Enfin pour valider que les pipelines fonctionnent, lancer un pipeline. Par exemple manuellement via la page Build -> Pipelines -> Run pipeline, en choisissant la branche 'auto' du projet Heat sur laquelle vous avez déjà travaillé.
2 Distribuer la dernière version stable
Le processus de développement d'un logiciel implique de faire des releases régulièrement. Par release
on entend une
version bien identifiée, testée, avec une interface fixée, une documentation, un changelog
(document texte rappelant
les modifications entre deux versions) et une ou plusieurs archives disponibles pour l'installation.
Le processus de mise à disposition de ces éléments peut être automatisé afin de gagner du temps humain à chaque création d'une nouvelle release. Ainsi cela permet d'en faire plus facilement et donc plus souvent.
2.1 Créer une release
A partir de votre branche 'auto' on vous demande de créer une nouvelle release source et binaire de Heat et de la publier sur Gitlab.
2.1.1 Manuellement via l'interface web Gitlab
Pour cela commencer par générer une archive source, cmake utilise cpack pour faciliter la création de package source et binaire. Utilisez par exemple ces commandes pour générer les archives source et binaire (compatible debian) :
rm -r build cmake -S . -B build -DHEAT_DOC=ON cmake --build build --target doc cp -r build/doc/html doc/ cmake --build build --target package_source cmake --build build --target package
Pour information, la version du paquet, ici 1.0.0, est définie par la variable cmake HEAT_VERSION
dans le fichier
CMakeLists.txt
. Remarquez que l'on génère la documentation afin de l'incorporer dans l'archive source.
Sauvegardez les 6 lignes précédentes de création de paquets (les commandes avec rm, cmake, cp) dans un script
./tools/release.sh
avec comme entête (1re ligne) #!/bin/sh
. Testez son fonctionnement :
chmod +x ./tools/release.sh ./tools/release.sh ll build/heat-1.0.0*
Maintenant sauvegardez votre état actuel via un tag git, ex. v1.0.0 :
git tag v1.0.0 git push origin --tags
Visitez ensuite la page Code -> Tags de votre projet et vérifiez que vous trouvez bien votre tag v1.0.0. Le tag git
permet d'identifier cette version au sens git et donc de retrouver cette version précise du code source avec git
checkout v1.0.0
.
Sur la page des tags vous pouvez créer manuellement une release au sens Gitlab à partir d'un tag existant. Cliquez sur 'Create release' à droite, puis ajoutez quelques commentaires dans la section `Release notes` :
- Add gitlab-ci pipeline - Use a Dockerfile to define the testing environment
Vous pouvez ajouter des liens type url et des fichiers attachés. Utilisez le bouton "Attach a file or image" (trombone dans la partie Release notes) afin d'y ajouter les fichiers .tar.gz et .deb que vous venez de générer avec cpack. Sauvegarder. Vous avez une nouvelle release sur la page Deploy -> Releases avec le code source téléchargeable ainsi que vos deux packages associés.
2.1.2 Via l'API Gitlab
La création de release Gitlab peut être opérée via une commande curl afin d'interagir avec l'API REST de Gitlab, l'opération peut ainsi être scriptable et, nous le verrons par la suite, automatisable.
Lors du TP précédent vous aviez créé un "Personnal Access Token" sauvegardé dans le fichier ~/.gitlabtoken
. Ce token
vous permet d'exécuter des commmandes pour interagir avec Gitlab et de réaliser des opérations sur vos projets. Voir
la documentation sur son utilisation avec curl :
https://docs.gitlab.com/ee/api/rest/#personalprojectgroup-access-tokens.
Exemple pour interroger Gitlab sur la liste des membres d'un projet spécifique. Le projet est identifiable via son numéro identifiant unique que l'on trouve sur la page principale (cf. "Project ID", trois petits points verticaux en haut à droite, à côté de Star et Fork) :
export TOKEN=`cat ~/.gitlabtoken` export PROJECT_ID="votre numéro de projet" curl --header "PRIVATE-TOKEN: $TOKEN" "https://gitlab-ce.iut.u-bordeaux.fr/api/v4/projects/$PROJECT_ID/users" # sortie json structurée curl --header "PRIVATE-TOKEN: $TOKEN" "https://gitlab-ce.iut.u-bordeaux.fr/api/v4/projects/$PROJECT_ID/users" | jq
Vous pouvez accéder à l'API sur les releases (lister, créer, supprimer) :
# afficher les releases existantes curl --header "PRIVATE-TOKEN: $TOKEN" "https://gitlab-ce.iut.u-bordeaux.fr/api/v4/projects/$PROJECT_ID/releases" | jq # afficher leur noms curl --header "PRIVATE-TOKEN: $TOKEN" "https://gitlab-ce.iut.u-bordeaux.fr/api/v4/projects/$PROJECT_ID/releases" | jq '.[0].name' # supprimer la release v1.0.0 export TAG_NAME=v1.0.0 curl --header "PRIVATE-TOKEN: $TOKEN" --request DELETE "https://gitlab-ce.iut.u-bordeaux.fr/api/v4/projects/$PROJECT_ID/releases/$TAG_NAME" | jq
Activer le package registry : Settings -> General -> Visibility, project features -> Package registry -> enable and save changes. Vous pouvez maintenant téléverser des fichiers de type packages binaires via l'API du Generic Package Repository :
RELEASE_NUM=`echo $TAG_NAME | sed -e "s#v##"` # téléverser heat-1.0.0.tar.gz sur le package registry curl --header "PRIVATE-TOKEN: $TOKEN" --upload-file ./build/heat-$RELEASE_NUM.tar.gz "https://gitlab-ce.iut.u-bordeaux.fr/api/v4/projects/$PROJECT_ID/packages/generic/release/$RELEASE_NUM/heat-$RELEASE_NUM.tar.gz" # téléverser heat-1.0.0-Linux.deb sur le package registry curl --header "PRIVATE-TOKEN: $TOKEN" --upload-file ./build/heat-$RELEASE_NUM-Linux.deb "https://gitlab-ce.iut.u-bordeaux.fr/api/v4/projects/$PROJECT_ID/packages/generic/release/$RELEASE_NUM/heat-$RELEASE_NUM-Linux.deb" # pour retrouver l'id du package PACKAGE_ID=`curl --header "PRIVATE-TOKEN: $TOKEN" "https://gitlab-ce.iut.u-bordeaux.fr/api/v4/projects/$PROJECT_ID/packages" | jq '.[0].id'` # pour supprimer le package à partir de id curl --header "PRIVATE-TOKEN: $TOKEN" --request DELETE "https://gitlab-ce.iut.u-bordeaux.fr/api/v4/projects/$PROJECT_ID/packages/$PACKAGE_ID"
Vous pouvez visiter la page Deploy -> Package Registry et y voir vos packages. Vous pouvez aussi les supprimer à la main.
Vous pouvez les télécharger ensuite simplement via wget
:
wget "https://gitlab-ce.iut.u-bordeaux.fr/api/v4/projects/$PROJECT_ID/packages/generic/release/$RELEASE_NUM/heat-$RELEASE_NUM.tar.gz" wget "https://gitlab-ce.iut.u-bordeaux.fr/api/v4/projects/$PROJECT_ID/packages/generic/release/$RELEASE_NUM/heat-$RELEASE_NUM-Linux.deb" ll heat-$RELEASE_NUM* rm heat-$RELEASE_NUM.tar.gz rm heat-$RELEASE_NUM-Linux.deb
Admettons que le tag est bien déjà créé et que les fichiers sont téléversés sur le package registry, nous allons maintenant procéder à la création de la release finale :
CMD=`echo curl --header \"Content-Type: application/json\" \ --header \"PRIVATE-TOKEN: $TOKEN\" \ --data \'{ \"name\": \"$TAG_NAME\", \"tag_name\": \"$TAG_NAME\", \ \"assets\": { \"links\": [{ \"name\": \"heat-$RELEASE_NUM.tar.gz\", \"url\": \"https://gitlab-ce.iut.u-bordeaux.fr/api/v4/projects/$PROJECT_ID/packages/generic/release/$RELEASE_NUM/heat-$RELEASE_NUM.tar.gz\" }, \ { \"name\": \"heat-$RELEASE_NUM-Linux.deb\", \"url\": \"https://gitlab-ce.iut.u-bordeaux.fr/api/v4/projects/$PROJECT_ID/packages/generic/release/$RELEASE_NUM/heat-$RELEASE_NUM-Linux.deb\" }] } \ }\' \ --request POST \"https://gitlab-ce.iut.u-bordeaux.fr/api/v4/projects/$PROJECT_ID/releases\"` eval $CMD | jq
Vous pouvez visiter la page Deploy -> Releases.
Modifiez maintenant le script release.sh
afin de créer la release v1.0.0 via l'API Gitlab en y ajoutant les
commandes pour téléverser les fichiers de packages et pour créer la release dans la foulée. Ajoutez ce script
./tools/release.sh
au git (git add, commit, push).
2.2 Distribution en continu
Le but ici est d'automatiser la création et la distribution d'une release par un job gitlab-ci.
Lors d'un pipeline, gitlab-ci fourni des variables d'environnements contenant des informations : url du projet git,
sur quelle référence git le pipeline est-il exécuté, un token Gitlab temporaire, etc. On va donc modifier le script
release.sh
pour utiliser les variables des pipelines existantes. Remplacer :
PRIVATE-TOKEN: $TOKEN
parJOB-TOKEN: $CI_JOB_TOKEN
TAG_NAME
parCI_COMMIT_TAG
PROJECT_ID
parCI_PROJECT_ID
Attention de bien conserver le champs
tag_name
en minuscule dans la commandecurl
avec le `–data`.
Maintenant modifier .gitlab-ci.yml
pour ajouter un job release (ajouter aussi le stage release après test tout en
haut) qui permet de créer la release automatiquement lorsque l'utilisateur pousse un nouveau tag (respectant une
certaine sémantique vX.Y.Z) sur Gitlab :
release: stage: release rules: - if: $CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_TAG =~ /^v[0-9]\.[0-9]\.[0-9]$/ dependencies: [] artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG" paths: - build/heat-*.tar.gz - build/heat-*.deb script: - ./tools/release.sh
Note : le mot clef dependencies: [] est utilisé pour indiquer qu'on ne souhaite pas télécharger les artefacts des jobs des étapes précédentes puisqu'ils ne sont pas nécessaires ici.
Modifier le fichier dockerfile-buildenv
afin d'ajouter les paquets bash
, curl
, jq
, dpkg
et graphviz
(dépendances
nécessaires pour la création de l'archive).
Utiliser la règle suivante dans le job buildenv à la place de vos anciennes expérimentations (only: changes, when: manual, only: schedule) Commiter, pousser sur la branche auto. Cela devrait reconstruire l'image de test, tout en évitant de recontruire l'image pour d'autres scénario (nouvelle étiquette, pipeline manuel, schedule).
rules: - if: $CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_TAG == null changes: paths: - dockerfile-buildenv
Modifier le numéro de version dans le CMakeLists.txt afin d'être à 1.1.0 :
set(HEAT_VERSION_MAJOR "1") set(HEAT_VERSION_MINOR "1") set(HEAT_VERSION_PATCH "0")
Commiter et pousser.
Tester à présent la création d'une étiquette et la pousser sur Gitlab :
git tag v1.1.0 git push origin --tags
Si ça ne fonctionne pas, après avoir analysé le log du job en échec, vous pouvez supprimer l'étiquette (en local et sur le serveur), puis modifier vos sources, commiter, pousser pour re-tester la procédure.
git tag -d v1.1.0 git push --delete origin v1.1.0 git tag v1.1.0 git push origin --tags
Ne pas hésiter à lire ceci à propos du versioning des releases : https://semver.org/.
Pour garantir que le numéro de tag est consistant avec le numéro de version du projet, cf.
HEAT_VERSION
dans le CMakeLists.txt principal qui donne la version aux fichiers générés .tar.gz/.deb, on peut ajouter ces lignes dansrelease.sh
avant de téléverser les archives .tar.gz/.deb avec curl :PACKAGE=`ls build/heat-*.tar.gz` PACKAGE_NUM=`echo $PACKAGE | grep -o "[0-9].[0-9].[0-9]"` if [ $PACKAGE_NUM != $RELEASE_NUM ]; then echo "PACKAGE=$PACKAGE" echo "PACKAGE_NUM=$PACKAGE_NUM" echo "RELEASE_NUM=$RELEASE_NUM" echo "(CI_COMMIT_TAG=$CI_COMMIT_TAG)" echo "Package number does not match RELEASE_NUM (computed from CI_COMMIT_TAG, without v), exit" exit 1 fi
3 Déploiement en continue
Jusqu'à présent nous avons pu mettre en oeuvre les tests unitaires automatiques (intégration continue) ainsi que la distribution d'une version stable via le processus de release (distribution continue). Pour certaines applications, par exemple des services web, il est en plus nécessaire d'installer la dernière version mise à jour dans son environnement de production, on parle alors de déploiement continu.
3.1 Déploiement de son application via Docker
Le fichier Dockerfile
sert de recette au déploiement de notre application.
Ajouter un fichier Dockerfile
au git afin d'installer l'application dans l'image docker :
ARG IMAGE_FROM FROM $IMAGE_FROM RUN mkdir -p heat/ COPY . heat/ RUN cd heat/ && cmake -B build && cmake --build build USER root RUN cd heat/ && cmake --install build && cd /builds && rm -r heat/ USER gitlab
Pour tester localement :
rm build -rf podman build -t heat -f Dockerfile --build-arg IMAGE_FROM=gitlab-ce.iut.u-bordeaux.fr:5050/$USER/heat/testing .
Remarquez qu'on se base ici sur notre image de test pour éviter la réinstallation de tous les paquets.
Modifier le .gitlab-ci.yml
afin d'y ajouter le job de déploiement (ajouter aussi deploy comme dernière étape dans
stage en début de fichier) :
deploy: stage: deploy rules: - if: $CI_PIPELINE_SOURCE == "push" dependencies: [] image: quay.io/podman/stable variables: IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG before_script: - podman login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY script: - podman build -t $IMAGE_TAG -f Dockerfile --build-arg IMAGE_FROM=$CI_REGISTRY_IMAGE/testing . - podman push $IMAGE_TAG
Note : on aurait pu utiliser la même
rules
que pour le job release (tag push), mais on souhaite ici pouvoir déployer l'environnement de production pour chaque évolution dans les branches. Eventuellement, on pourrait déployer seulement sur la branchemaster
et pour destags
, en ajoutant :- if: $CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - if: $CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_TAG
Visiter la page Deploy -> Container Registry et vérifier la présence de votre image de production.
Tester l'image en local :
export TOKEN=`cat ~/.gitlabtoken` podman login gitlab-ce.iut.u-bordeaux.fr:5050 -u $USER -p $TOKEN podman run gitlab-ce.iut.u-bordeaux.fr:5050/$USER/heat:auto /usr/local/bin/heat_seq 33 33 33 1 0
3.2 Déploiement de la documentation
Un autre exemple courant de déploiement en continu est la documentation. On cherche par exemple à maintenir une page de documentation à jour par rapport au code de la branche principale.
Sur le Gitlab de l'IUT la fonctionnalité de pages (site web de pages html/css statiques associé au projet git) n'est pas activée, il faut donc tester cette fonctionnalité sur un autre Gitlab.
- Créer un compte sur Gitlab.com : https://gitlab.com/users/sign_in.
- Créer un projet vide (sans README initial) heat.
Dans votre projet Heat local commencez par pousser la branche master sur le serveur Gitlab.com :
export LOGIN="votre login gitlab.com" git remote add gitlabcom https://gitlab.com/$LOGIN/heat.git git switch master git push gitlabcom master
Ensuite positionnez vous sur votre branche auto, créer une nouvelle branche pages et ajoutez un script
./tools/pages.sh
générant la documentation :
#!/bin/sh set -x mkdir public cmake -B build -DHEAT_DOC=ON cmake --build build cp -r build/doc/html public/doxygen ./build/heat_seq 33 33 500 1 0 python3 heat_plot.py ffmpeg -i heat.avi -c:v libtheora -q:v 7 -c:a libvorbis -q:a 4 heat.ogv cp tools/index.html public/ cp heat.ogv public/
On pourra utiliser le fichier ./tools/index.html
suivant :
<video style="display:block; margin: 0 auto;" width="900" height="900" controls> <source src="heat.ogv" type="video/mp4"> Your browser does not support the video tag. </video> See the doxygen documentation <a href=./doxygen/>here</a>.
Modifiez le fichier .gitlab-ci.yml
pour y ajouter le job de construction de la documentation :
pages: stage: deploy image: fpruvost/heat dependencies: [] artifacts: paths: - public script: - ./tools/pages.sh
Par convention le job doit être nommé exactement pages et les fichiers ajoutés dans les artefacts seront automatiquement téléversés sur le site web associé au projet sur Gitlab.com, voir la page Deploy -> Pages.
Pour information l'image docker
fpruvost/heat
a été construite avec ce Dockerfile voir sur dockerhub :FROM ubuntu:24.04 # Installing as root: docker images are usually set up as root. # Since some autotools scripts might complain about this being unsafe, we set # FORCE_UNSAFE_CONFIGURE=1 to avoid configure errors. ENV FORCE_UNSAFE_CONFIGURE=1 ENV DEBIAN_FRONTEND noninteractive RUN apt-get update -y RUN apt-get install -y build-essential clang cmake curl doxygen ffmpeg gcovr git python-is-python3 python3-numpy python3-matplotlib xsltproc RUN apt-get autoremove -y
Commentez les autres jobs (avec un . devant le nom de chaque job) puis testez le job pages en poussant votre branche pages sur Gitlab.com :
git add ... git commit -m ... git push gitlabcom pages
Dans vos pipelines un nouveau job pages:deploy sera créé automatiquement et se charge de la mise à jour du site web avec les nouveaux fichiers.
Vous pouvez maintenant visiter la page de votre site dont l'url est indiquée sur Deploy -> Pages, quelque chose comme : https://$login.gitlab.io/heat/.