Table des matières

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 :

C’est aussi une méthodologie d’audit puis d’accompagnement au changement et de formation pour :

Principales Fonctionnalités

Foundation : un socle de briques open source (OSS) enrichi par des use-cases :

Des modules additionnels indispensables :

Mise en oeuvre

Pour la mise en place d’une usine logicielle :

Installation Jenkins

RÉF : Jenkins - Intégration continue

Installation Docker

RÉF : Docker.io

RÉF : Docker.io - Déploiement sous linux

Installation Harbor

RÉF : Harbor où la registry Docker

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 : 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 : 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 :


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.

Annexe

https://success.docker.com/article/how-do-i-enable-the-remote-api-for-dockerd

https://docs.docker.com/engine/security/

https://hub.docker.com/u/jenkins

https://plugins.jenkins.io/docker-plugin/