ENSG - Décembre 2019
Présentation disponible à l’URL https://dduportal.github.io/ensg-ci-cd/2019-2020
This work is licensed under a Creative Commons Attribution 4.0 International License
Pour naviguer, utilisez les flèches en bas à droite (ou celles de votre clavier)
Gauche/Droite: changer de chapitre
Haut/Bas: naviguer dans un chapitre
Pour avoir une vue globale : utiliser la touche "o" (pour "Overview")
Pour voir les notes de l’auteur : utilisez la touche "s" (pour "Speaker notes")
Freelancer
Former Developer Advocate @ Containous
Former Training Engineer @ CloudBees
Former Geoportail Tech Lead @ Worldine
Contact:
mailto:damien.duportal@gmail.com
Découvrir (ou redécouvrir) les bases des éléments de la "chaîne de production logicielle"
SCM, Tests, Sécurité, etc.
Découvrir les concepts de l’Intégration Continue et du Déploiement Continu
Découvrir Jenkins
Mettre en oeuvre Jenkins en tant que développeur
2 jours ensemble
Matin:
09:00 → 10:30
11:00 → 12:30
Après-Midi:
13:30 → 15:00
15:30 → 17:00
QCM: 25 % de la note
Rendu de TP: 75 % de la note
But : évaluer l’acquisition des concepts théoriques autour du CI/CD
Format :
15 questions à choix multiples portant sur les 2 jours
Pas de points négatifs
Temps limité: 15 minutes
But : évaluer l’acquisition de compétences de mise en oeuvre basiques autour du CI/CD
Format:
Rendu public dans GitHub : valorisable sur vos CVs
Réutilisation d’un de vos projets
Temps:
Estimation du travail personnel: 4h +- 1h
Dates de remise des consignes et rendus à définir ensemble
3 semaines entre les deux
"Gestion de Code Source"
Les Gestionnaires de Code Source, également connus comme "Version Control Systems" (VCS):
Sont des systèmes logiciels
Conservent toutes les modifications apportées à une collection de fichiers, dans le temps
Permettent de partager ces changements
Fournissent des fonctionnalités de merge et de suivi des modifications
Pour collaborer efficacement sur un même référentiel de code source
Aide à la résolution de conflits
Partage de contenu facile
Pour conserver une trace de tous les changements : On parle de source unique de vérité (Single Source of Truth)
Historique complet des modifications
Possibilité de retour arrière à tout moment
Locaux
Centralisés
Distribués
Plus vieux type, ancêtre de tous les autres
Uniquement historique de modification
Utilise une "base de donnée de versions" des fichiers
Stockage uniquement des différences ("diff")
Pas de partage
Exemple: rcs (toujours dans Apple XCode Tools)
Couvre historique ET partage
La "base de données de versions" est stockée sur un serveur central
Chaque client ne possède qu'une seule version du code
Apprentissage très facile, limité sur la résolution de conflits
Exemples: CVS, SVN, Perforce, TFS
La "base de données de versions" est distribuée par duplication sur chaque noeud
Exemples: Git, Mercurial, Bazaar, Monotone
Hébergés dans le Cloud
Hébergé "à la maison"
SCM as a Service
Le serveur centralisé est un service hébergé par un fournisseur
Avantages:
Pas de temps/énergie passé sur la gestion
Associent au SCM d’autres services : gestionnaire de tickets, wiki, éditeur de texte online, etc.
Risque: Votre code est hébergé par un tiers
Exemples: GitHub, Bitbucket by Atlassian, Amazon CodeCommit, Visual Studio Online by Microsoft, SourceForge, GitLab.com, etc.
Pour pallier au risque précédent, on trouve des versions "On-Premise" (généralement payantes)
Le monde de l’Open Source fournit également des solutions à héberger soi-même
Très souvent gratuit et on peut le corriger
Temps et énergie à consacrer
Exemples: Gitlab, Gitea, Gogs, Bazaar server, VisualSVN Server, etc.
diff: un ensemble de lignes "changées" sur un fichier donné
changeset: un ensemble de "diff" (donc peut couvrir plusieurs fichiers)
commit: action de sauvegarder un changeset dans la base de données des versions.
Le dernier commit dans l’historique est aliasé comme "HEAD"
Abstraction d’une version "isolée" du code
Concrètement, une branche est un alias pointant vers un "commit"
On intègre une branche dans une autre en effectuant un merge
Un nouveau commit est créé, fruit de la combinaison de 2 autres commits
Une Pull Request (ou "Merge Request") est une procédure de revue de code avant intégration
Voici quelques motifs d’utilisation des SCMs :
"Centralized" Flow
"Feature Branch" Flow
"Git" Flow
"GitHub" Flow
Une seule branche par fonctionnalité
"Infrastructure as Code" :
Besoins de traçabilité, de définition explicite et de gestion de conflits
Collaboration requise pour chaque changement (revue, responsabilités)
Code Civil:
Un peu de lecture :
Le code est donc sujet à erreurs: Conséquences?
Les tests identifient ces erreurs, dans un but de correction
Le test logiciel est une pratique suivant 2 piliers :
Valider que le logiciel remplisse les rôles qui lui sont confiés
Rechercher les fautes pour les corriger, améliorant la qualité du système
Automatiser : répétition et reproductibilité
Test Manuel à considérer dans peu de cas, quand :
Coût de l’automatisation dépasse sa valeur
Automatisation impossible
SUT: "System Under Testing". Définit les frontières du système.
Test Double: Terme générique désignant un sous-ensemble simplifié du "S.U.T.". Exemples: Mock, Stub, Spy, etc.
Boîte Blanche: Tester avec une vue interne du SUT
Boîte Noire: Tester le SUT sans connaissance préalable de ses mécanismes internes
La question primordiale est: "Que voulez-vous tester ?"
En fonction de la réponse, différent types de tests peuvent être utilisés (liste NON exhaustive) :
Unit testing
Integration testing
Smoke testing
Functional Testing
Non-Regression testing
Acceptance testing
Focalisé sur le plus petit sous-sytème possible du SUT, en "boîte blanche"
Tests indépendants les uns des autres
Ordre d’exécution non important
Utilisation de Test Doubles pour simuler le "reste" en bon fonctionnement
Vérifier l’intégration entre différents sous-systèmes
Le SUT est en "boîte blanche"
But : Fail Fast en "boîte blanche"
Valide les fonctions "de base" du système
On parle parfois de "Sanity Checking"
If it smokes, it’s bad
Vérifie que le logiciel se comporte comme prévu par les personnes en charge de la fabrication
Pas de biais d’interprétation
Le SUT est en "boîte noire"
Vérifie que le SUT a un comportement stable dans le temps
Focalisation sur bug qui ne doit pas revenir
Le SUT est en "boîte noire"
Correcting a single bug may introduce several more.
Également connu sous l’acronyme "UAT" User Acceptance Testing
Vérifie que le logiciel se comporte comme attendu par l’utilisateur
Biais de communication inclus
Le SUT est en "boîte noire"
Fonction des temps d’exécutions, des coûts de corrections, et des valeurs ajoutées. Contextuel.
TDD: Écrire les tests unitaires avant le code
BDD: Privilégier language naturel et interactions
"Given, When, Then"
Moins de technique. Valeur ajoutée pour l’utilisateur.
Continuous Integration is a software development practice where members of a team integrate their work frequently. Usually each person integrates at least daily - leading to multiple integrations per day.
Construire et intégrer le code en continu
Le code est intégré souvent, au moins quotidiennement pour que l’intégration soit un non-évenement
Chaque intégration est validée par une construction automatisée avec tests
But : Détecter les fautes au plus tôt
Continuous Integration doesn’t get rid of bugs, but it does make them dramatically easier to find and remove.
Continous Delivery (CD)
Diminuer les risque liés au déploiement
Permettre de récolter des retours utilisateurs plus souvent
Rendre l’avancement visible par tous
How long would it take to your organization to deploy a change that involves just one single line of code?
Suite logique de l’intégration continue:
Chaque changement est potentiellement déployable en production
Le déploiement peut donc être effectué à tout moment
Your team prioritizes keeping the software deployable over working on new features
Continuous Deployment
Version "avancée" de la livraison continue:
Chaque changement est déployé en production, de manière automatique
Question importante: En avez-vous besoin ?
Avez-vous les mêmes besoins que Amazon Google ou Netflix ?
"Software Supply Chain"
"Feedback loop" / "Boucle de feedback"
Problèmatique : réagir rapidement pour corriger une faute
"Au plus tôt, au moins cher"
Problème #1: Avoir un retour
Problème #2: Réagir systématiquement sur un retour
Problème #3: Avoir confiance
Quels acteurs du système ?
Quel médium de communication ?
Quels déclencheurs et quelles limites ?
Culture à construire, les outils suivent facilement
Industrialisation du logiciel
Modélisation de la chaîne de valeur ("Value Stream Mapping")
"Fast is cheap": Piloté par le concept de la défaillance rapide ("fail fast")
Stage ("étape"): Élément de base
Abstraction atomique d’un ensemble d’actions
Exemple: "Build", "Run Unit Tests"
Possibilité de parallélisation
Gate ("Porte"): Transition entre 2 étapes
Manuelle ou automatique
Peuvent être conditionnelles
Déclenchement initial : un changement dans la base de code
Chaque étape peut produire des livrables: on parlera d'Artefacts dans ce cours
Le déploiement est ce qui permet de rendre le logiciel prêt à l’usage
Un "déploiement" est exécuté vers un environnement
Production
Préproduction ("staging") / recette ("qualification")
Tests
"Disaster Recovery Environment"
Commencer par un "Produit Minimum Viable" (MVP) puis itérer
S’efforcer d’appliquer les bonne pratiques
Optimiser le Pipeline (lors des itérations)
Réutilisation des artefacts: "Only Build Your Binaries Once"
Arrêt du Pipeline dès qu’une faute est identifiée: "Fail Fast"
Identifier si un artefact n’est pas déployable (tests…)
S’assurer qu’une même version de la base de code est utilisée à tout moment pour un Pipeline donné
Paralléliser les étapes
Arrêt du Pipeline si une "branche" est en erreur
Sinon: étape inutile à supprimer
Les "gates" manuelles peuvent également être parallélisée
relation "1-N": N "gates" manuelles déclencheront N étapes parallèles
Un peu de lecture :
Votre organisation utilise l'information pour créer de la valeur
L’information doit donc être:
Confidentielle
Intègre
Disponible
C’est l’ensemble des pratiques et des outils permettant de prévenir et combattre les menaces sur l’organisation
4 piliers:
Connaissance du sytème
Least Privilege
Défense en profondeur
Mieux vaut prévenir que guérir
AAA signifie :
Authentification
Authorisation
Accounting (comptabilisation)
C’est l’ensemble des procédures et outils pour identifier un acteur avec une confiance suffisante
Une fois l’acteur identifié avec confiance, il faut contrôler ses droits en terme de manipulations
Nomenclature :
Ressources: Tâches ou objets manipulables et accessibles
Rôles: Ensemble de droits regroupés par commodité
Requêteurs: Acteur souhaitant manipuler des ressources
Etre autorisé à manipuler des ressources ne garantie pas l’effection à 100%
Limites du système (mémoire, disque, consommation, temps, etc.)
Erreurs, pannes et fautes
L'"accounting" permet de mesurer et contrôler les manipulations
Respect des limites
Reprises sur erreur
Capacity planning
Un peu de lecture :
Git
Docker 18.03+
docker-compose 1.24.0+
Configurer le proxy HTTP: https://docs.docker.com/config/daemon/systemd/#httphttps-proxy
Clone le dépot https://github.com/dduportal/ensg-ci-cd
Se positionner sur la branche 2019-2020
(git checkout <branch name>
)
Se positionner dans le dossier lab-vm/docker
docker-compose up -d # Démarrage
docker-compose stop # Suspendre
docker-compose kill # Arrêt brutal
docker-compose down -v # Tout supprimer une fois arrêté
# Arrêter le gitserver
docker-compose kill gitserver
# Nettoyer le gitserver
docker-compose rm -f -v gitserver
# Récupérer le fix
git pull origin 2019-2020
# Récupérer la nouvelle image
docker-compose pull gitserver
# Redémarrer le gitserver
dockr-compose up -d gitserver
Dans votre navigateur, ouvrir https://localhost
Accepter l’exception de sécurité ou ajouter le certificat comme "trusted"
Bienvenue sur la page d’accueil du Lab:
Le lien Slides vous permettra d’accéder au slides
Cliquer sur le lien Git Server sur la page d’accueil du Lab
S’authentifier en tant que butler
(mot de passe butler
)
Cliquer sur Explore (en haut)
Cliquer sur butler/demoapp
But : Illustrer un exemple de "Software Supply Chain"
Problématique : Quel language/framework/outil choisir ?
Solution:
"Opinionated" demo application
Tout le monde sur le même pied
Application Web
Page d’accueil affichant "Greetings from Spring Boot!"
C’est un des exemples de Spring Boot Starter
Langage: Java (OpenJDK 8)
Toolchain: Maven (Maven >= 3.3)
Code source stocké dans un dépôt Git
Configuration Maven: pom.xml
Code source de l’application: src/main/java/
Code source des tests: src/test
Scripts utilitaires: scripts
Ouvrir la console DevBox
WebSockets doit être autorisé
Clean the window: clear
Show command history: history
CTRL + R
: search the command history interactively
CTRL + C
: cancel current command and clean line buffer
CTRL + A
: jump to beginning of line
CTRL + E
: jump to end of line
cat
(concatenate)
ls
(list)
cd
(change directory)
pwd
(print working directory)
man
(manual)
rm
(remove)
mkdir
(make directory)
touch
(create an empty file)
Obtenir l’adresse HTTP du dépôt depuis le GitServer
# Get the code. Requires authentication.
git clone http://gitserver:3000/butler/demoapp
# Browse to the local repository
cd ./demoapp
# Check source code
ls -l
Maven TL;DR:
Workflow standardisé
pom.xml
décrit l’application (Project Object Model)
Maven Command line : mvn
, attend des goals en argument
mvn dependency:list
Accepte des flags
mvn dependency:list -fn
Goal compile
Résolution des dépendances
Pré-traitement du code source
Compilation des classes
Résultats dans le dossier ./target
:
mvn compile
ls -l ./target
Goal test
Exécute le goal compile
Compilation des tests unitaires
Exécution des tests unitaires
Rapports de tests dans ./target/surefire-reports
:
mvn test
ls -l ./target/surefire-reports
goal package
Exécute les goals compile
et test
Paquetage de l’application
Résultat dans ./target
mvn package
ls -lrh ./target/
Spring Boot demo exécutée comme un "Über-Jar"
Exécution avec la commande java:
java -jar ./target/demoapp.jar
# Utiliser CTRL-C pour arrêter
Ouvrir la page http://localhost:8080,
ou tester avec la commande curl
dans
une autre instance de DevBox avec :
curl http://localhost:8080 && echo
Goal verify
Exécute compile
, test
et package
Compile et exécute les tests d’intégration sur l’application empaquetée
Rapport de tests dans ./target/failsafe-reports
:
mvn verify # 1 test failure expected
ls -l ./target/failsafe-reports
Jenkins is an open source automation server which enables developers around the world to reliably build, test, and deploy their software.
Orchestrateur de tâches Open Source
#1 Serveur Intégration Continue Integration
Un des tout premiers moteur d’intégration continue
Créé par Kohsuke Kawaguchi en 2006
Architecture orientée plugins
Une communauté indépendante et active (jenkins.io)
Projet original: "Hudson", renommé "Jenkins" en 2011
500+ releases
150,000+ installations actives
1,200+ plugins
But : CI → CD
Pas de "cassure" depuis Jenkins 1
Expérience "premier démarrage" améliorée
Pipeline-as-Code:
Syntaxe "scriptée" : "Code your Pipeline"
Un outil pour définir votre "Pipeline" dans Jenkins avec un language de domaine
Le Pipeline est décrit dans un fichier texte: le JenkinsFile
"Pipeline-as-code" : Stocké dans un SCM
"Pipeline-as-code" : Nous avons besoin d’un Jenkinsfile
Par où commencer ?
Declarative
Syntaxe par défaut
S’utilise avec Blue Ocean
Scripted
Syntaxe originale (~3 ans)
"Great Power == Great Responsibility"
À utiliser lorsque le Déclaratif commence à être bizarre
Fournit le cycle ("round trip") complet avec le SCM
Pas de Pipeline ? "Suivez le guide".
Le Pipeline existe déjà ? Edit, commit, et exécutez le
Cliquez sur le lien "Jenkins" sur la page d’accueil du lab
Authentifiez vous en tant que butler
(le mot de passe est butler
)
Passez à l’interface Blue Ocean:
Ou cliquez sur le lien "Open Blue Ocean" à gauche
Créer un nouveau Pipeline, avec les réglages suivants :
Stocké dans Git
Utilisez l’URL en SSH
La clef SSH a déjà été préparée pour vous
(Dans Gitea, en haut à droite → "Settings" → "SSH/GPG Keys")
Si la page est bloquée: rechargez !
Cliquez sur le bouton Create a Pipeline
Aucun fichier Jenkinsfile
n’est trouvé dans le dépôt de code:
Le Blue Ocean Pipeline Editor s’ouvre pour en générer un.
Familiarisez-vous avec l’interface:
Pour créer une étape ("stage"), utilisez le petit "plus" ()
Cliquez sur une étape pour voir le panneau de configuration à droite
Créez un pipeline avec 3 "stages" : Build, Test et Deploy
Chaque "stage" doit avoir 1 "step" qui affiche un message
"Building…" pour Build, "Testing…" pour Test …
pipeline {
agent any
stages {
stage('Build') {
steps {
echo 'Build'
}
}
stage('Test') {
steps {
echo 'Test'
}
}
stage('Deploy') {
steps {
echo 'Deploy'
}
}
}
}
En utilisant Blue Ocean Pipeline Editor:
Modifiez les 3 stages du pipeline actuel
Les scripts sont stockés dans le dossier ./scripts
Utilisez la "step" Shell Script (mot clef sh
)
Supprimez les steps echo
pipeline {
agent any
stages {
stage('Build') {
steps {
sh './scripts/build.sh'
}
}
stage('Test') {
steps {
sh './scripts/integration-tests.sh'
}
}
stage('Deploy') {
steps {
sh './scripts/deploy.sh'
}
}
}
}
En utilisant le Blue Ocean Pipeline Editor:
Modifiez le pipeline actuel
La "stage" Build doit archiver tous les fichiers ".jar" générés
Indice : Dans le dossier target
, motif *.jar
La "stage" Test doit publier récursivement tous les rapports de test junit
d’intégration
Indice : Dans le dossier target
, motif failsafe-reports/**/*.xml
Le Pipeline doit être UNSTABLE
pipeline {
agent any
stages {
stage('Build') {
steps {
sh './scripts/build.sh'
archiveArtifacts 'target/*.jar'
}
}
stage('Test') {
steps {
sh './scripts/integration-tests.sh'
junit 'target/failsafe-reports/**/*.xml'
}
}
stage('Deploy') {
steps {
sh './scripts/deploy.sh'
}
}
}
}
Le build est en état UNSTABLE
(jaune)
Priorité: réparer le build
Utiliser Gitea git server
Les tests d’intégration sont dans src/test/java/hello
Indice:
Integration Tests: == IT
Pour relancer le build : depuis l’accueil Blue Ocean, naviguez sur le projet → Branches, et regardez à droite, dans la ligne correspondant à "master"
Éditer le fichier src/test/java/hello/HelloControllerIT.java
Commenter la ligne 39
Dé-commenter la ligne 40
Lancer le build manuellement dans Blue Ocean
Attention: ne pas utiliser "Re-run" (même commit)
Le build résultant doit être stable
Nous avons dû lancer le build manuellement
IC: Retours rapides !
Lancer le build dès que le code est poussé
Configurons un "Webhook" dans Gitea → Settings → Webhooks
Ajoutez un nouveau webhook:
Type: Gitea
Target URL:
http://jenkins:8080/jenkins/git/notifyCommit?url=ssh://git@gitserver:5022/butler/demoapp.git
Trigger On: All events
Une fois créé, testez le (cliquez sur le webhook, puis tout en bas)
Ajoutez un commentaire dans le Jenkinsfile depuis Gitea git server
Un build va démarrer
Validez dans l’onglet "Changes"
Dans l’éditeur Blue Ocean, voir la version textuelle:
Combinaison CTRL + S
(On Mac: CMD +S
)
Bi-directionnel: essayez de charger une solution de pipeline
Le Pipeline Syntax Snippet Generator comme acolyte:
Génération dynamique en fonction de vos plugins
Depuis l’interface "ancienne" de Jenkins
Menu de gauche de votre job "Pipeline" (ou MultiBranch)
But: réutiliser les binaires générés dans Build
Action: "mise sur étagère": Stash / Unstash
Modifier le Pipeline pour :
"Stasher" le dossier target
(récursivement), à la fin de la phase Build
Indice : Dans le dossier target
, motif target/**/*
"Unstasher" au début de la phase Test
Indice : Attention à l’ordre !
pipeline {
agent any
stages {
stage('Build') {
steps {
sh './scripts/build.sh'
archiveArtifacts 'target/*.jar'
stash(name: 'build-result', includes: 'target/**/*')
}
}
stage('Test') {
steps {
unstash 'build-result'
sh './scripts/integration-tests.sh'
junit 'target/failsafe-reports/**/*.xml'
}
}
stage('Deploy') {
steps {
sh './scripts/deploy.sh'
}
}
}
}
Section post
:
Contient des "steps" à exécuter à la fin du Pipeline ou après une "stage"
Divisée en "condition d’états":
always
, success
, failure
, changed
Chaque condition contient ses propres "steps"
Pas encore intégré dans l’éditeur Blue Ocean
Si le "stage" Build échoue, alors la tâche "archiveArtifacts" ne devrait pas être exécutée
Même chose pour stash
Les rapports de tests unitaires doivent être publiés dans tous les cas après la phase Build
Format Junit
Stockés dans target/**/*.xml
Utiliser la documentation:
pipeline {
agent any
stages {
stage('Build') {
steps {
sh './scripts/build.sh'
}
post {
success {
archiveArtifacts 'target/*.jar'
stash(name: 'build-result', includes: 'target/**/*')
}
always {
junit 'target/surefire-reports/**/*.xml'
}
}
}
stage('Test') {
steps {
unstash 'build-result'
sh './scripts/integration-tests.sh'
junit 'target/failsafe-reports/**/*.xml'
}
}
stage('Deploy') {
steps {
sh './scripts/deploy.sh'
}
}
}
}
Un noeud (ou node) est une machine prête à recevoir des builds
Step agent
spécifie sur quel "noeud" exécuter des "stages".
Une section agent
globale doit être définie
(au niveau du block pipeline
)
On peut aussi définir des sections agent
par "stage"
Exécuter l’étape Build sur un agent
configuré avec le label maven-jdk8
Exécuter l’étape Test sur un agent
configuré avec le label java8
L’éditeur Blue Ocean est utilisable
pipeline {
agent any
stages {
stage('Build') {
agent {
node {
label 'maven-jdk8'
}
}
steps {
sh './scripts/build.sh'
}
post {
always {
junit 'target/surefire-reports/**/*.xml'
}
success {
archiveArtifacts 'target/*.jar'
stash(name: 'build-result', includes: 'target/**/*')
}
}
}
stage('Test') {
agent {
node {
label 'java8'
}
}
steps {
unstash 'build-result'
sh './scripts/integration-tests.sh'
junit 'target/failsafe-reports/**/*.xml'
}
}
stage('Deploy') {
steps {
sh './scripts/deploy.sh'
}
}
}
}
But: Tester en parallèle l’application sur java 8 et la future version de java
Mot clef parallel
définissant un block contenant
des "stages"
Agent javanext
pour le Test Java "Future version"
L’éditeur Blue Ocean est utilisable (et recommandé)
pipeline {
agent any
stages {
stage('Build') {
agent {
node {
label 'maven-jdk8'
}
}
steps {
sh './scripts/build.sh'
}
post {
always {
junit 'target/surefire-reports/**/*.xml'
}
success {
archiveArtifacts 'target/*.jar'
stash(name: 'build-result', includes: 'target/**/*')
}
}
}
stage('Test') {
parallel {
stage('Test Java 8') {
agent {
node {
label 'java8'
}
}
steps {
unstash 'build-result'
sh './scripts/integration-tests.sh'
junit 'target/failsafe-reports/**/*.xml'
}
}
stage('Test Java Next') {
agent {
node {
label 'javanext'
}
}
steps {
unstash 'build-result'
sh './scripts/integration-tests.sh'
junit 'target/failsafe-reports/**/*.xml'
}
}
}
}
stage('Deploy') {
steps {
sh './scripts/deploy.sh'
}
}
}
}
But: Usage de Docker pour faciliter la définition des environnements de build
Le mot clef agent
permet d’exécuter les "stages" dans
un container Docker, depuis une "image Docker",
ou depuis un Dockerfile
(recette maison d’images Docker)
Exécuter le Build dans un containeur
basé sur le fichier Dockerfile.build
pipeline {
agent any
stages {
stage('Build') {
agent {
dockerfile {
filename 'Dockerfile.build'
label 'docker'
additionalBuildArgs '--build-arg HTTP_PROXY=http://10.0.4.2:3128/ --build-arg HTTPS_PROXY=http://10.0.4.2:3128/'
}
}
steps {
sh './scripts/build.sh'
}
post {
always {
junit 'target/surefire-reports/**/*.xml'
}
success {
archiveArtifacts 'target/*.jar'
stash(name: 'build-result', includes: 'target/**/*')
}
}
}
stage('Test') {
parallel {
stage('Test Java 8') {
agent {
node {
label 'java8'
}
}
steps {
unstash 'build-result'
sh './scripts/integration-tests.sh'
junit 'target/failsafe-reports/**/*.xml'
}
}
stage('Test Java Next') {
agent {
node {
label 'javanext'
}
}
steps {
unstash 'build-result'
sh './scripts/integration-tests.sh'
junit 'target/failsafe-reports/**/*.xml'
}
}
}
}
stage('Deploy') {
steps {
sh './scripts/deploy.sh'
}
}
}
}
Problème courante : Environnements différents entre le poste du dév. et la production
Solution: Utiliser Docker pour définir un environment portable
## Construire uniquement l'environnement de build
docker build --build-arg HTTP_PROXY=http://10.0.4.2:3128/ \
--build-arg HTTPS_PROXY=http://10.0.4.2:3128/ \
-t demoapp:build -f Dockerfile.build ./
## Executer un container
mvn -v
docker run --rm -ti -v $(pwd):/app --entrypoint="bash" demoapp:build
bash-4.4# ls -al /app
bash-4.4# mvn -v # Difference ?
But: Usage de Docker pour faciliter la définition des environements de build
Le mot clef agent
permet d’exécuter les "stages" dans
un container Docker, depuis une "image Docker",
ou depuis un Dockerfile
(recette maison d’image Docker)
Exécuter l’étape Build dans un containeur
basé sur le fichier Dockerfile.build
Attention, le proxy doit être précisé à Jenkins. Comment passer le flag --build-arg
dans le pipeline?
Indice : Documentation Directive Agent sur jenkins.io
Attention, le proxy doit également être passé à Maven, qui ne sait pas lire la variable HTTP_PROXY
Indice : Documentation de Maven sur la configuration d’un proxy
Indice : L’utilisateur par défaut dans Dockerfile.build
est root
. Son HOME
est le dossier /root
Indice : Vous pouvez tester dans la devbox de manière interactive
Fichier Jenkinsfile
:
pipeline {
agent any
stages {
stage('Build') {
agent {
dockerfile {
filename 'Dockerfile.build'
label 'docker'
additionalBuildArgs '--build-arg HTTP_PROXY=http://10.0.4.2:3128/ --build-arg HTTPS_PROXY=http://10.0.4.2:3128/'
}
}
steps {
sh './scripts/build.sh'
}
post {
always {
junit 'target/surefire-reports/**/*.xml'
}
success {
archiveArtifacts 'target/*.jar'
stash(name: 'build-result', includes: 'target/**/*')
}
}
}
stage('Test') {
parallel {
stage('Test Java 8') {
agent {
node {
label 'java8'
}
}
steps {
unstash 'build-result'
sh './scripts/integration-tests.sh'
junit 'target/failsafe-reports/**/*.xml'
}
}
stage('Test Java Next') {
agent {
node {
label 'javanext'
}
}
steps {
unstash 'build-result'
sh './scripts/integration-tests.sh'
junit 'target/failsafe-reports/**/*.xml'
}
}
}
}
stage('Deploy') {
steps {
sh './scripts/deploy.sh'
}
}
}
}
Fichier settings.xml
:
<settings>
<proxies>
<proxy>
<id>httpproxy</id>
<active>true</active>
<protocol>http</protocol>
<host>10.0.4.2</host>
<port>3128</port>
<nonProxyHosts>ensg.eu|localhost</nonProxyHosts>
</proxy>
<proxy>
<id>httpsproxy</id>
<active>true</active>
<protocol>https</protocol>
<host>10.0.4.2</host>
<port>3128</port>
<nonProxyHosts>ensg.eu|localhost</nonProxyHosts>
</proxy>
</proxies>
</settings>
FROM maven:3-jdk-8-alpine
# Install Some tools
RUN apk add --no-cache \
curl \
bats \
docker \
git
# Configure Proxy
RUN mkdir -p /root/.m2
COPY ./settings.xml /root/.m2/settings.xml
Jenkins permet de "mettre en pause" l’exécution d’un pipeline, en attendant une validation humaine
Continuous Delivery / Deployment
Mot-clef input
, c’est une "step" Pipeline
Hautement configurable: Documentation Step Input
Généralement utilisé dans un "stage" dédié.
Obligatoirement avec agent none
(pas d’exécuteur, pas de workspace)
Ajouter une stage "approval" avant le déploiement
pipeline {
agent any
stages {
stage('Build') {
agent {
dockerfile {
filename 'Dockerfile.build'
label 'docker'
additionalBuildArgs '--build-arg HTTP_PROXY=http://10.0.4.2:3128/ --build-arg HTTPS_PROXY=http://10.0.4.2:3128/'
}
}
steps {
sh './scripts/build.sh'
}
post {
always {
junit 'target/surefire-reports/**/*.xml'
}
success {
archiveArtifacts 'target/*.jar'
stash(name: 'build-result', includes: 'target/**/*')
}
}
}
stage('Test') {
parallel {
stage('Test Java 8') {
agent {
node {
label 'java8'
}
}
steps {
unstash 'build-result'
sh './scripts/integration-tests.sh'
junit 'target/failsafe-reports/**/*.xml'
}
}
stage('Test Java Next') {
agent {
node {
label 'javanext'
}
}
steps {
unstash 'build-result'
sh './scripts/integration-tests.sh'
junit 'target/failsafe-reports/**/*.xml'
}
}
}
}
stage('Approval') {
agent none
steps {
input(message: 'Deploy Application?', ok: 'Yes Deploy!')
}
}
stage('Deploy') {
steps {
sh './scripts/deploy.sh'
}
}
}
}
Définir l’exécution conditionnelle d’une "stage"
Mot clef when
Doit contenir au moins une condition parmi:
branch
environment
expression
Logique "built-in": allOf
, anyOf
, etc.
Définir des collections de clé-valeurs, en tant que variables d’environnement:
Définition globale ou par "stage"
Ou en utilisant withEnv
dans un block steps
pour une instruction spécifique
N’exécuter les "stages" Approval et Deploy que:
Si on se trouve sur la branche master
Ou si la variable FORCE_DEPLOY
est à true
pipeline {
agent any
environment {
FORCE_DEPLOY = 'false'
}
stages {
stage('Build') {
agent {
dockerfile {
filename 'Dockerfile.build'
label 'docker'
additionalBuildArgs '--build-arg HTTP_PROXY=http://10.0.4.2:3128/ --build-arg HTTPS_PROXY=http://10.0.4.2:3128/'
}
}
steps {
sh './scripts/build.sh'
}
post {
always {
junit 'target/surefire-reports/**/*.xml'
}
success {
archiveArtifacts 'target/*.jar'
stash(name: 'build-result', includes: 'target/**/*')
}
}
}
stage('Test') {
parallel {
stage('Test Java 8') {
agent {
node {
label 'java8'
}
}
steps {
unstash 'build-result'
sh './scripts/integration-tests.sh'
junit 'target/failsafe-reports/**/*.xml'
}
}
stage('Test Java Next') {
agent {
node {
label 'javanext'
}
}
steps {
unstash 'build-result'
sh './scripts/integration-tests.sh'
junit 'target/failsafe-reports/**/*.xml'
}
}
}
}
stage('Approval') {
agent none
steps {
input(message: 'Deploy Application?', ok: 'Yes Deploy!')
}
when {
anyOf {
branch 'master'
environment name: 'FORCE_DEPLOY', value: 'true'
}
}
}
stage('Deploy') {
steps {
sh './scripts/deploy.sh'
}
when {
anyOf {
branch 'master'
environment name: 'FORCE_DEPLOY', value: 'true'
}
}
}
}
}
Directive parameters
: l’utilisateur fournit des arguments
au pipeline
Disponible en tant que variables accessibles dans l’objet params
Oeuf & Poule : Jenkins ne peut pas accéder aux paramètres lors du 1er build
Ajouter un paramètre DEPLOY_MESSAGE
dont la valeur par défaut est Deploy ?
Ce paramètre est utilisé dans le input
pipeline {
agent any
parameters {
string(name: 'DEPLOY_MESSAGE', defaultValue: 'Deploy ?', description: 'Message de déploiement')
}
environment {
FORCE_DEPLOY = 'false'
}
stages {
stage('Build') {
agent {
dockerfile {
filename 'Dockerfile.build'
label 'docker'
additionalBuildArgs '--build-arg HTTP_PROXY=http://10.0.4.2:3128/ --build-arg HTTPS_PROXY=http://10.0.4.2:3128/'
}
}
steps {
sh './scripts/build.sh'
}
post {
always {
junit 'target/surefire-reports/**/*.xml'
}
success {
archiveArtifacts 'target/*.jar'
stash(name: 'build-result', includes: 'target/**/*')
}
}
}
stage('Test') {
parallel {
stage('Test Java 8') {
agent {
node {
label 'java8'
}
}
steps {
unstash 'build-result'
sh './scripts/integration-tests.sh'
junit 'target/failsafe-reports/**/*.xml'
}
}
stage('Test Java Next') {
agent {
node {
label 'javanext'
}
}
steps {
unstash 'build-result'
sh './scripts/integration-tests.sh'
junit 'target/failsafe-reports/**/*.xml'
}
}
}
}
stage('Approval') {
agent none
steps {
input(message: params.DEPLOY_MESSAGE, ok: 'Yes Deploy!')
}
when {
anyOf {
branch 'master'
environment name: 'FORCE_DEPLOY', value: 'true'
}
}
}
stage('Deploy') {
steps {
sh './scripts/deploy.sh'
}
when {
anyOf {
branch 'master'
environment name: 'FORCE_DEPLOY', value: 'true'
}
}
}
}
}
Options: Configurer un "job" depuis le Pipeline
Configuration: Même chose dans la GUI "Legacy"
Mot clef options
Documentation des options: https://jenkins.io/doc/book/pipeline/syntax/#options
Certaines options comme timeout
peuvent être appliquées
dans un bloc step
On souhaite que la "stage" Approval attende 3 minutes avant d’arrêter le pipeline
On ne veut conserver que les 5 derniers builds d’un pipeline: limiter l’usage disque
pipeline {
agent any
options {
buildDiscarder(logRotator(numToKeepStr: '5'))
}
parameters {
string(name: 'DEPLOY_MESSAGE', defaultValue: 'Deploy ?', description: 'Message de déploiement')
}
environment {
FORCE_DEPLOY = 'false'
}
stages {
stage('Build') {
agent {
dockerfile {
filename 'Dockerfile.build'
label 'docker'
additionalBuildArgs '--build-arg HTTP_PROXY=http://10.0.4.2:3128/ --build-arg HTTPS_PROXY=http://10.0.4.2:3128/'
}
}
steps {
sh './scripts/build.sh'
}
post {
always {
junit 'target/surefire-reports/**/*.xml'
}
success {
archiveArtifacts 'target/*.jar'
stash(name: 'build-result', includes: 'target/**/*')
}
}
}
stage('Test') {
parallel {
stage('Test Java 8') {
agent {
node {
label 'java8'
}
}
steps {
unstash 'build-result'
sh './scripts/integration-tests.sh'
junit 'target/failsafe-reports/**/*.xml'
}
}
stage('Test Java Next') {
agent {
node {
label 'javanext'
}
}
steps {
unstash 'build-result'
sh './scripts/integration-tests.sh'
junit 'target/failsafe-reports/**/*.xml'
}
}
}
}
stage('Approval') {
agent none
steps {
timeout(time: 3, unit: 'MINUTES') {
input(message: params.DEPLOY_MESSAGE, ok: 'Yes Deploy!')
}
}
when {
anyOf {
branch 'master'
environment name: 'FORCE_DEPLOY', value: 'true'
}
}
}
stage('Deploy') {
steps {
sh './scripts/deploy.sh'
}
when {
anyOf {
branch 'master'
environment name: 'FORCE_DEPLOY', value: 'true'
}
}
}
}
}
Industrialisation du logiciel : secteur balbutiant et très dynamique
Beaucoup de solutions !
Hébergement du service ?
Existant ?
Profil des utilisateurs ?
Criticité ?
CI seul ou "plus large" ?
Topologie de facturation ?
SaaS (modèles gratuits et/ou Enterprise) :
Github Actions
Travis CI
Circle CI
CodeShip
GitLab.com
Microsoft VSTS
Amazon Pipeline
A héberger soi-même :
Jenkins
CloudBees Jenkins Enteprise
GitLab
Microsoft VSTS
Atlassian Bamboo (BitBucket Pipeline)
"Serverless": JenkinsX, https://github.com/lambci
Glisser vers la production (FluxCD, OpenShift Pipelines)