Introduction
La Software Factory, est une suite logicielle modulaire et flexible pour répondre aux différents enjeux et besoins dans un contexte multi-clients, multi-sites, multi-prestataires, multi-technologies (Java, C/C++, PhP …), avec un processus et des outils à l’état de l’art :
- Piloter l’usine logicielle, un site de production, un projet en particulier,
- Maîtriser le processus de fabrication, la qualité du logiciel, les livrables venant de l’extérieur …
- Standardiser le processus d’intégration continue, les tests systèmes, le suivi des exigences, le déploiement …
- Améliorer la Productivité en automatisant les tâches non créatives et en incitant à la réutilisation.
C’est aussi une méthodologie d’audit puis d’accompagnement au changement et de formation pour :
- Impliquer les différents acteurs et parties prenantes,
- Industrialiser depuis une usine logicielle existante,
- Accélérer le cycle de développement des produits.
Principales Fonctionnalités
Foundation : un socle de briques open source (OSS) enrichi par des use-cases :
- Normalise les processus de développement pour plus de productivité,
- Unifie l’usage des modules pour automatiser les tâches répétitives,
- Rationalise le déploiement et le paramétrage de l’usine logicielle pour des montées de version maîtrisées.
Des modules additionnels indispensables :
- Activity : pour suivre votre activité globale (timesheet, CIR/CII …),
- Dashboard : pour des tableaux de bord de pilotage basé sur des KPI devons et la méthodologie Balanced-Score-Card (BSC),
- Checking : pour une qualimétrie logicielle et une douane applicative.
Mise en oeuvre
Pour la mise en place d’une usine logicielle :
- Suivi des versions : On utilise Git pour suivre les modifications sur le code source, permettre un retour en arrière, isoler des développements sur des branches, permettre la revue de code, etc. On utilise des versions en ligne (Github, Bitbucket), ou des logiciels libres (Gitlab)
- Automatiser la Compilation : avec la mise en place de scripts de compilation (Bash, MakeFile, Gradle, Maven) déclenchés automatiquement par les outils d’intégration continue (Jenkins, Gitlab-ci, Travis)
- Qualité de Code : Le service d’intégration continue peut déclencher les tests unitaires ou d’intégration (sur Docker), mais également lancer une analyse de code via SonarQube par exemple.
- Déploiement : Déploiement Automatique vers les serveurs de tests ou d’intégration, et déploiement à la demande sur les serveurs de production. Livraison automatique des livrables sur les plateformes dédiées.
Installation Jenkins
Installation Docker
RÉF : Docker.io
Installation Harbor
Interface Jenkins/Docker
Pour information, l'installation de Jenkins et docker a été réalisé sur CentOS 7
Paramétrage de l'API REST de Docker
Après avoir effectué l'installation de Jenkins et Docker, il faut dans tout d'abord effectuer le paramétrage de Docker pour activer l'API REST ce qui est indispensable pour le rendre opérationnel l'interfaçage avec Jenkins.
Dans un premier temps, nous allons rendre SElinux permissif :
# setenforce permissive (pour la session en cours) # sed -i s/enforcing/permissive/g /etc/selinux/config (pour que cela persiste au redémarrage)
Nous pouvons maintenant procéder à la configuration de Docker et activer l'API REST.
Pour activer le mode API il faut créer un fichier d'override systemd du service docker :
# systemctl edit docker
Puis y saisir le bloc suivant :
[Service] ExecStart= ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:<port> --containerd=/run/containerd/containerd.sock
Nous constatons qu'il y a 2 fois la variable ExecStart pour la simple est bonne raison que systemd ne permet pas de surcharger directement des variable, il est nécessaire de abord “unset” la variable, puis de la “set” de nouveau suivi la commande que nous souhaitons passer.
Remplacez <port> par le port souhaité, dans notre exemple, nous allons utiliser le port 2376
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2376 --containerd=/run/containerd/containerd.sock
Nous allons ensuite autoriser le port de l'API sur le firewall de l'OS
# firewall-cmd --add-port 2376/tcp --permanent # firewall-cmd --reload
Il faut effectué ensuite un reload du daemon systemd afin que notre fichier d'override soit pris en compte au prochain démarrage de docker et nous allons le démarrer par la même occasion :
# systemctl daemon-reload && systemctl start docker
Maintenant que Docker est démarré nous allons vérifier que le port de l'API est bien en écoute :
# ss -lntp | grep 2376
LISTEN 0 128 [::]:2376 [::]:* users:(("dockerd",pid=13004,fd=3))
Nous allons également s'assurer que l'API fonctionne correctement, nous allons faire le teste en lançant un container en mode détaché :
# docker run --name test-api -d -it ubuntu bash
Nous vérifions que le container est bien démarré :
# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES b61f0125afe9 ubuntu "bash" 4 seconds ago Up 4 seconds test-api
Nous pouvons désormais vérifier que l'API est bien opérationnelle en faisant une requête HTTP GET à l'aide de curl
# curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://localhost:2376/containers/json
Nous devrions obtenir le résultat suivant :
HTTP/1.1 200 OK
Api-Version: 1.40
Content-Type: application/json
Docker-Experimental: false
Ostype: linux
Server: Docker/19.03.8 (linux)
Date: Tue, 07 Apr 2020 17:31:05 GMT
Content-Length: 782
[{"Id":"b61f0125afe9c6e09295850f31c751422897ecb7bcb6a7522c4b2f88747434ce","Names":["/test-api"],"Image":"ubuntu","ImageID":"sha256:4e5021d210f65ebe915670c7089120120bc0a303b90208592851708c1b8c04bd","Command":"bash","Created":1586280369,"Ports":[],"Labels":{},"State":"running","Status":"Up 4 minutes","HostConfig":{"NetworkMode":"default"},"NetworkSettings":{"Networks":{"bridge":{"IPAMConfig":null,"Links":null,"Aliases":null,"NetworkID":"ab2f857aeb3fb5e570cf014d9fe5fd6c8a3970acf3519d14d99c10754c5a2779","EndpointID":"4628c1ede46eb57f9d772ba57c197f7b8f4c14f0a5ccff20cb3f0f49f96a4c2a","Gateway":"172.17.0.1","IPAddress":"172.17.0.2","IPPrefixLen":16,"IPv6Gateway":"","GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"MacAddress":"02:42:ac:11:00:02","DriverOpts":null}}},"Mounts":[]}]
A partir de cet instant nous pouvons confirmer que l'API Rest de docker est bien accessible, ce test peut être fait également depuis la machine Jenkins afin de s'assuré qu'il n'y a pas de blocage réseau entre ces 2 serveurs. Vous pouvez également stopper le container qui a été lancé, nous n'en aurons plus besoin.
Paramétrage Jenkins <=> Docker
Après avoir réalisé l'installation de Docker, nous pouvons désormais passer au paramétrage de Jenkins et la mise en place de son interconnexion avec Docker. Le but de cette configuration est de déployer des container docker qui auront pour rôle d'être des agents Jenkins.
Installation du plugin Docker
Se rendre dans le menu : Jenkins > Administrer Jenkins > Gestion des plugins
Lorsque vous êtes arrivé sur la page Gestion des plugins , cliquer sur l'onglet “Disponibles” , puis dans le champs Filtre saisir : Docker
Sélectionner ensuite le plugin Docker puis cliquer sur “Installer sans redémarrer”
Lorsque l'installation sera terminer, passer à l'étape suivante.
Paramétrage du plugin Docker
Pour notre exemple, l'IP du serveur Docker est 10.0.0.3
Se rendre dans le menu : Jenkins > Administrer Jenkins > Gerer les noeuds
Dans le menu de droite cliquer sur Configure Clouds
Puis cliquer sur “Ajouter un nouveau cloud” , sélectionner “Docker” , ensuite cliquer sur “Docker Cloud details” .
A partir de là, il faut renseigner les champs suivant :
- Name
- Docker Host URI
- Cocher la case “Enabled”
Name : Correspond au nom que nous donnons à notre cloud, cela permettra par la suite de le repérer sur la page suivante https://<JENKINS_URL/docker-plugin/
Docker Host URI : Correspond à l'URI du serveur Docker vers lequel nous devons pointé, tcp://<IP_ou_FQDN_du_serveur_docker>:<port>
Enabled : Permet d'activer ou de désactiver le cloud
Une fois les 3 champs renseigné, cliquer sur “Test Connection” , cela effectuera un test de connexion vers le serveur Docker que nous avons paramétré
Voici un exemple de configuration :
Paramétrage d'un Agent
Il est important de noter que les images qui seront déployé comme agents doivent avoir JAVA (1.8 minimum) d'installé ansi que le .jar de l'agent Jenkins, l'image de base que nous utiliserons dans cet exemple est : https://hub.docker.com/r/jenkins/agent/ Vous pouvez consulter le dockerfile ici : https://www.github.com/jenkinsci/docker-agent
Toujours sur la page “Configure Clouds” , après avoir effectué le paramétrage du serveur Docker, nous pouvons désormais procéder au paramétrage d'un Agent Jenkins. Pour ce faire, cliquer sur “Docker Agent Template” .
A partir de là, il faut au minimum renseigner les champs suivant :
- Labels
- Cocher la case “Enabled”
- Docker Image
Labels : Correspond au nom de l'agent, ce nom doit être pertinent, il sera utilisé dans les jobs.
Enabled : Permet d'activer ou de désactiver l'agent.
Name : Correspond au nom du container qui sera déployé, se nom sera suivi d'un ID aléatoire lorsque le container sera déployé pour un job.
Docker Image : Corresponds au nom de l'image situé sur une registry Docker, par défaut, le plugin va récupérer les images sur hub.docker.io, mais nous pouvons préciser une autre registry au besoin, il faudra préciser le chemin complet sous la forme <fqdn_registry>/<projet>/<image>:<tag> par exemple registry.demo.com/monprojet/ssh-slave:latest
Voici un exemple de configuration exploitant une autre registry que hub.docker.io :
Pour cet exemple, nous avons tout simplement répliqué l'image officiel jenkins/agent sur notre registry personnelle. Par la suite nous mettrons en place une pipeline pour exploiter cet agent et ses fonctionnalités incluse (bash et GIT).

Et enfin cliquer sur “Save” .
Validation Jenkins / Docker
Creation d'une pipeline
La pipeline que nous allons mettre en place, va permettre d'exploiter l'agent, ainsi que le contenu du container, l'image est basé sur une Debian Buster 10.3 avec openjdk11 et git.
Dans un premier temps, créer un nouvel item de type “pipeline”, pour ce test, nous allons utiliser le code suivant qui va effectuer un git sur un repos publique et exécuter un script bash “Hello world”
pipeline {
agent none
stages {
stage('Hello') {
agent { label 'docker-agent' }
steps {
git 'https://github.com/ruanyf/simple-bash-scripts.git'
sh 'scripts/hello-world.sh'
}
}
}
}
Lors de l’exécution du job, nous pouvons constaté qu'un agent Jenkins est généré sur notre host docker.
Creation d'une pipeline utilisant 2 containers ou plus
Maintenant que nous avons valider ce test nous allons maintenant travailler avec 2 container partageant le même volume, le premier container sera l'agent Jenkins qui nous avons précédemment paramétré, le second sera un container sous ansible 2.9.
Nous allons tout d'abord créer un nouveau volume sur le host docker :
# docker volume create jenkins_data
Nous pouvons maintenant effectué la configuration volumes sur les templates, retourner sur la page de configuration “Configure Clouds”, sur le template “docker-agent”, cliquer sur “container settings”, un formulaire va apparaître, dans le champs “Volumes” saisir :
jenkins_data:/home/jenkins
Maintenant il faut créer le “Docker Agent Template” pour le container ansible, cliquer sur “Add Docker Template” et saisir les paramètres suivants dans les champs adéquats :
- Labels: ansible
- Cocher la case “Enabled”
- Name: ansible
- Image: gdelachat/jenkins-ansible
- Volumes : jenkins_data:/home/jenkins
L'image “gdelachat/jenkins-ansible” a été build par mes soins en se basant sur l'image docker-agent, dans celui-ci git n'est pas embarqué, mais uniquement ansible 2.9
On peut s'assurer que le container Ansible fonctionne correctement en utilisant la pipeline test suivante :
pipeline {
agent none
stages {
stage('Ansible') {
agent { label 'ansible' }
steps {
sh 'ansible localhost -m setup'
}
}
}
}
Nous devons normalement obtenir l'output suivant :
Nous allons maintenant effectuer notre test qui utilisera les 2 containers partageant le même volume, la pipeline exécutera d'abord le premier le git sur notre container 'docker-agent' puis la commande ansible-playbook sera exécuter sur le second.
Pour notre exemple nous utiliser le playbook ping.yml sur le repo suivant : https://github.com/lukry59/ansible-playbook.git
Voici la pipeline utilisé pour notre test, elle est composé de 2 stages, un stage pour le git, puis un stage pour ansible :
pipeline {
agent none
stages {
stage('git') {
agent { label 'docker-agent' }
steps {
git 'https://github.com/lukry59/ansible-playbook.git'
}
}
stage('ansible') {
agent { label 'ansible' }
steps {
sh 'ansible-playbook ping.yml'
}
}
}
}
Lors de l'exécution de cette pipeline nous devons obtenir le résultat suivant :

Nous constatons que 2 containers ont été généré et travaillent ensemble sur le même volume.
Utilisation de la methode 'dockerNode' dans une pipeline
Le plugin jenkins “docker-plugin” dispose d'une fonction pouvant être appelé dans une pipeline déclarative, cette méthode nous permet d’exécuter notre container directement sans avoir au préalable défini de “Docker Agent Template”
La documentation de la fonction dockerNode est disponible ici : https://www.jenkins.io/doc/pipeline/steps/docker-plugin/
Voici un exemple de pipeline exploitant l'image ansible précédemment utilisé :
pipeline {
agent none
stages {
stage('ansible') {
agent none
steps {
dockerNode(image: 'gdelachat/jenkins-ansible', dockerHost: 'tcp://10.0.0.3:2376') {
sh 'ansible localhost -m ping'
}
}
}
}
}
Cependant cette méthode ne permet pas le mappage de volume.


